From a8aacd4ad70c446486800cb6d4bc0f329ef3bc82 Mon Sep 17 00:00:00 2001 From: yronglin Date: Tue, 15 Apr 2025 15:47:57 +0800 Subject: [PATCH 01/10] [libc++] Implement P3379R0 Constrain std::expected equality operators Signed-off-by: yronglin --- libcxx/docs/Status/Cxx2cPapers.csv | 2 +- libcxx/include/__expected/expected.h | 33 +++++++++++++++---- .../equality/equality.T2.pass.cpp | 15 ++++----- .../equality/equality.other_expected.pass.cpp | 16 +++++---- .../equality/equality.unexpected.pass.cpp | 15 ++++----- .../equality/equality.other_expected.pass.cpp | 14 ++++---- .../equality/equality.unexpected.pass.cpp | 15 ++++----- libcxx/test/std/utilities/expected/types.h | 13 ++++++++ 8 files changed, 79 insertions(+), 44 deletions(-) diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index 63e19f097e301..7b13b32b3cce5 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -78,7 +78,7 @@ "","","","","","" "`P3136R1 `__","Retiring niebloids","2024-11 (Wrocław)","","","" "`P3138R5 `__","``views::cache_latest``","2024-11 (Wrocław)","","","" -"`P3379R0 `__","Constrain ``std::expected`` equality operators","2024-11 (Wrocław)","","","" +"`P3379R0 `__","Constrain ``std::expected`` equality operators","2024-11 (Wrocław)","|Complete|","21","" "`P2862R1 `__","``text_encoding::name()`` should never return null values","2024-11 (Wrocław)","","","" "`P2897R7 `__","``aligned_accessor``: An ``mdspan`` accessor expressing pointer over-alignment","2024-11 (Wrocław)","|Complete|","21","" "`P3355R1 `__","Fix ``submdspan`` for C++26","2024-11 (Wrocław)","","","" diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h index 03bbd1623ed5c..f8af6429c05ae 100644 --- a/libcxx/include/__expected/expected.h +++ b/libcxx/include/__expected/expected.h @@ -10,6 +10,7 @@ #define _LIBCPP___EXPECTED_EXPECTED_H #include <__assert> +#include <__concepts/boolean_testable.h> #include <__config> #include <__expected/bad_expected_access.h> #include <__expected/unexpect.h> @@ -1139,8 +1140,12 @@ class expected : private __expected_base<_Tp, _Err> { // [expected.object.eq], equality operators template - requires(!is_void_v<_T2>) - _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) { + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) + requires(!is_void_v<_T2>) && requires { + { *__x == *__y } -> __boolean_testable; + { __x.error() == __y.error() } -> __boolean_testable; + } + { if (__x.__has_val() != __y.__has_val()) { return false; } else { @@ -1153,12 +1158,20 @@ class expected : private __expected_base<_Tp, _Err> { } template - _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v) { + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v) + requires(!__is_std_expected<_T2>::value) && requires { + { *__x == __v } -> __boolean_testable; + } + { return __x.__has_val() && static_cast(__x.__val() == __v); } template - _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __e) { + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __e) + requires requires { + { __x.error() == __e.error() } -> __boolean_testable; + } + { return !__x.__has_val() && static_cast(__x.__unex() == __e.error()); } }; @@ -1851,7 +1864,11 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> { // [expected.void.eq], equality operators template requires is_void_v<_T2> - _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) { + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) + requires requires { + { __x.error() == __y.error() } -> __boolean_testable; + } + { if (__x.__has_val() != __y.__has_val()) { return false; } else { @@ -1860,7 +1877,11 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> { } template - _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y) { + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y) + requires requires { + { __x.error() == __y.error() } -> __boolean_testable; + } + { return !__x.__has_val() && static_cast(__x.__unex() == __y.error()); } }; diff --git a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp index bc8b9de97e4d2..2959834a85dd1 100644 --- a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp @@ -17,18 +17,17 @@ #include #include "test_macros.h" +#include "../../types.h" -struct Data { - int i; - constexpr Data(int ii) : i(ii) {} - - friend constexpr bool operator==(const Data& data, int ii) { return data.i == ii; } -}; +// https://wg21.link/P3379R0 +static_assert(CanCompare, int>); +static_assert(CanCompare, EqualityComparable>); +static_assert(!CanCompare, NonComparable>); constexpr bool test() { // x.has_value() { - const std::expected e1(std::in_place, 5); + const std::expected e1(std::in_place, 5); int i2 = 10; int i3 = 5; assert(e1 != i2); @@ -37,7 +36,7 @@ constexpr bool test() { // !x.has_value() { - const std::expected e1(std::unexpect, 5); + const std::expected e1(std::unexpect, 5); int i2 = 10; int i3 = 5; assert(e1 != i2); diff --git a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp index 9325c6c61ad2d..e2da668728e0d 100644 --- a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp @@ -18,19 +18,21 @@ #include #include "test_macros.h" +#include "../../types.h" // Test constraint -template -concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; }; - -struct Foo{}; -static_assert(!CanCompare); +static_assert(!CanCompare); static_assert(CanCompare, std::expected>); static_assert(CanCompare, std::expected>); -// Note this is true because other overloads are unconstrained -static_assert(CanCompare, std::expected>); +// https://wg21.link/P3379R0 +static_assert(!CanCompare, std::expected>); +static_assert(CanCompare, std::expected>); +static_assert(!CanCompare, std::expected>); +static_assert(!CanCompare, std::expected>); +static_assert(!CanCompare, std::expected>); +static_assert(!CanCompare, std::expected>); constexpr bool test() { // x.has_value() && y.has_value() diff --git a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.unexpected.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.unexpected.pass.cpp index a8c469d01be28..cd2db0efc48b1 100644 --- a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.unexpected.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.unexpected.pass.cpp @@ -17,18 +17,17 @@ #include #include "test_macros.h" +#include "../../types.h" -struct Data { - int i; - constexpr Data(int ii) : i(ii) {} - - friend constexpr bool operator==(const Data& data, int ii) { return data.i == ii; } -}; +// https://wg21.link/P3379R0 +static_assert(CanCompare, std::unexpected>); +static_assert(CanCompare, std::unexpected>); +static_assert(!CanCompare, std::unexpected>); constexpr bool test() { // x.has_value() { - const std::expected e1(std::in_place, 5); + const std::expected e1(std::in_place, 5); std::unexpected un2(10); std::unexpected un3(5); assert(e1 != un2); @@ -37,7 +36,7 @@ constexpr bool test() { // !x.has_value() { - const std::expected e1(std::unexpect, 5); + const std::expected e1(std::unexpect, 5); std::unexpected un2(10); std::unexpected un3(5); assert(e1 != un2); diff --git a/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp index 8b24875586852..224cbc610e78b 100644 --- a/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp @@ -18,10 +18,7 @@ #include #include "test_macros.h" - -// Test constraint -template -concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; }; +#include "../../types.h" struct Foo{}; static_assert(!CanCompare); @@ -29,8 +26,13 @@ static_assert(!CanCompare); static_assert(CanCompare, std::expected>); static_assert(CanCompare, std::expected>); -// Note this is true because other overloads in expected are unconstrained -static_assert(CanCompare, std::expected>); +// https://wg21.link/P3379R0 +static_assert(!CanCompare, std::expected>); +static_assert(CanCompare, std::expected>); +static_assert(CanCompare, std::expected>); +static_assert(!CanCompare, std::expected>); +static_assert(!CanCompare, std::expected>); +static_assert(!CanCompare, std::expected>); constexpr bool test() { // x.has_value() && y.has_value() diff --git a/libcxx/test/std/utilities/expected/expected.void/equality/equality.unexpected.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/equality/equality.unexpected.pass.cpp index 4500971131b65..4d9afaf24e3a6 100644 --- a/libcxx/test/std/utilities/expected/expected.void/equality/equality.unexpected.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.void/equality/equality.unexpected.pass.cpp @@ -17,18 +17,17 @@ #include #include "test_macros.h" +#include "../../types.h" -struct Data { - int i; - constexpr Data(int ii) : i(ii) {} - - friend constexpr bool operator==(const Data& data, int ii) { return data.i == ii; } -}; +// https://wg21.link/P3379R0 +static_assert(CanCompare, std::unexpected>); +static_assert(CanCompare, std::unexpected>); +static_assert(!CanCompare, std::unexpected>); constexpr bool test() { // x.has_value() { - const std::expected e1; + const std::expected e1; std::unexpected un2(10); std::unexpected un3(5); assert(e1 != un2); @@ -37,7 +36,7 @@ constexpr bool test() { // !x.has_value() { - const std::expected e1(std::unexpect, 5); + const std::expected e1(std::unexpect, 5); std::unexpected un2(10); std::unexpected un3(5); assert(e1 != un2); diff --git a/libcxx/test/std/utilities/expected/types.h b/libcxx/test/std/utilities/expected/types.h index df73ebdfe495e..11473ca3d97de 100644 --- a/libcxx/test/std/utilities/expected/types.h +++ b/libcxx/test/std/utilities/expected/types.h @@ -336,4 +336,17 @@ struct CheckForInvalidWrites : public CheckForInvalidWritesBase +concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; }; + #endif // TEST_STD_UTILITIES_EXPECTED_TYPES_H From f69fb888c6dafb90107cc0104c7c3b46f8140d6d Mon Sep 17 00:00:00 2001 From: yronglin Date: Tue, 22 Apr 2025 22:35:02 +0800 Subject: [PATCH 02/10] [libc++] Introduce __core_convertible_to and make this paper as a C++26 feature Signed-off-by: yronglin --- libcxx/include/CMakeLists.txt | 1 + .../include/__concepts/core_convertible_to.h | 31 +++++++++++++++++++ libcxx/include/__cxx03/module.modulemap | 1 + libcxx/include/__expected/expected.h | 31 +++++++++++++------ libcxx/include/module.modulemap.in | 1 + .../equality/equality.T2.pass.cpp | 2 ++ .../equality/equality.other_expected.pass.cpp | 6 +++- .../equality/equality.unexpected.pass.cpp | 2 ++ .../equality/equality.other_expected.pass.cpp | 5 +++ .../gn/secondary/libcxx/include/BUILD.gn | 1 + 10 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 libcxx/include/__concepts/core_convertible_to.h diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index f1bdf684a8549..ead93e7d50672 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -307,6 +307,7 @@ set(files __concepts/constructible.h __concepts/convertible_to.h __concepts/copyable.h + __concepts/core_convertible_to.h __concepts/derived_from.h __concepts/destructible.h __concepts/different_from.h diff --git a/libcxx/include/__concepts/core_convertible_to.h b/libcxx/include/__concepts/core_convertible_to.h new file mode 100644 index 0000000000000..d093cdc10e7d9 --- /dev/null +++ b/libcxx/include/__concepts/core_convertible_to.h @@ -0,0 +1,31 @@ +#ifndef _LIBCPP___CONCEPTS_CORE_CONVERTIBLE_TO_H +#define _LIBCPP___CONCEPTS_CORE_CONVERTIBLE_TO_H + +#include <__config> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +// [conv.general]/3 says "E is convertible to T" whenever "T t=E;" is well-formed. +// We can't test for that, but we can test implicit convertibility by passing it +// to a function. Unlike std::convertible_to, __core_convertible_to doesn't test +// static_cast or handle cv void, while accepting move-only types. +// +// This is a conceptual __is_core_convertible. +template +concept __core_convertible_to = requires { + // rejects function and array types which are adjusted to pointer types in parameter lists + static_cast<_Up (*)()>(nullptr)(); + static_cast(nullptr)(static_cast<_Tp (*)()>(nullptr)()); +}; + +#endif + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___CONCEPTS_CORE_CONVERTIBLE_TO_H diff --git a/libcxx/include/__cxx03/module.modulemap b/libcxx/include/__cxx03/module.modulemap index 34a2d0f25fc45..61e717d188550 100644 --- a/libcxx/include/__cxx03/module.modulemap +++ b/libcxx/include/__cxx03/module.modulemap @@ -1190,6 +1190,7 @@ module cxx03_std_private_concepts_constructible [system] { } module cxx03_std_private_concepts_convertible_to [system] { header "__concepts/convertible_to.h" } module cxx03_std_private_concepts_copyable [system] { header "__concepts/copyable.h" } +module cxx03_std_private_concepts_core_convertible_to [system] { header "__concepts/core_convertible_to.h" } module cxx03_std_private_concepts_derived_from [system] { header "__concepts/derived_from.h" } module cxx03_std_private_concepts_destructible [system] { header "__concepts/destructible.h" diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h index f8af6429c05ae..4cd169dd0cb2f 100644 --- a/libcxx/include/__expected/expected.h +++ b/libcxx/include/__expected/expected.h @@ -10,7 +10,7 @@ #define _LIBCPP___EXPECTED_EXPECTED_H #include <__assert> -#include <__concepts/boolean_testable.h> +#include <__concepts/core_convertible_to.h> #include <__config> #include <__expected/bad_expected_access.h> #include <__expected/unexpect.h> @@ -1141,10 +1141,13 @@ class expected : private __expected_base<_Tp, _Err> { // [expected.object.eq], equality operators template _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) - requires(!is_void_v<_T2>) && requires { - { *__x == *__y } -> __boolean_testable; - { __x.error() == __y.error() } -> __boolean_testable; - } + requires(!is_void_v<_T2>) +# if _LIBCPP_STD_VER >= 26 + && requires { + { *__x == *__y } -> __core_convertible_to; + { __x.error() == __y.error() } -> __core_convertible_to; + } +# endif { if (__x.__has_val() != __y.__has_val()) { return false; @@ -1159,18 +1162,22 @@ class expected : private __expected_base<_Tp, _Err> { template _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v) +# if _LIBCPP_STD_VER >= 26 requires(!__is_std_expected<_T2>::value) && requires { - { *__x == __v } -> __boolean_testable; + { *__x == __v } -> __core_convertible_to; } +# endif { return __x.__has_val() && static_cast(__x.__val() == __v); } template _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __e) +# if _LIBCPP_STD_VER >= 26 requires requires { - { __x.error() == __e.error() } -> __boolean_testable; + { __x.error() == __e.error() } -> __core_convertible_to; } +# endif { return !__x.__has_val() && static_cast(__x.__unex() == __e.error()); } @@ -1865,9 +1872,12 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> { template requires is_void_v<_T2> _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) +# if _LIBCPP_STD_VER >= 26 + requires requires { - { __x.error() == __y.error() } -> __boolean_testable; + { __x.error() == __y.error() } -> __core_convertible_to; } +# endif { if (__x.__has_val() != __y.__has_val()) { return false; @@ -1878,9 +1888,12 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> { template _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y) +# if _LIBCPP_STD_VER >= 26 + requires requires { - { __x.error() == __y.error() } -> __boolean_testable; + { __x.error() == __y.error() } -> __core_convertible_to; } +# endif { return !__x.__has_val() && static_cast(__x.__unex() == __y.error()); } diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index af928a63f2315..1c1b1594058f7 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1075,6 +1075,7 @@ module std [system] { module constructible { header "__concepts/constructible.h" } module convertible_to { header "__concepts/convertible_to.h" } module copyable { header "__concepts/copyable.h" } + module core_convertible_to { header "__concepts/core_convertible_to.h" } module derived_from { header "__concepts/derived_from.h" } module destructible { header "__concepts/destructible.h" } module different_from { header "__concepts/different_from.h" } diff --git a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp index 2959834a85dd1..25eb97a2df4d3 100644 --- a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp @@ -19,10 +19,12 @@ #include "test_macros.h" #include "../../types.h" +#if TEST_STD_VER >= 26 // https://wg21.link/P3379R0 static_assert(CanCompare, int>); static_assert(CanCompare, EqualityComparable>); static_assert(!CanCompare, NonComparable>); +#endif constexpr bool test() { // x.has_value() diff --git a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp index e2da668728e0d..f0f549b6b7772 100644 --- a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp @@ -26,6 +26,7 @@ static_assert(!CanCompare); static_assert(CanCompare, std::expected>); static_assert(CanCompare, std::expected>); +#if TEST_STD_VER >= 26 // https://wg21.link/P3379R0 static_assert(!CanCompare, std::expected>); static_assert(CanCompare, std::expected>); @@ -33,7 +34,10 @@ static_assert(!CanCompare, std::expected, std::expected>); static_assert(!CanCompare, std::expected>); static_assert(!CanCompare, std::expected>); - +#else +// Note this is true because other overloads in expected are unconstrained +static_assert(CanCompare, std::expected>); +#endif constexpr bool test() { // x.has_value() && y.has_value() { diff --git a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.unexpected.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.unexpected.pass.cpp index cd2db0efc48b1..6c7d2f39514e7 100644 --- a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.unexpected.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.unexpected.pass.cpp @@ -19,10 +19,12 @@ #include "test_macros.h" #include "../../types.h" +#if TEST_STD_VER >= 26 // https://wg21.link/P3379R0 static_assert(CanCompare, std::unexpected>); static_assert(CanCompare, std::unexpected>); static_assert(!CanCompare, std::unexpected>); +#endif constexpr bool test() { // x.has_value() diff --git a/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp index 224cbc610e78b..b6c3d8deee644 100644 --- a/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp @@ -26,6 +26,7 @@ static_assert(!CanCompare); static_assert(CanCompare, std::expected>); static_assert(CanCompare, std::expected>); +#if TEST_STD_VER >= 26 // https://wg21.link/P3379R0 static_assert(!CanCompare, std::expected>); static_assert(CanCompare, std::expected>); @@ -33,6 +34,10 @@ static_assert(CanCompare, std::expected>); static_assert(!CanCompare, std::expected>); static_assert(!CanCompare, std::expected>); static_assert(!CanCompare, std::expected>); +#else +// Note this is true because other overloads in expected are unconstrained +static_assert(CanCompare, std::expected>); +#endif constexpr bool test() { // x.has_value() && y.has_value() diff --git a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn index f1d5c86240b4a..54161267e399f 100644 --- a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn +++ b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn @@ -382,6 +382,7 @@ if (current_toolchain == default_toolchain) { "__concepts/constructible.h", "__concepts/convertible_to.h", "__concepts/copyable.h", + "__concepts/core_convertible_to.h", "__concepts/derived_from.h", "__concepts/destructible.h", "__concepts/different_from.h", From 72e61010f9b2c50ac86008daed989ca147b52357 Mon Sep 17 00:00:00 2001 From: yronglin Date: Wed, 23 Apr 2025 00:10:11 +0800 Subject: [PATCH 03/10] [libc++] Remove changes in cxx0f modulemap file Signed-off-by: yronglin --- libcxx/include/__cxx03/module.modulemap | 1 - 1 file changed, 1 deletion(-) diff --git a/libcxx/include/__cxx03/module.modulemap b/libcxx/include/__cxx03/module.modulemap index 61e717d188550..34a2d0f25fc45 100644 --- a/libcxx/include/__cxx03/module.modulemap +++ b/libcxx/include/__cxx03/module.modulemap @@ -1190,7 +1190,6 @@ module cxx03_std_private_concepts_constructible [system] { } module cxx03_std_private_concepts_convertible_to [system] { header "__concepts/convertible_to.h" } module cxx03_std_private_concepts_copyable [system] { header "__concepts/copyable.h" } -module cxx03_std_private_concepts_core_convertible_to [system] { header "__concepts/core_convertible_to.h" } module cxx03_std_private_concepts_derived_from [system] { header "__concepts/derived_from.h" } module cxx03_std_private_concepts_destructible [system] { header "__concepts/destructible.h" From 7754d351634d097e7c467f6dd3bf429d43fef06c Mon Sep 17 00:00:00 2001 From: yronglin Date: Wed, 23 Apr 2025 22:52:48 +0800 Subject: [PATCH 04/10] Update libcxx/include/__concepts/core_convertible_to.h Co-authored-by: A. Jiang --- libcxx/include/__concepts/core_convertible_to.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/include/__concepts/core_convertible_to.h b/libcxx/include/__concepts/core_convertible_to.h index d093cdc10e7d9..541318e8a7743 100644 --- a/libcxx/include/__concepts/core_convertible_to.h +++ b/libcxx/include/__concepts/core_convertible_to.h @@ -14,7 +14,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD // [conv.general]/3 says "E is convertible to T" whenever "T t=E;" is well-formed. // We can't test for that, but we can test implicit convertibility by passing it // to a function. Unlike std::convertible_to, __core_convertible_to doesn't test -// static_cast or handle cv void, while accepting move-only types. +// static_cast or handle cv void, while accepting non-movable types. // // This is a conceptual __is_core_convertible. template From 3e0f49e6784a233b9d9f8d3fb618d877c1f92e0b Mon Sep 17 00:00:00 2001 From: yronglin Date: Thu, 24 Apr 2025 22:14:52 +0800 Subject: [PATCH 05/10] [libc++] Move __core_convertible_to into __type_traits/is_core_convertible.h Signed-off-by: yronglin --- libcxx/include/CMakeLists.txt | 1 - .../include/__concepts/core_convertible_to.h | 31 ------------------- libcxx/include/__expected/expected.h | 2 +- .../__type_traits/is_core_convertible.h | 8 +++++ libcxx/include/module.modulemap.in | 1 - 5 files changed, 9 insertions(+), 34 deletions(-) delete mode 100644 libcxx/include/__concepts/core_convertible_to.h diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index ead93e7d50672..f1bdf684a8549 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -307,7 +307,6 @@ set(files __concepts/constructible.h __concepts/convertible_to.h __concepts/copyable.h - __concepts/core_convertible_to.h __concepts/derived_from.h __concepts/destructible.h __concepts/different_from.h diff --git a/libcxx/include/__concepts/core_convertible_to.h b/libcxx/include/__concepts/core_convertible_to.h deleted file mode 100644 index 541318e8a7743..0000000000000 --- a/libcxx/include/__concepts/core_convertible_to.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef _LIBCPP___CONCEPTS_CORE_CONVERTIBLE_TO_H -#define _LIBCPP___CONCEPTS_CORE_CONVERTIBLE_TO_H - -#include <__config> - -#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) -# pragma GCC system_header -#endif - -_LIBCPP_BEGIN_NAMESPACE_STD - -#if _LIBCPP_STD_VER >= 20 - -// [conv.general]/3 says "E is convertible to T" whenever "T t=E;" is well-formed. -// We can't test for that, but we can test implicit convertibility by passing it -// to a function. Unlike std::convertible_to, __core_convertible_to doesn't test -// static_cast or handle cv void, while accepting non-movable types. -// -// This is a conceptual __is_core_convertible. -template -concept __core_convertible_to = requires { - // rejects function and array types which are adjusted to pointer types in parameter lists - static_cast<_Up (*)()>(nullptr)(); - static_cast(nullptr)(static_cast<_Tp (*)()>(nullptr)()); -}; - -#endif - -_LIBCPP_END_NAMESPACE_STD - -#endif // _LIBCPP___CONCEPTS_CORE_CONVERTIBLE_TO_H diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h index 4cd169dd0cb2f..cfcef9a5a58d9 100644 --- a/libcxx/include/__expected/expected.h +++ b/libcxx/include/__expected/expected.h @@ -10,7 +10,6 @@ #define _LIBCPP___EXPECTED_EXPECTED_H #include <__assert> -#include <__concepts/core_convertible_to.h> #include <__config> #include <__expected/bad_expected_access.h> #include <__expected/unexpect.h> @@ -26,6 +25,7 @@ #include <__type_traits/is_assignable.h> #include <__type_traits/is_constructible.h> #include <__type_traits/is_convertible.h> +#include <__type_traits/is_core_convertible.h> #include <__type_traits/is_function.h> #include <__type_traits/is_nothrow_assignable.h> #include <__type_traits/is_nothrow_constructible.h> diff --git a/libcxx/include/__type_traits/is_core_convertible.h b/libcxx/include/__type_traits/is_core_convertible.h index 93e23d24d6624..d6fbe911b54d8 100644 --- a/libcxx/include/__type_traits/is_core_convertible.h +++ b/libcxx/include/__type_traits/is_core_convertible.h @@ -30,6 +30,14 @@ template struct __is_core_convertible<_Tp, _Up, decltype(static_cast(0)(static_cast<_Tp (*)()>(0)()))> : true_type {}; + +#if _LIBCPP_STD_VER >= 20 + +template +concept __core_convertible_to = __is_core_convertible_v<_Tp, _Up>; + +#endif // _LIBCPP_STD_VER >= 20 + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP___TYPE_TRAITS_IS_CORE_CONVERTIBLE_H diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 1c1b1594058f7..af928a63f2315 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1075,7 +1075,6 @@ module std [system] { module constructible { header "__concepts/constructible.h" } module convertible_to { header "__concepts/convertible_to.h" } module copyable { header "__concepts/copyable.h" } - module core_convertible_to { header "__concepts/core_convertible_to.h" } module derived_from { header "__concepts/derived_from.h" } module destructible { header "__concepts/destructible.h" } module different_from { header "__concepts/different_from.h" } From 860ad2509e5e519585378d37eaf3f72e17c78fe3 Mon Sep 17 00:00:00 2001 From: yronglin Date: Thu, 24 Apr 2025 22:20:47 +0800 Subject: [PATCH 06/10] [NFC][libc++] Format Signed-off-by: yronglin --- libcxx/include/__type_traits/is_core_convertible.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libcxx/include/__type_traits/is_core_convertible.h b/libcxx/include/__type_traits/is_core_convertible.h index d6fbe911b54d8..4dfae1b57195f 100644 --- a/libcxx/include/__type_traits/is_core_convertible.h +++ b/libcxx/include/__type_traits/is_core_convertible.h @@ -30,7 +30,6 @@ template struct __is_core_convertible<_Tp, _Up, decltype(static_cast(0)(static_cast<_Tp (*)()>(0)()))> : true_type {}; - #if _LIBCPP_STD_VER >= 20 template From fe542c0d4e91dbfe323021310480974887bbce1a Mon Sep 17 00:00:00 2001 From: yronglin Date: Thu, 24 Apr 2025 23:49:18 +0800 Subject: [PATCH 07/10] [libc++] Fix build errors Signed-off-by: yronglin --- libcxx/include/__type_traits/is_core_convertible.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/include/__type_traits/is_core_convertible.h b/libcxx/include/__type_traits/is_core_convertible.h index 4dfae1b57195f..ca3a346c17cd7 100644 --- a/libcxx/include/__type_traits/is_core_convertible.h +++ b/libcxx/include/__type_traits/is_core_convertible.h @@ -33,7 +33,7 @@ struct __is_core_convertible<_Tp, _Up, decltype(static_cast(0)(st #if _LIBCPP_STD_VER >= 20 template -concept __core_convertible_to = __is_core_convertible_v<_Tp, _Up>; +concept __core_convertible_to = __is_core_convertible<_Tp, _Up>::value; #endif // _LIBCPP_STD_VER >= 20 From e94e3c9780e45cb1314c72234b7b4860887ab120 Mon Sep 17 00:00:00 2001 From: yronglin Date: Fri, 25 Apr 2025 08:59:04 +0800 Subject: [PATCH 08/10] [libc++] Remove __concepts/core_convertible_to.h in BUILD.gn Signed-off-by: yronglin --- llvm/utils/gn/secondary/libcxx/include/BUILD.gn | 1 - 1 file changed, 1 deletion(-) diff --git a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn index 54161267e399f..f1d5c86240b4a 100644 --- a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn +++ b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn @@ -382,7 +382,6 @@ if (current_toolchain == default_toolchain) { "__concepts/constructible.h", "__concepts/convertible_to.h", "__concepts/copyable.h", - "__concepts/core_convertible_to.h", "__concepts/derived_from.h", "__concepts/destructible.h", "__concepts/different_from.h", From 58cfc99adc06924feaafc56b7ee37f70e93e61fd Mon Sep 17 00:00:00 2001 From: yronglin Date: Fri, 25 Apr 2025 21:06:04 +0800 Subject: [PATCH 09/10] [libc++] Fix test Signed-off-by: yronglin --- .../expected.void/equality/equality.unexpected.pass.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libcxx/test/std/utilities/expected/expected.void/equality/equality.unexpected.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/equality/equality.unexpected.pass.cpp index 4d9afaf24e3a6..f37f38bb71512 100644 --- a/libcxx/test/std/utilities/expected/expected.void/equality/equality.unexpected.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.void/equality/equality.unexpected.pass.cpp @@ -19,10 +19,12 @@ #include "test_macros.h" #include "../../types.h" +#if TEST_STD_VER >= 26 // https://wg21.link/P3379R0 -static_assert(CanCompare, std::unexpected>); -static_assert(CanCompare, std::unexpected>); -static_assert(!CanCompare, std::unexpected>); +static_assert(CanCompare, std::unexpected>); +static_assert(CanCompare, std::unexpected>); +static_assert(!CanCompare, std::unexpected>); +#endif constexpr bool test() { // x.has_value() From 300eefa9db54febb3a835ea1dc819a49e9215942 Mon Sep 17 00:00:00 2001 From: yronglin Date: Tue, 29 Apr 2025 20:00:40 +0800 Subject: [PATCH 10/10] [libc++] Remove empty line Signed-off-by: yronglin --- libcxx/include/__expected/expected.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h index cfcef9a5a58d9..6b3d335f2151c 100644 --- a/libcxx/include/__expected/expected.h +++ b/libcxx/include/__expected/expected.h @@ -1873,7 +1873,6 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> { requires is_void_v<_T2> _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) # if _LIBCPP_STD_VER >= 26 - requires requires { { __x.error() == __y.error() } -> __core_convertible_to; } @@ -1889,7 +1888,6 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> { template _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y) # if _LIBCPP_STD_VER >= 26 - requires requires { { __x.error() == __y.error() } -> __core_convertible_to; }