[libcxx] [llvm] [libc++] Implement P3379R0 Constrain `std::expected` equality operators (PR #135759)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 22 07:37:24 PDT 2025
https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/135759
>From a8aacd4ad70c446486800cb6d4bc0f329ef3bc82 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 15 Apr 2025 15:47:57 +0800
Subject: [PATCH 1/2] [libc++] Implement P3379R0 Constrain std::expected
equality operators
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
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 <https://wg21.link/P3136R1>`__","Retiring niebloids","2024-11 (Wrocław)","","",""
"`P3138R5 <https://wg21.link/P3138R5>`__","``views::cache_latest``","2024-11 (Wrocław)","","",""
-"`P3379R0 <https://wg21.link/P3379R0>`__","Constrain ``std::expected`` equality operators","2024-11 (Wrocław)","","",""
+"`P3379R0 <https://wg21.link/P3379R0>`__","Constrain ``std::expected`` equality operators","2024-11 (Wrocław)","|Complete|","21",""
"`P2862R1 <https://wg21.link/P2862R1>`__","``text_encoding::name()`` should never return null values","2024-11 (Wrocław)","","",""
"`P2897R7 <https://wg21.link/P2897R7>`__","``aligned_accessor``: An ``mdspan`` accessor expressing pointer over-alignment","2024-11 (Wrocław)","|Complete|","21",""
"`P3355R1 <https://wg21.link/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 <class _T2, class _E2>
- 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 <class _T2>
- _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<bool>(__x.__val() == __v);
}
template <class _E2>
- _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<bool>(__x.__unex() == __e.error());
}
};
@@ -1851,7 +1864,11 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
// [expected.void.eq], equality operators
template <class _T2, class _E2>
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 <class _E2>
- _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<bool>(__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 <utility>
#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::expected<int, int>, int>);
+static_assert(CanCompare<std::expected<int, int>, EqualityComparable>);
+static_assert(!CanCompare<std::expected<int, int>, NonComparable>);
constexpr bool test() {
// x.has_value()
{
- const std::expected<Data, int> e1(std::in_place, 5);
+ const std::expected<EqualityComparable, int> 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<Data, int> e1(std::unexpect, 5);
+ const std::expected<EqualityComparable, int> 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 <utility>
#include "test_macros.h"
+#include "../../types.h"
// Test constraint
-template <class T1, class T2>
-concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; };
-
-struct Foo{};
-static_assert(!CanCompare<Foo, Foo>);
+static_assert(!CanCompare<NonComparable, NonComparable>);
static_assert(CanCompare<std::expected<int, int>, std::expected<int, int>>);
static_assert(CanCompare<std::expected<int, int>, std::expected<short, short>>);
-// Note this is true because other overloads are unconstrained
-static_assert(CanCompare<std::expected<int, int>, std::expected<void, int>>);
+// https://wg21.link/P3379R0
+static_assert(!CanCompare<std::expected<int, int>, std::expected<void, int>>);
+static_assert(CanCompare<std::expected<int, int>, std::expected<int, int>>);
+static_assert(!CanCompare<std::expected<NonComparable, int>, std::expected<NonComparable, int>>);
+static_assert(!CanCompare<std::expected<int, NonComparable>, std::expected<int, NonComparable>>);
+static_assert(!CanCompare<std::expected<NonComparable, int>, std::expected<int, NonComparable>>);
+static_assert(!CanCompare<std::expected<int, NonComparable>, std::expected<NonComparable, int>>);
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 <utility>
#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::expected<EqualityComparable, EqualityComparable>, std::unexpected<int>>);
+static_assert(CanCompare<std::expected<EqualityComparable, int>, std::unexpected<EqualityComparable>>);
+static_assert(!CanCompare<std::expected<EqualityComparable, NonComparable>, std::unexpected<int>>);
constexpr bool test() {
// x.has_value()
{
- const std::expected<Data, Data> e1(std::in_place, 5);
+ const std::expected<EqualityComparable, EqualityComparable> e1(std::in_place, 5);
std::unexpected<int> un2(10);
std::unexpected<int> un3(5);
assert(e1 != un2);
@@ -37,7 +36,7 @@ constexpr bool test() {
// !x.has_value()
{
- const std::expected<Data, Data> e1(std::unexpect, 5);
+ const std::expected<EqualityComparable, EqualityComparable> e1(std::unexpect, 5);
std::unexpected<int> un2(10);
std::unexpected<int> 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 <utility>
#include "test_macros.h"
-
-// Test constraint
-template <class T1, class T2>
-concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; };
+#include "../../types.h"
struct Foo{};
static_assert(!CanCompare<Foo, Foo>);
@@ -29,8 +26,13 @@ static_assert(!CanCompare<Foo, Foo>);
static_assert(CanCompare<std::expected<void, int>, std::expected<void, int>>);
static_assert(CanCompare<std::expected<void, int>, std::expected<void, short>>);
-// Note this is true because other overloads in expected<non-void> are unconstrained
-static_assert(CanCompare<std::expected<void, int>, std::expected<int, int>>);
+// https://wg21.link/P3379R0
+static_assert(!CanCompare<std::expected<void, int>, std::expected<int, int>>);
+static_assert(CanCompare<std::expected<void, int>, std::expected<void, int>>);
+static_assert(CanCompare<std::expected<void, int>, std::expected<void, int>>);
+static_assert(!CanCompare<std::expected<void, NonComparable>, std::expected<void, NonComparable>>);
+static_assert(!CanCompare<std::expected<void, int>, std::expected<void, NonComparable>>);
+static_assert(!CanCompare<std::expected<void, NonComparable>, std::expected<void, int>>);
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 <utility>
#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::expected<EqualityComparable, EqualityComparable>, std::unexpected<int>>);
+static_assert(CanCompare<std::expected<EqualityComparable, int>, std::unexpected<EqualityComparable>>);
+static_assert(!CanCompare<std::expected<EqualityComparable, NonComparable>, std::unexpected<int>>);
constexpr bool test() {
// x.has_value()
{
- const std::expected<void, Data> e1;
+ const std::expected<void, EqualityComparable> e1;
std::unexpected<int> un2(10);
std::unexpected<int> un3(5);
assert(e1 != un2);
@@ -37,7 +36,7 @@ constexpr bool test() {
// !x.has_value()
{
- const std::expected<void, Data> e1(std::unexpect, 5);
+ const std::expected<void, EqualityComparable> e1(std::unexpect, 5);
std::unexpected<int> un2(10);
std::unexpected<int> 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<WithPaddedExpect
}
};
+struct NonComparable {};
+
+struct EqualityComparable {
+ int i;
+ constexpr EqualityComparable(int ii) : i(ii) {}
+
+ friend constexpr bool operator==(const EqualityComparable& data, int ii) { return data.i == ii; }
+};
+
+// Test constraint
+template <class T1, class T2>
+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 <yronglin777 at gmail.com>
Date: Tue, 22 Apr 2025 22:35:02 +0800
Subject: [PATCH 2/2] [libc++] Introduce __core_convertible_to and make this
paper as a C++26 feature
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
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 <class _Tp, class _Up>
+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<void (*)(_Up)>(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 <class _T2, class _E2>
_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<bool>;
+ { __x.error() == __y.error() } -> __core_convertible_to<bool>;
+ }
+# endif
{
if (__x.__has_val() != __y.__has_val()) {
return false;
@@ -1159,18 +1162,22 @@ class expected : private __expected_base<_Tp, _Err> {
template <class _T2>
_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<bool>;
}
+# endif
{
return __x.__has_val() && static_cast<bool>(__x.__val() == __v);
}
template <class _E2>
_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<bool>;
}
+# endif
{
return !__x.__has_val() && static_cast<bool>(__x.__unex() == __e.error());
}
@@ -1865,9 +1872,12 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
template <class _T2, class _E2>
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<bool>;
}
+# endif
{
if (__x.__has_val() != __y.__has_val()) {
return false;
@@ -1878,9 +1888,12 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
template <class _E2>
_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<bool>;
}
+# endif
{
return !__x.__has_val() && static_cast<bool>(__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<std::expected<int, int>, int>);
static_assert(CanCompare<std::expected<int, int>, EqualityComparable>);
static_assert(!CanCompare<std::expected<int, int>, 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<NonComparable, NonComparable>);
static_assert(CanCompare<std::expected<int, int>, std::expected<int, int>>);
static_assert(CanCompare<std::expected<int, int>, std::expected<short, short>>);
+#if TEST_STD_VER >= 26
// https://wg21.link/P3379R0
static_assert(!CanCompare<std::expected<int, int>, std::expected<void, int>>);
static_assert(CanCompare<std::expected<int, int>, std::expected<int, int>>);
@@ -33,7 +34,10 @@ static_assert(!CanCompare<std::expected<NonComparable, int>, std::expected<NonCo
static_assert(!CanCompare<std::expected<int, NonComparable>, std::expected<int, NonComparable>>);
static_assert(!CanCompare<std::expected<NonComparable, int>, std::expected<int, NonComparable>>);
static_assert(!CanCompare<std::expected<int, NonComparable>, std::expected<NonComparable, int>>);
-
+#else
+// Note this is true because other overloads in expected<non-void> are unconstrained
+static_assert(CanCompare<std::expected<void, int>, std::expected<int, int>>);
+#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::expected<EqualityComparable, EqualityComparable>, std::unexpected<int>>);
static_assert(CanCompare<std::expected<EqualityComparable, int>, std::unexpected<EqualityComparable>>);
static_assert(!CanCompare<std::expected<EqualityComparable, NonComparable>, std::unexpected<int>>);
+#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<Foo, Foo>);
static_assert(CanCompare<std::expected<void, int>, std::expected<void, int>>);
static_assert(CanCompare<std::expected<void, int>, std::expected<void, short>>);
+#if TEST_STD_VER >= 26
// https://wg21.link/P3379R0
static_assert(!CanCompare<std::expected<void, int>, std::expected<int, int>>);
static_assert(CanCompare<std::expected<void, int>, std::expected<void, int>>);
@@ -33,6 +34,10 @@ static_assert(CanCompare<std::expected<void, int>, std::expected<void, int>>);
static_assert(!CanCompare<std::expected<void, NonComparable>, std::expected<void, NonComparable>>);
static_assert(!CanCompare<std::expected<void, int>, std::expected<void, NonComparable>>);
static_assert(!CanCompare<std::expected<void, NonComparable>, std::expected<void, int>>);
+#else
+// Note this is true because other overloads in expected<non-void> are unconstrained
+static_assert(CanCompare<std::expected<void, int>, std::expected<int, int>>);
+#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",
More information about the llvm-commits
mailing list