Skip to content

[libc++] Implement P2538R1 "ADL-proof std::projected" #65411

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions libcxx/docs/ReleaseNotes/18.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,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
-----------------------------
Expand Down Expand Up @@ -126,5 +128,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
--------------------
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx2cPapers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -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","","",""
Expand Down
28 changes: 20 additions & 8 deletions libcxx/include/__iterator/projected.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down Expand Up @@ -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 || defined(_LIBCPP_VERSION)
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
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down Expand Up @@ -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 || defined(_LIBCPP_VERSION)
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
Original file line number Diff line number Diff line change
Expand Up @@ -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>);

Expand All @@ -29,3 +31,18 @@ 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 || defined(_LIBCPP_VERSION)
// TODO: Enable this on GCC once this bug is fixed: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111419
#ifndef TEST_COMPILER_GCC
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
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down Expand Up @@ -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 || defined(_LIBCPP_VERSION)
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <type_traits>

#include "indirectly_readable.h"
#include "test_macros.h"

using It = IndirectlyReadable<struct Token>;

Expand Down Expand Up @@ -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 || defined(_LIBCPP_VERSION)
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include <iterator>
#include <type_traits>

#include "test_macros.h"

struct Deref {
int operator()(int*) const;
};
Expand Down Expand Up @@ -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 || defined(_LIBCPP_VERSION)
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <concepts>

#include "indirectly_readable.h"
#include "test_macros.h"

using It = IndirectlyReadable<struct Token>;
using R1 = T1<struct ReturnToken>;
Expand Down Expand Up @@ -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 || defined(_LIBCPP_VERSION)
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <concepts>

#include "indirectly_readable.h"
#include "test_macros.h"

using It = IndirectlyReadable<struct Token>;
using R1 = T1<struct ReturnToken>;
Expand Down Expand Up @@ -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 || defined(_LIBCPP_VERSION)
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
1 change: 0 additions & 1 deletion libcxx/utils/data/ignore_format.txt
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,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
Expand Down