Skip to content

Commit 4d08ecc

Browse files
ldionneArthur O'Dwyer
andauthored
[libc++] Implement P2538R1 "ADL-proof std::projected" (#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. Co-authored-by: Arthur O'Dwyer <[email protected]>
1 parent 63327fa commit 4d08ecc

12 files changed

+116
-12
lines changed

libcxx/docs/ReleaseNotes/18.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ Implemented Papers
5151
- P2497R0 - Testing for success or failure of ``<charconv>`` functions
5252
- P2697R1 - Interfacing ``bitset`` with ``string_view``
5353
- P2443R1 - ``views::chunk_by``
54+
- P2538R1 - ADL-proof ``std::projected``
55+
5456

5557
Improvements and New Features
5658
-----------------------------
@@ -126,5 +128,9 @@ ABI Affecting Changes
126128
and you notice changes to your exported symbols list, then this means that you were not properly preventing libc++
127129
symbols from being part of your ABI.
128130

131+
- The name mangling for intantiations of ``std::projected`` has changed in order to implement P2538R1. This technically
132+
results in an ABI break, however in practice we expect uses of ``std::projected`` in ABI-sensitive places to be
133+
extremely rare. Any error resulting from this change should result in a link-time error.
134+
129135
Build System Changes
130136
--------------------

libcxx/docs/Status/Cxx2cPapers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"`P2562R1 <https://wg21.link/P2562R1>`__","LWG","``constexpr`` Stable Sorting","Varna June 2023","","",""
66
"`P2545R4 <https://wg21.link/P2545R4>`__","LWG","Read-Copy Update (RCU)","Varna June 2023","","",""
77
"`P2530R3 <https://wg21.link/P2530R3>`__","LWG","Hazard Pointers for C++26","Varna June 2023","","",""
8-
"`P2538R1 <https://wg21.link/P2538R1>`__","LWG","ADL-proof ``std::projected``","Varna June 2023","","","|ranges|"
8+
"`P2538R1 <https://wg21.link/P2538R1>`__","LWG","ADL-proof ``std::projected``","Varna June 2023","|Complete|","18.0","|ranges|"
99
"`P2495R3 <https://wg21.link/P2495R3>`__","LWG","Interfacing ``stringstreams`` with ``string_view``","Varna June 2023","","",""
1010
"`P2510R3 <https://wg21.link/P2510R3>`__","LWG","Formatting pointers","Varna June 2023","|Complete| [#note-P2510R3]_","17.0","|format|"
1111
"`P2198R7 <https://wg21.link/P2198R7>`__","LWG","Freestanding Feature-Test Macros and Implementation-Defined Extensions","Varna June 2023","","",""

libcxx/include/__iterator/projected.h

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
#include <__config>
1414
#include <__iterator/concepts.h>
15-
#include <__iterator/incrementable_traits.h>
15+
#include <__iterator/incrementable_traits.h> // iter_difference_t
1616
#include <__type_traits/remove_cvref.h>
1717

1818
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -23,17 +23,29 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2323

2424
#if _LIBCPP_STD_VER >= 20
2525

26-
template<indirectly_readable _It, indirectly_regular_unary_invocable<_It> _Proj>
27-
struct projected {
28-
using value_type = remove_cvref_t<indirect_result_t<_Proj&, _It>>;
29-
indirect_result_t<_Proj&, _It> operator*() const; // not defined
26+
template <class _It, class _Proj>
27+
struct __projected_impl {
28+
struct __type {
29+
using value_type = remove_cvref_t<indirect_result_t<_Proj&, _It>>;
30+
indirect_result_t<_Proj&, _It> operator*() const; // not defined
31+
};
3032
};
3133

32-
template<weakly_incrementable _It, class _Proj>
33-
struct incrementable_traits<projected<_It, _Proj>> {
34-
using difference_type = iter_difference_t<_It>;
34+
template <weakly_incrementable _It, class _Proj>
35+
struct __projected_impl<_It, _Proj> {
36+
struct __type {
37+
using value_type = remove_cvref_t<indirect_result_t<_Proj&, _It>>;
38+
using difference_type = iter_difference_t<_It>;
39+
indirect_result_t<_Proj&, _It> operator*() const; // not defined
40+
};
3541
};
3642

43+
// Note that we implement std::projected in a way that satisfies P2538R1 even in standard
44+
// modes before C++26 to avoid breaking the ABI between standard modes (even though ABI
45+
// breaks with std::projected are expected to have essentially no impact).
46+
template <indirectly_readable _It, indirectly_regular_unary_invocable<_It> _Proj>
47+
using projected = typename __projected_impl<_It, _Proj>::__type;
48+
3749
#endif // _LIBCPP_STD_VER >= 20
3850

3951
_LIBCPP_END_NAMESPACE_STD

libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_binary_predicate.compile.pass.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
// template<class F, class I1, class I2>
1212
// concept indirect_binary_predicate;
1313

14+
#include <functional>
1415
#include <iterator>
1516
#include <type_traits>
1617

1718
#include "indirectly_readable.h"
19+
#include "test_macros.h"
1820

1921
using It1 = IndirectlyReadable<struct Token1>;
2022
using It2 = IndirectlyReadable<struct Token2>;
@@ -80,3 +82,11 @@ struct BadPredicate6 {
8082
bool operator()(std::iter_common_reference_t<It1>, std::iter_common_reference_t<It2>) const = delete;
8183
};
8284
static_assert(!std::indirect_binary_predicate<BadPredicate6, It1, It2>);
85+
86+
// Test ADL-proofing (P2538R1)
87+
#if TEST_STD_VER >= 26 || defined(_LIBCPP_VERSION)
88+
struct Incomplete;
89+
template<class T> struct Holder { T t; };
90+
static_assert(std::indirect_binary_predicate<std::less<Holder<Incomplete>*>, Holder<Incomplete>**, Holder<Incomplete>**>);
91+
static_assert(!std::indirect_binary_predicate<Holder<Incomplete>*, Holder<Incomplete>**, Holder<Incomplete>**>);
92+
#endif

libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_equivalence_relation.compile.pass.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
// template<class F, class I1, class I2 = I1>
1212
// concept indirect_equivalence_relation;
1313

14-
#include <iterator>
1514
#include <concepts>
15+
#include <functional>
16+
#include <iterator>
1617

1718
#include "indirectly_readable.h"
19+
#include "test_macros.h"
1820

1921
using It1 = IndirectlyReadable<struct Token1>;
2022
using It2 = IndirectlyReadable<struct Token2>;
@@ -95,3 +97,11 @@ struct BadRelation6 {
9597
bool operator()(std::iter_common_reference_t<It1>, std::iter_common_reference_t<It2>) const = delete;
9698
};
9799
static_assert(!std::indirect_equivalence_relation<BadRelation6, It1, It2>);
100+
101+
// Test ADL-proofing (P2538R1)
102+
#if TEST_STD_VER >= 26 || defined(_LIBCPP_VERSION)
103+
struct Incomplete;
104+
template<class T> struct Holder { T t; };
105+
static_assert(std::indirect_equivalence_relation<std::equal_to<Holder<Incomplete>*>, Holder<Incomplete>**, Holder<Incomplete>**>);
106+
static_assert(!std::indirect_equivalence_relation<Holder<Incomplete>*, Holder<Incomplete>**, Holder<Incomplete>**>);
107+
#endif

libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_result_t.compile.pass.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include <iterator>
1414
#include <concepts>
1515

16+
#include "test_macros.h"
17+
1618
static_assert(std::same_as<std::indirect_result_t<int (*)(int), int*>, int>);
1719
static_assert(std::same_as<std::indirect_result_t<double (*)(int const&, float), int const*, float*>, double>);
1820

@@ -29,3 +31,18 @@ constexpr bool has_indirect_result = requires {
2931

3032
static_assert(!has_indirect_result<int (*)(int), int>); // int isn't indirectly_readable
3133
static_assert(!has_indirect_result<int, int*>); // int isn't invocable
34+
35+
// Test ADL-proofing (P2538R1)
36+
#if TEST_STD_VER >= 26 || defined(_LIBCPP_VERSION)
37+
// TODO: Enable this on GCC once this bug is fixed: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111419
38+
#ifndef TEST_COMPILER_GCC
39+
struct Incomplete;
40+
template<class T> struct Holder { T t; };
41+
static_assert(std::same_as<std::indirect_result_t<int (&)(int), int*>, int>);
42+
static_assert(std::same_as<std::indirect_result_t<Holder<Incomplete>&(&)(int), int*>, Holder<Incomplete>&>);
43+
static_assert(std::same_as<std::indirect_result_t<Holder<Incomplete>*(&)(int), int*>, Holder<Incomplete>*>);
44+
static_assert(std::same_as<std::indirect_result_t<int (&)(Holder<Incomplete>*), Holder<Incomplete>**>, int>);
45+
static_assert(std::same_as<std::indirect_result_t<Holder<Incomplete>&(&)(Holder<Incomplete>*), Holder<Incomplete>**>, Holder<Incomplete>&>);
46+
static_assert(std::same_as<std::indirect_result_t<Holder<Incomplete>*(&)(Holder<Incomplete>*), Holder<Incomplete>**>, Holder<Incomplete>*>);
47+
#endif
48+
#endif

libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_strict_weak_order.compile.pass.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
// template<class F, class I1, class I2 = I1>
1212
// concept indirect_strict_weak_order;
1313

14-
#include <iterator>
1514
#include <concepts>
15+
#include <functional>
16+
#include <iterator>
1617

1718
#include "indirectly_readable.h"
19+
#include "test_macros.h"
1820

1921
using It1 = IndirectlyReadable<struct Token1>;
2022
using It2 = IndirectlyReadable<struct Token2>;
@@ -95,3 +97,11 @@ struct BadOrder6 {
9597
bool operator()(std::iter_common_reference_t<It1>, std::iter_common_reference_t<It2>) const = delete;
9698
};
9799
static_assert(!std::indirect_strict_weak_order<BadOrder6, It1, It2>);
100+
101+
// Test ADL-proofing (P2538R1)
102+
#if TEST_STD_VER >= 26 || defined(_LIBCPP_VERSION)
103+
struct Incomplete;
104+
template<class T> struct Holder { T t; };
105+
static_assert(std::indirect_strict_weak_order<std::less<Holder<Incomplete>*>, Holder<Incomplete>**, Holder<Incomplete>**>);
106+
static_assert(!std::indirect_strict_weak_order<Holder<Incomplete>*, Holder<Incomplete>**, Holder<Incomplete>**>);
107+
#endif

libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirect_unary_predicate.compile.pass.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <type_traits>
1616

1717
#include "indirectly_readable.h"
18+
#include "test_macros.h"
1819

1920
using It = IndirectlyReadable<struct Token>;
2021

@@ -62,3 +63,12 @@ struct BadPredicate4 {
6263
bool operator()(std::iter_common_reference_t<It>) const = delete;
6364
};
6465
static_assert(!std::indirect_unary_predicate<BadPredicate4, It>);
66+
67+
// Test ADL-proofing (P2538R1)
68+
#if TEST_STD_VER >= 26 || defined(_LIBCPP_VERSION)
69+
struct Incomplete;
70+
template<class T> struct Holder { T t; };
71+
struct HolderIncompletePred { bool operator()(Holder<Incomplete>*) const; };
72+
static_assert(std::indirect_unary_predicate<HolderIncompletePred, Holder<Incomplete>**>);
73+
static_assert(!std::indirect_unary_predicate<Holder<Incomplete>*, Holder<Incomplete>**>);
74+
#endif

libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_comparable.compile.pass.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include <iterator>
1616
#include <type_traits>
1717

18+
#include "test_macros.h"
19+
1820
struct Deref {
1921
int operator()(int*) const;
2022
};
@@ -48,3 +50,11 @@ void is_subsumed(F);
4850

4951
static_assert(subsumes(std::less<int>()));
5052
static_assert(is_subsumed(std::less<int>()));
53+
54+
// Test ADL-proofing (P2538R1)
55+
#if TEST_STD_VER >= 26 || defined(_LIBCPP_VERSION)
56+
struct Incomplete;
57+
template<class T> struct Holder { T t; };
58+
static_assert(std::indirectly_comparable<Holder<Incomplete>**, Holder<Incomplete>**, std::less<Holder<Incomplete>*>>);
59+
static_assert(!std::indirectly_comparable<Holder<Incomplete>**, Holder<Incomplete>**, Holder<Incomplete>*>);
60+
#endif

libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_regular_unary_invocable.compile.pass.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <concepts>
1616

1717
#include "indirectly_readable.h"
18+
#include "test_macros.h"
1819

1920
using It = IndirectlyReadable<struct Token>;
2021
using R1 = T1<struct ReturnToken>;
@@ -85,3 +86,12 @@ static_assert(!std::indirectly_regular_unary_invocable<int (*)(int*, int*), int*
8586
static_assert(!std::indirectly_regular_unary_invocable<int (&)(int*, int*), int*>);
8687
static_assert(!std::indirectly_regular_unary_invocable<int (S::*)(int*), S*>);
8788
static_assert(!std::indirectly_regular_unary_invocable<int (S::*)(int*) const, S*>);
89+
90+
// Test ADL-proofing (P2538R1)
91+
#if TEST_STD_VER >= 26 || defined(_LIBCPP_VERSION)
92+
struct Incomplete;
93+
template<class T> struct Holder { T t; };
94+
struct HolderIncompletePred { bool operator()(Holder<Incomplete>*) const; };
95+
static_assert(std::indirectly_regular_unary_invocable<HolderIncompletePred, Holder<Incomplete>**>);
96+
static_assert(!std::indirectly_regular_unary_invocable<Holder<Incomplete>*, Holder<Incomplete>**>);
97+
#endif

libcxx/test/std/iterators/iterator.requirements/indirectcallable/indirectinvocable/indirectly_unary_invocable.compile.pass.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <concepts>
1616

1717
#include "indirectly_readable.h"
18+
#include "test_macros.h"
1819

1920
using It = IndirectlyReadable<struct Token>;
2021
using R1 = T1<struct ReturnToken>;
@@ -85,3 +86,12 @@ static_assert(!std::indirectly_unary_invocable<int (*)(int*, int*), int*>);
8586
static_assert(!std::indirectly_unary_invocable<int (&)(int*, int*), int*>);
8687
static_assert(!std::indirectly_unary_invocable<int (S::*)(int*), S*>);
8788
static_assert(!std::indirectly_unary_invocable<int (S::*)(int*) const, S*>);
89+
90+
// Test ADL-proofing (P2538R1)
91+
#if TEST_STD_VER >= 26 || defined(_LIBCPP_VERSION)
92+
struct Incomplete;
93+
template<class T> struct Holder { T t; };
94+
struct HolderIncompletePred { bool operator()(Holder<Incomplete>*) const; };
95+
static_assert(std::indirectly_unary_invocable<HolderIncompletePred, Holder<Incomplete>**>);
96+
static_assert(!std::indirectly_unary_invocable<Holder<Incomplete>*, Holder<Incomplete>**>);
97+
#endif

libcxx/utils/data/ignore_format.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,6 @@ libcxx/include/__iterator/ostreambuf_iterator.h
283283
libcxx/include/__iterator/ostream_iterator.h
284284
libcxx/include/__iterator/permutable.h
285285
libcxx/include/__iterator/prev.h
286-
libcxx/include/__iterator/projected.h
287286
libcxx/include/__iterator/readable_traits.h
288287
libcxx/include/__iterator/reverse_access.h
289288
libcxx/include/__iterator/reverse_iterator.h

0 commit comments

Comments
 (0)