[libcxx-commits] [libcxx] [libc++] Implement P2538R1 "ADL-proof std::projected" (PR #65411)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Sep 6 08:30:15 PDT 2023
https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/65411:
>From 23567ab14509413d47d6a165cccbb36c02cc0841 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 | 6 ++++
libcxx/docs/Status/Cxx2cPapers.csv | 2 +-
libcxx/include/__iterator/projected.h | 28 +++++++++++++------
...indirect_binary_predicate.compile.pass.cpp | 10 +++++++
...rect_equivalence_relation.compile.pass.cpp | 12 +++++++-
.../indirect_result_t.compile.pass.cpp | 14 ++++++++++
...ndirect_strict_weak_order.compile.pass.cpp | 12 +++++++-
.../indirect_unary_predicate.compile.pass.cpp | 10 +++++++
.../indirectly_comparable.compile.pass.cpp | 10 +++++++
...y_regular_unary_invocable.compile.pass.cpp | 10 +++++++
...ndirectly_unary_invocable.compile.pass.cpp | 10 +++++++
libcxx/utils/data/ignore_format.txt | 1 -
12 files changed, 113 insertions(+), 12 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index b10c8fa78c2270..36e6d69cd97cc8 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -42,6 +42,8 @@ Implemented Papers
- P2497R0 - Testing for success or failure of ``<charconv>`` functions
- P2697R1 - Interfacing ``bitset`` with ``string_view``
- P2443R1 - ``views::chunk_by``
+- P2538R1 - ADL-proof ``std::projected``
+
Improvements and New Features
-----------------------------
@@ -88,5 +90,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..463d07b0d33c2d 100644
--- a/libcxx/include/__iterator/projected.h
+++ b/libcxx/include/__iterator/projected.h
@@ -12,7 +12,7 @@
#include <__config>
#include <__iterator/concepts.h>
-#include <__iterator/incrementable_traits.h>
+#include <__iterator/incrementable_traits.h> // iter_difference_t
#include <__type_traits/remove_cvref.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -23,17 +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>
-struct incrementable_traits<projected<_It, _Proj>> {
- using difference_type = iter_difference_t<_It>;
+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 that 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 (even though ABI
+// breaks with std::projected are expected to have essentially no impact).
+template <indirectly_readable _It, indirectly_regular_unary_invocable<_It> _Proj>
+using projected = typename __projected_impl<_It, _Proj>::__type;
+
#endif // _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
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..a5c4f24764dd78 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
@@ -11,10 +11,12 @@
// template<class F, class I1, class I2>
// concept indirect_binary_predicate;
+#include <functional>
#include <iterator>
#include <type_traits>
#include "indirectly_readable.h"
+#include "test_macros.h"
using It1 = IndirectlyReadable<struct Token1>;
using It2 = IndirectlyReadable<struct Token2>;
@@ -80,3 +82,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..c6fc8fab42355f 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
@@ -11,10 +11,12 @@
// template<class F, class I1, class I2 = I1>
// concept indirect_equivalence_relation;
-#include <iterator>
#include <concepts>
+#include <functional>
+#include <iterator>
#include "indirectly_readable.h"
+#include "test_macros.h"
using It1 = IndirectlyReadable<struct Token1>;
using It2 = IndirectlyReadable<struct Token2>;
@@ -95,3 +97,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..d6f76269da3773 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
@@ -13,6 +13,8 @@
#include <iterator>
#include <concepts>
+#include "test_macros.h"
+
static_assert(std::same_as<std::indirect_result_t<int (*)(int), int*>, int>);
static_assert(std::same_as<std::indirect_result_t<double (*)(int const&, float), int const*, float*>, double>);
@@ -29,3 +31,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..7828e105ba397f 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
@@ -11,10 +11,12 @@
// template<class F, class I1, class I2 = I1>
// concept indirect_strict_weak_order;
-#include <iterator>
#include <concepts>
+#include <functional>
+#include <iterator>
#include "indirectly_readable.h"
+#include "test_macros.h"
using It1 = IndirectlyReadable<struct Token1>;
using It2 = IndirectlyReadable<struct Token2>;
@@ -95,3 +97,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..7b551d567959df 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
@@ -15,6 +15,7 @@
#include <type_traits>
#include "indirectly_readable.h"
+#include "test_macros.h"
using It = IndirectlyReadable<struct Token>;
@@ -62,3 +63,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..c171d092567e78 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
@@ -15,6 +15,8 @@
#include <iterator>
#include <type_traits>
+#include "test_macros.h"
+
struct Deref {
int operator()(int*) const;
};
@@ -48,3 +50,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..85d37bbd7bed49 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
@@ -15,6 +15,7 @@
#include <concepts>
#include "indirectly_readable.h"
+#include "test_macros.h"
using It = IndirectlyReadable<struct Token>;
using R1 = T1<struct ReturnToken>;
@@ -85,3 +86,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..8fdb77772bd93c 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
@@ -15,6 +15,7 @@
#include <concepts>
#include "indirectly_readable.h"
+#include "test_macros.h"
using It = IndirectlyReadable<struct Token>;
using R1 = T1<struct ReturnToken>;
@@ -85,3 +86,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
diff --git a/libcxx/utils/data/ignore_format.txt b/libcxx/utils/data/ignore_format.txt
index 3caf9d45c783ec..95eeb8200f97db 100644
--- a/libcxx/utils/data/ignore_format.txt
+++ b/libcxx/utils/data/ignore_format.txt
@@ -284,7 +284,6 @@ libcxx/include/__iterator/ostreambuf_iterator.h
libcxx/include/__iterator/ostream_iterator.h
libcxx/include/__iterator/permutable.h
libcxx/include/__iterator/prev.h
-libcxx/include/__iterator/projected.h
libcxx/include/__iterator/readable_traits.h
libcxx/include/__iterator/reverse_access.h
libcxx/include/__iterator/reverse_iterator.h
More information about the libcxx-commits
mailing list