[libcxx-commits] [libcxx] [libc++] Implement P2538R1 "ADL-proof std::projected" (PR #65411)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Tue Sep 5 13:25:42 PDT 2023


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/65411:

Notice that because Holder<Incomplete> is _possible_ to complete, but _unsafe_ to complete, that means that Holder<Incomplete>* is basically not an iterator and it's not even safe to ask if input_iterator<Holder<Incomplete>*> because that _will_ necessarily complete the type. So it's totally expected that we still cannot safely ask e.g.

  static_assert(std::indirect_unary_predicate<bool(&)(Holder<Incomplete>&), Holder<Incomplete>*>);

or even

  static_assert(!std::indirect_unary_predicate<int, Holder<Incomplete>*>);

This was originally uploaded as https://reviews.llvm.org/D119029 and I picked it up here as part of the Github PR transition.

>From f3e998868d5acfa78281fe0817651863d67e4f37 Mon Sep 17 00:00:00 2001
From: Arthur O'Dwyer <arthur.j.odwyer at gmail.com>
Date: Tue, 5 Sep 2023 16:12:02 -0400
Subject: [PATCH] [libc++] Implement P2538R1 "ADL-proof std::projected"

Notice that because Holder<Incomplete> is _possible_ to complete,
but _unsafe_ to complete, that means that Holder<Incomplete>*
is basically not an iterator and it's not even safe to ask if
input_iterator<Holder<Incomplete>*> because that _will_ necessarily
complete the type. So it's totally expected that we still cannot
safely ask e.g.

  static_assert(std::indirect_unary_predicate<bool(&)(Holder<Incomplete>&), Holder<Incomplete>*>);

or even

  static_assert(!std::indirect_unary_predicate<int, Holder<Incomplete>*>);

Differential Revision: https://reviews.llvm.org/D119029

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/docs/ReleaseNotes/18.rst               |  5 ++++
 libcxx/docs/Status/Cxx2cPapers.csv            |  2 +-
 libcxx/include/__iterator/projected.h         | 26 +++++++++++++++----
 ...indirect_binary_predicate.compile.pass.cpp |  8 ++++++
 ...rect_equivalence_relation.compile.pass.cpp |  8 ++++++
 .../indirect_result_t.compile.pass.cpp        | 12 +++++++++
 ...ndirect_strict_weak_order.compile.pass.cpp |  8 ++++++
 .../indirect_unary_predicate.compile.pass.cpp |  9 +++++++
 .../indirectly_comparable.compile.pass.cpp    |  8 ++++++
 ...y_regular_unary_invocable.compile.pass.cpp |  9 +++++++
 ...ndirectly_unary_invocable.compile.pass.cpp |  9 +++++++
 11 files changed, 98 insertions(+), 6 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index 1d08693adb02e8..467cb0d8daf6d3 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -41,6 +41,7 @@ Implemented Papers
 
 - P2497R0 - Testing for success or failure of ``<charconv>`` functions
 - P2697R1 - Interfacing ``bitset`` with ``string_view``
+- P2538R1 - ADL-proof ``std::projected``
 
 
 Improvements and New Features
@@ -88,5 +89,9 @@ ABI Affecting Changes
   and you notice changes to your exported symbols list, then this means that you were not properly preventing libc++
   symbols from being part of your ABI.
 
+- The name mangling for intantiations of ``std::projected`` has changed in order to implement P2538R1. This technically
+  results in an ABI break, however in practice we expect uses of ``std::projected`` in ABI-sensitive places to be
+  extremely rare. Any error resulting from this change should result in a link-time error.
+
 Build System Changes
 --------------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index fe9d17004e2aab..0590e2d487c27b 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -5,7 +5,7 @@
 "`P2562R1 <https://wg21.link/P2562R1>`__","LWG","``constexpr`` Stable Sorting","Varna June 2023","","",""
 "`P2545R4 <https://wg21.link/P2545R4>`__","LWG","Read-Copy Update (RCU)","Varna June 2023","","",""
 "`P2530R3 <https://wg21.link/P2530R3>`__","LWG","Hazard Pointers for C++26","Varna June 2023","","",""
-"`P2538R1 <https://wg21.link/P2538R1>`__","LWG","ADL-proof ``std::projected``","Varna June 2023","","","|ranges|"
+"`P2538R1 <https://wg21.link/P2538R1>`__","LWG","ADL-proof ``std::projected``","Varna June 2023","|Complete|","18.0","|ranges|"
 "`P2495R3 <https://wg21.link/P2495R3>`__","LWG","Interfacing ``stringstreams`` with ``string_view``","Varna June 2023","","",""
 "`P2510R3 <https://wg21.link/P2510R3>`__","LWG","Formatting pointers","Varna June 2023","|Complete| [#note-P2510R3]_","17.0","|format|"
 "`P2198R7 <https://wg21.link/P2198R7>`__","LWG","Freestanding Feature-Test Macros and Implementation-Defined Extensions","Varna June 2023","","",""
diff --git a/libcxx/include/__iterator/projected.h b/libcxx/include/__iterator/projected.h
index e74e56d6fb71e7..6f9d1dc17f4351 100644
--- a/libcxx/include/__iterator/projected.h
+++ b/libcxx/include/__iterator/projected.h
@@ -23,13 +23,29 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 20
 
-template<indirectly_readable _It, indirectly_regular_unary_invocable<_It> _Proj>
-struct projected {
-  using value_type = remove_cvref_t<indirect_result_t<_Proj&, _It>>;
-  indirect_result_t<_Proj&, _It> operator*() const; // not defined
+template <class _It, class _Proj>
+struct __projected_impl {
+  struct __type {
+    using value_type = remove_cvref_t<indirect_result_t<_Proj&, _It>>;
+    indirect_result_t<_Proj&, _It> operator*() const; // not defined
+  };
 };
 
-template<weakly_incrementable _It, class _Proj>
+template <weakly_incrementable _It, class _Proj>
+struct __projected_impl<_It, _Proj> {
+  struct __type {
+    using value_type      = remove_cvref_t<indirect_result_t<_Proj&, _It>>;
+    using difference_type = iter_difference_t<_It>;
+    indirect_result_t<_Proj&, _It> operator*() const; // not defined
+  };
+};
+
+// Note: We implement std::projected in a way that satisfies P2538R1 even in standard
+//       modes before C++26 to avoid breaking the ABI between standard modes.
+template <indirectly_readable _It, indirectly_regular_unary_invocable<_It> _Proj>
+using projected = typename __projected_impl<_It, _Proj>::__type;
+
+template <weakly_incrementable _It, class _Proj>
 struct incrementable_traits<projected<_It, _Proj>> {
   using difference_type = iter_difference_t<_It>;
 };
diff --git a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_binary_predicate.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_binary_predicate.compile.pass.cpp
index 741aeeedaa8c31..f937bcf9baf12a 100644
--- a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_binary_predicate.compile.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_binary_predicate.compile.pass.cpp
@@ -80,3 +80,11 @@ struct BadPredicate6 {
     bool operator()(std::iter_common_reference_t<It1>, std::iter_common_reference_t<It2>) const = delete;
 };
 static_assert(!std::indirect_binary_predicate<BadPredicate6, It1, It2>);
+
+// Test ADL-proofing (P2538R1)
+#if TEST_STD_VER >= 26
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(std::indirect_binary_predicate<std::less<Holder<Incomplete>*>, Holder<Incomplete>**, Holder<Incomplete>**>);
+static_assert(!std::indirect_binary_predicate<Holder<Incomplete>*, Holder<Incomplete>**, Holder<Incomplete>**>);
+#endif
diff --git a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_equivalence_relation.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_equivalence_relation.compile.pass.cpp
index 0af5def93d65b9..0418e7cc8d3c7a 100644
--- a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_equivalence_relation.compile.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_equivalence_relation.compile.pass.cpp
@@ -95,3 +95,11 @@ struct BadRelation6 {
     bool operator()(std::iter_common_reference_t<It1>, std::iter_common_reference_t<It2>) const = delete;
 };
 static_assert(!std::indirect_equivalence_relation<BadRelation6, It1, It2>);
+
+// Test ADL-proofing (P2538R1)
+#if TEST_STD_VER >= 26
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(std::indirect_equivalence_relation<std::equal_to<Holder<Incomplete>*>, Holder<Incomplete>**, Holder<Incomplete>**>);
+static_assert(!std::indirect_equivalence_relation<Holder<Incomplete>*, Holder<Incomplete>**, Holder<Incomplete>**>);
+#endif
diff --git a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_result_t.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_result_t.compile.pass.cpp
index 4121170c3e46cc..e9cec254e3a3e9 100644
--- a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_result_t.compile.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_result_t.compile.pass.cpp
@@ -29,3 +29,15 @@ constexpr bool has_indirect_result = requires {
 
 static_assert(!has_indirect_result<int (*)(int), int>); // int isn't indirectly_readable
 static_assert(!has_indirect_result<int, int*>);         // int isn't invocable
+
+// Test ADL-proofing (P2538R1)
+#if TEST_STD_VER >= 26
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(std::same_as<std::indirect_result_t<int                (&)(int), int*>, int>);
+static_assert(std::same_as<std::indirect_result_t<Holder<Incomplete>&(&)(int), int*>, Holder<Incomplete>&>);
+static_assert(std::same_as<std::indirect_result_t<Holder<Incomplete>*(&)(int), int*>, Holder<Incomplete>*>);
+static_assert(std::same_as<std::indirect_result_t<int                (&)(Holder<Incomplete>*), Holder<Incomplete>**>, int>);
+static_assert(std::same_as<std::indirect_result_t<Holder<Incomplete>&(&)(Holder<Incomplete>*), Holder<Incomplete>**>, Holder<Incomplete>&>);
+static_assert(std::same_as<std::indirect_result_t<Holder<Incomplete>*(&)(Holder<Incomplete>*), Holder<Incomplete>**>, Holder<Incomplete>*>);
+#endif
diff --git a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_strict_weak_order.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_strict_weak_order.compile.pass.cpp
index 5a5360cf208dd3..998f5880da1c17 100644
--- a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_strict_weak_order.compile.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_strict_weak_order.compile.pass.cpp
@@ -95,3 +95,11 @@ struct BadOrder6 {
     bool operator()(std::iter_common_reference_t<It1>, std::iter_common_reference_t<It2>) const = delete;
 };
 static_assert(!std::indirect_strict_weak_order<BadOrder6, It1, It2>);
+
+// Test ADL-proofing (P2538R1)
+#if TEST_STD_VER >= 26
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(std::indirect_strict_weak_order<std::less<Holder<Incomplete>*>, Holder<Incomplete>**, Holder<Incomplete>**>);
+static_assert(!std::indirect_strict_weak_order<Holder<Incomplete>*, Holder<Incomplete>**, Holder<Incomplete>**>);
+#endif
diff --git a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_unary_predicate.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_unary_predicate.compile.pass.cpp
index 05e359d0cdf2b1..3f4748e2c12504 100644
--- a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_unary_predicate.compile.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_unary_predicate.compile.pass.cpp
@@ -62,3 +62,12 @@ struct BadPredicate4 {
     bool operator()(std::iter_common_reference_t<It>) const = delete;
 };
 static_assert(!std::indirect_unary_predicate<BadPredicate4, It>);
+
+// Test ADL-proofing (P2538R1)
+#if TEST_STD_VER >= 26
+struct Incomplete;
+template<class T> struct Holder { T t; };
+struct HolderIncompletePred { bool operator()(Holder<Incomplete>*) const; };
+static_assert(std::indirect_unary_predicate<HolderIncompletePred, Holder<Incomplete>**>);
+static_assert(!std::indirect_unary_predicate<Holder<Incomplete>*, Holder<Incomplete>**>);
+#endif
diff --git a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_comparable.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_comparable.compile.pass.cpp
index eae5b342d5986a..3c80973889b2c2 100644
--- a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_comparable.compile.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_comparable.compile.pass.cpp
@@ -48,3 +48,11 @@ void is_subsumed(F);
 
 static_assert(subsumes(std::less<int>()));
 static_assert(is_subsumed(std::less<int>()));
+
+// Test ADL-proofing (P2538R1)
+#if TEST_STD_VER >= 26
+struct Incomplete;
+template<class T> struct Holder { T t; };
+static_assert(std::indirectly_comparable<Holder<Incomplete>**, Holder<Incomplete>**, std::less<Holder<Incomplete>*>>);
+static_assert(!std::indirectly_comparable<Holder<Incomplete>**, Holder<Incomplete>**, Holder<Incomplete>*>);
+#endif
diff --git a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_regular_unary_invocable.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_regular_unary_invocable.compile.pass.cpp
index 333b9fadbba599..2d8464da27fb95 100644
--- a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_regular_unary_invocable.compile.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_regular_unary_invocable.compile.pass.cpp
@@ -85,3 +85,12 @@ static_assert(!std::indirectly_regular_unary_invocable<int (*)(int*, int*), int*
 static_assert(!std::indirectly_regular_unary_invocable<int (&)(int*, int*), int*>);
 static_assert(!std::indirectly_regular_unary_invocable<int (S::*)(int*), S*>);
 static_assert(!std::indirectly_regular_unary_invocable<int (S::*)(int*) const, S*>);
+
+// Test ADL-proofing (P2538R1)
+#if TEST_STD_VER >= 26
+struct Incomplete;
+template<class T> struct Holder { T t; };
+struct HolderIncompletePred { bool operator()(Holder<Incomplete>*) const; };
+static_assert(std::indirectly_regular_unary_invocable<HolderIncompletePred, Holder<Incomplete>**>);
+static_assert(!std::indirectly_regular_unary_invocable<Holder<Incomplete>*, Holder<Incomplete>**>);
+#endif
diff --git a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_unary_invocable.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_unary_invocable.compile.pass.cpp
index 8b7c3f1eee0658..db1a44b8243d57 100644
--- a/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_unary_invocable.compile.pass.cpp
+++ b/libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_unary_invocable.compile.pass.cpp
@@ -85,3 +85,12 @@ static_assert(!std::indirectly_unary_invocable<int (*)(int*, int*), int*>);
 static_assert(!std::indirectly_unary_invocable<int (&)(int*, int*), int*>);
 static_assert(!std::indirectly_unary_invocable<int (S::*)(int*), S*>);
 static_assert(!std::indirectly_unary_invocable<int (S::*)(int*) const, S*>);
+
+// Test ADL-proofing (P2538R1)
+#if TEST_STD_VER >= 26
+struct Incomplete;
+template<class T> struct Holder { T t; };
+struct HolderIncompletePred { bool operator()(Holder<Incomplete>*) const; };
+static_assert(std::indirectly_unary_invocable<HolderIncompletePred, Holder<Incomplete>**>);
+static_assert(!std::indirectly_unary_invocable<Holder<Incomplete>*, Holder<Incomplete>**>);
+#endif



More information about the libcxx-commits mailing list