-
Notifications
You must be signed in to change notification settings - Fork 13.3k
[libc++] Implement P3379R0 Constrain std::expected
equality operators
#135759
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
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-libcxx Author: None (yronglin) ChangesThis PR implement P3379R0 Constrain std::expected equality operators. Full diff: https://github.com/llvm/llvm-project/pull/135759.diff 8 Files Affected:
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index b40d9eb58c24e..be591c9b6a587 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)","","",""
"`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
|
std::expected
equality operators
@yronglin A few comments:
|
Thanks for your review!
Thanks for reminding me, I didn't aware the change in the process before.
Yes, I'd like to implement the rest of P2944R3.
I reuse the test data between the types because it's very similar, but I have no preference. Should we revert the reuses things? |
Signed-off-by: yronglin <yronglin777@gmail.com>
…26 feature Signed-off-by: yronglin <yronglin777@gmail.com>
Signed-off-by: yronglin <yronglin777@gmail.com>
@yronglin Please add
to the top comment in the PR to link this PR to the GitHub issue as @frederick-vs-ja suggested above. |
Co-authored-by: A. Jiang <de34@live.cn>
…tible.h Signed-off-by: yronglin <yronglin777@gmail.com>
✅ With the latest revision this PR passed the C/C++ code formatter. |
Signed-off-by: yronglin <yronglin777@gmail.com>
Signed-off-by: yronglin <yronglin777@gmail.com>
Signed-off-by: yronglin <yronglin777@gmail.com>
libcxx/test/std/utilities/expected/expected.void/equality/equality.unexpected.pass.cpp
Outdated
Show resolved
Hide resolved
Signed-off-by: yronglin <yronglin777@gmail.com>
Closes #118135
This PR implement P3379R0 Constrain std::expected equality operators.