Skip to content

Conversation

@kazutakahirata
Copy link
Contributor

Currently, our llvm::identity is not quite like std::identity from
C++20. Ours is a template struct while std::identity is a
non-template struct with templatized operator(). This difference
means that we cannot mechanically replace llvm::identity with
std::identity when we switch to C++20 in the future.

This patch implements llvm::identity_cxx20, which behaves exactly like
std::identity.

Once this patch lands, I'm planning to migrate users of llvm::identity
to the new style. There aren't that many:

  • 4 instances of SparseSet<...>
  • 3 instances of SparseMultiSet<...>
  • about 50 instances of IndexedMap<...>

Currently, our llvm::identity<T> is not quite like std::identity from
C++20.  Ours is a template struct while std::identity is a
non-template struct with templatized operator().  This difference
means that we cannot mechanically replace llvm::identity with
std::identity when we switch to C++20 in the future.

This patch implements llvm::identity_cxx20, which behaves exactly like
std::identity.

Once this patch lands, I'm planning to migrate users of llvm::identity
to the new style.  There aren't that many:

- 4 instances of SparseSet<...>
- 3 instances of SparseMultiSet<...>
- about 50 instances of IndexedMap<...>
@llvmbot
Copy link
Member

llvmbot commented Oct 19, 2025

@llvm/pr-subscribers-llvm-adt

Author: Kazu Hirata (kazutakahirata)

Changes

Currently, our llvm::identity<T> is not quite like std::identity from
C++20. Ours is a template struct while std::identity is a
non-template struct with templatized operator(). This difference
means that we cannot mechanically replace llvm::identity with
std::identity when we switch to C++20 in the future.

This patch implements llvm::identity_cxx20, which behaves exactly like
std::identity.

Once this patch lands, I'm planning to migrate users of llvm::identity
to the new style. There aren't that many:

  • 4 instances of SparseSet<...>
  • 3 instances of SparseMultiSet<...>
  • about 50 instances of IndexedMap<...>

Full diff: https://github.com/llvm/llvm-project/pull/164143.diff

2 Files Affected:

  • (modified) llvm/include/llvm/ADT/STLForwardCompat.h (+10)
  • (modified) llvm/unittests/ADT/STLForwardCompatTest.cpp (+22)
diff --git a/llvm/include/llvm/ADT/STLForwardCompat.h b/llvm/include/llvm/ADT/STLForwardCompat.h
index 273a5cf8aa8ff..0e9bd2d455965 100644
--- a/llvm/include/llvm/ADT/STLForwardCompat.h
+++ b/llvm/include/llvm/ADT/STLForwardCompat.h
@@ -19,6 +19,7 @@
 
 #include <optional>
 #include <type_traits>
+#include <utility>
 
 namespace llvm {
 
@@ -117,6 +118,15 @@ struct detector<std::void_t<Op<Args...>>, Op, Args...> {
 template <template <class...> class Op, class... Args>
 using is_detected = typename detail::detector<void, Op, Args...>::value_t;
 
+struct identity_cxx20 // NOLINT(readability-identifier-naming)
+{
+  using is_transparent = void;
+
+  template <typename T> constexpr T &&operator()(T &&self) const noexcept {
+    return std::forward<T>(self);
+  }
+};
+
 //===----------------------------------------------------------------------===//
 //     Features from C++23
 //===----------------------------------------------------------------------===//
diff --git a/llvm/unittests/ADT/STLForwardCompatTest.cpp b/llvm/unittests/ADT/STLForwardCompatTest.cpp
index 4a8f53cf72f94..2a97e8d6a552f 100644
--- a/llvm/unittests/ADT/STLForwardCompatTest.cpp
+++ b/llvm/unittests/ADT/STLForwardCompatTest.cpp
@@ -184,4 +184,26 @@ TEST(TransformTest, ToUnderlying) {
   static_assert(llvm::to_underlying(E3::B3) == 0);
 }
 
+TEST(STLForwardCompatTest, IdentityCxx20) {
+  llvm::identity_cxx20 identity;
+
+  // Test with an lvalue.
+  int X = 42;
+  int &Y = identity(X);
+  EXPECT_EQ(&X, &Y);
+
+  // Test with a const lvalue.
+  const int CX = 10;
+  const int &CY = identity(CX);
+  EXPECT_EQ(&CX, &CY);
+
+  // Test with an rvalue.
+  EXPECT_EQ(identity(123), 123);
+
+  // Test perfect forwarding.
+  static_assert(std::is_same_v<int &, decltype(identity(X))>);
+  static_assert(std::is_same_v<const int &, decltype(identity(CX))>);
+  static_assert(std::is_same_v<int &&, decltype(identity(int(5)))>);
+}
+
 } // namespace

@kazutakahirata kazutakahirata merged commit 622605e into llvm:main Oct 19, 2025
12 checks passed
@kazutakahirata kazutakahirata deleted the cleanup_20251018_ADT_identity_cxx20 branch October 19, 2025 15:34
Lukacma pushed a commit to Lukacma/llvm-project that referenced this pull request Oct 29, 2025
Currently, our llvm::identity<T> is not quite like std::identity from
C++20.  Ours is a template struct while std::identity is a
non-template struct with templatized operator().  This difference
means that we cannot mechanically replace llvm::identity with
std::identity when we switch to C++20 in the future.

This patch implements llvm::identity_cxx20, which behaves exactly like
std::identity.

Once this patch lands, I'm planning to migrate users of llvm::identity
to the new style.  There aren't that many:

- 4 instances of SparseSet<...>
- 3 instances of SparseMultiSet<...>
- about 50 instances of IndexedMap<...>
aokblast pushed a commit to aokblast/llvm-project that referenced this pull request Oct 30, 2025
Currently, our llvm::identity<T> is not quite like std::identity from
C++20.  Ours is a template struct while std::identity is a
non-template struct with templatized operator().  This difference
means that we cannot mechanically replace llvm::identity with
std::identity when we switch to C++20 in the future.

This patch implements llvm::identity_cxx20, which behaves exactly like
std::identity.

Once this patch lands, I'm planning to migrate users of llvm::identity
to the new style.  There aren't that many:

- 4 instances of SparseSet<...>
- 3 instances of SparseMultiSet<...>
- about 50 instances of IndexedMap<...>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants