Skip to content

Commit f32488c

Browse files
committed
[libc++] Implement P3379R0 Constrain std::expected equality operators
Signed-off-by: yronglin <[email protected]>
1 parent 04b6f54 commit f32488c

File tree

8 files changed

+79
-44
lines changed

8 files changed

+79
-44
lines changed

libcxx/docs/Status/Cxx2cPapers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
"","","","","",""
7979
"`P3136R1 <https://wg21.link/P3136R1>`__","Retiring niebloids","2024-11 (Wrocław)","","",""
8080
"`P3138R5 <https://wg21.link/P3138R5>`__","``views::cache_latest``","2024-11 (Wrocław)","","",""
81-
"`P3379R0 <https://wg21.link/P3379R0>`__","Constrain ``std::expected`` equality operators","2024-11 (Wrocław)","","",""
81+
"`P3379R0 <https://wg21.link/P3379R0>`__","Constrain ``std::expected`` equality operators","2024-11 (Wrocław)","|Complete|","21",""
8282
"`P2862R1 <https://wg21.link/P2862R1>`__","``text_encoding::name()`` should never return null values","2024-11 (Wrocław)","","",""
8383
"`P2897R7 <https://wg21.link/P2897R7>`__","``aligned_accessor``: An ``mdspan`` accessor expressing pointer over-alignment","2024-11 (Wrocław)","","",""
8484
"`P3355R1 <https://wg21.link/P3355R1>`__","Fix ``submdspan`` for C++26","2024-11 (Wrocław)","","",""

libcxx/include/__expected/expected.h

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define _LIBCPP___EXPECTED_EXPECTED_H
1111

1212
#include <__assert>
13+
#include <__concepts/boolean_testable.h>
1314
#include <__config>
1415
#include <__expected/bad_expected_access.h>
1516
#include <__expected/unexpect.h>
@@ -1139,8 +1140,12 @@ class expected : private __expected_base<_Tp, _Err> {
11391140

11401141
// [expected.object.eq], equality operators
11411142
template <class _T2, class _E2>
1142-
requires(!is_void_v<_T2>)
1143-
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) {
1143+
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y)
1144+
requires(!is_void_v<_T2>) && requires {
1145+
{ *__x == *__y } -> __boolean_testable;
1146+
{ __x.error() == __y.error() } -> __boolean_testable;
1147+
}
1148+
{
11441149
if (__x.__has_val() != __y.__has_val()) {
11451150
return false;
11461151
} else {
@@ -1153,12 +1158,20 @@ class expected : private __expected_base<_Tp, _Err> {
11531158
}
11541159

11551160
template <class _T2>
1156-
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v) {
1161+
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v)
1162+
requires(!__is_std_expected<_T2>::value) && requires {
1163+
{ *__x == __v } -> __boolean_testable;
1164+
}
1165+
{
11571166
return __x.__has_val() && static_cast<bool>(__x.__val() == __v);
11581167
}
11591168

11601169
template <class _E2>
1161-
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __e) {
1170+
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __e)
1171+
requires requires {
1172+
{ __x.error() == __e.error() } -> __boolean_testable;
1173+
}
1174+
{
11621175
return !__x.__has_val() && static_cast<bool>(__x.__unex() == __e.error());
11631176
}
11641177
};
@@ -1851,7 +1864,11 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
18511864
// [expected.void.eq], equality operators
18521865
template <class _T2, class _E2>
18531866
requires is_void_v<_T2>
1854-
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) {
1867+
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y)
1868+
requires requires {
1869+
{ __x.error() == __y.error() } -> __boolean_testable;
1870+
}
1871+
{
18551872
if (__x.__has_val() != __y.__has_val()) {
18561873
return false;
18571874
} else {
@@ -1860,7 +1877,11 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
18601877
}
18611878

18621879
template <class _E2>
1863-
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y) {
1880+
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y)
1881+
requires requires {
1882+
{ __x.error() == __y.error() } -> __boolean_testable;
1883+
}
1884+
{
18641885
return !__x.__has_val() && static_cast<bool>(__x.__unex() == __y.error());
18651886
}
18661887
};

libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,17 @@
1717
#include <utility>
1818

1919
#include "test_macros.h"
20+
#include "../../types.h"
2021

21-
struct Data {
22-
int i;
23-
constexpr Data(int ii) : i(ii) {}
24-
25-
friend constexpr bool operator==(const Data& data, int ii) { return data.i == ii; }
26-
};
22+
// https://wg21.link/P3379R0
23+
static_assert(CanCompare<std::expected<int, int>, int>);
24+
static_assert(CanCompare<std::expected<int, int>, EqualityComparable>);
25+
static_assert(!CanCompare<std::expected<int, int>, NonComparable>);
2726

2827
constexpr bool test() {
2928
// x.has_value()
3029
{
31-
const std::expected<Data, int> e1(std::in_place, 5);
30+
const std::expected<EqualityComparable, int> e1(std::in_place, 5);
3231
int i2 = 10;
3332
int i3 = 5;
3433
assert(e1 != i2);
@@ -37,7 +36,7 @@ constexpr bool test() {
3736

3837
// !x.has_value()
3938
{
40-
const std::expected<Data, int> e1(std::unexpect, 5);
39+
const std::expected<EqualityComparable, int> e1(std::unexpect, 5);
4140
int i2 = 10;
4241
int i3 = 5;
4342
assert(e1 != i2);

libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,21 @@
1818
#include <utility>
1919

2020
#include "test_macros.h"
21+
#include "../../types.h"
2122

2223
// Test constraint
23-
template <class T1, class T2>
24-
concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; };
25-
26-
struct Foo{};
27-
static_assert(!CanCompare<Foo, Foo>);
24+
static_assert(!CanCompare<NonComparable, NonComparable>);
2825

2926
static_assert(CanCompare<std::expected<int, int>, std::expected<int, int>>);
3027
static_assert(CanCompare<std::expected<int, int>, std::expected<short, short>>);
3128

32-
// Note this is true because other overloads are unconstrained
33-
static_assert(CanCompare<std::expected<int, int>, std::expected<void, int>>);
29+
// https://wg21.link/P3379R0
30+
static_assert(!CanCompare<std::expected<int, int>, std::expected<void, int>>);
31+
static_assert(CanCompare<std::expected<int, int>, std::expected<int, int>>);
32+
static_assert(!CanCompare<std::expected<NonComparable, int>, std::expected<NonComparable, int>>);
33+
static_assert(!CanCompare<std::expected<int, NonComparable>, std::expected<int, NonComparable>>);
34+
static_assert(!CanCompare<std::expected<NonComparable, int>, std::expected<int, NonComparable>>);
35+
static_assert(!CanCompare<std::expected<int, NonComparable>, std::expected<NonComparable, int>>);
3436

3537
constexpr bool test() {
3638
// x.has_value() && y.has_value()

libcxx/test/std/utilities/expected/expected.expected/equality/equality.unexpected.pass.cpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,17 @@
1717
#include <utility>
1818

1919
#include "test_macros.h"
20+
#include "../../types.h"
2021

21-
struct Data {
22-
int i;
23-
constexpr Data(int ii) : i(ii) {}
24-
25-
friend constexpr bool operator==(const Data& data, int ii) { return data.i == ii; }
26-
};
22+
// https://wg21.link/P3379R0
23+
static_assert(CanCompare<std::expected<EqualityComparable, EqualityComparable>, std::unexpected<int>>);
24+
static_assert(CanCompare<std::expected<EqualityComparable, int>, std::unexpected<EqualityComparable>>);
25+
static_assert(!CanCompare<std::expected<EqualityComparable, NonComparable>, std::unexpected<int>>);
2726

2827
constexpr bool test() {
2928
// x.has_value()
3029
{
31-
const std::expected<Data, Data> e1(std::in_place, 5);
30+
const std::expected<EqualityComparable, EqualityComparable> e1(std::in_place, 5);
3231
std::unexpected<int> un2(10);
3332
std::unexpected<int> un3(5);
3433
assert(e1 != un2);
@@ -37,7 +36,7 @@ constexpr bool test() {
3736

3837
// !x.has_value()
3938
{
40-
const std::expected<Data, Data> e1(std::unexpect, 5);
39+
const std::expected<EqualityComparable, EqualityComparable> e1(std::unexpect, 5);
4140
std::unexpected<int> un2(10);
4241
std::unexpected<int> un3(5);
4342
assert(e1 != un2);

libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,21 @@
1818
#include <utility>
1919

2020
#include "test_macros.h"
21-
22-
// Test constraint
23-
template <class T1, class T2>
24-
concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; };
21+
#include "../../types.h"
2522

2623
struct Foo{};
2724
static_assert(!CanCompare<Foo, Foo>);
2825

2926
static_assert(CanCompare<std::expected<void, int>, std::expected<void, int>>);
3027
static_assert(CanCompare<std::expected<void, int>, std::expected<void, short>>);
3128

32-
// Note this is true because other overloads in expected<non-void> are unconstrained
33-
static_assert(CanCompare<std::expected<void, int>, std::expected<int, int>>);
29+
// https://wg21.link/P3379R0
30+
static_assert(!CanCompare<std::expected<void, int>, std::expected<int, int>>);
31+
static_assert(CanCompare<std::expected<void, int>, std::expected<void, int>>);
32+
static_assert(CanCompare<std::expected<void, int>, std::expected<void, int>>);
33+
static_assert(!CanCompare<std::expected<void, NonComparable>, std::expected<void, NonComparable>>);
34+
static_assert(!CanCompare<std::expected<void, int>, std::expected<void, NonComparable>>);
35+
static_assert(!CanCompare<std::expected<void, NonComparable>, std::expected<void, int>>);
3436

3537
constexpr bool test() {
3638
// x.has_value() && y.has_value()

libcxx/test/std/utilities/expected/expected.void/equality/equality.unexpected.pass.cpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,17 @@
1717
#include <utility>
1818

1919
#include "test_macros.h"
20+
#include "../../types.h"
2021

21-
struct Data {
22-
int i;
23-
constexpr Data(int ii) : i(ii) {}
24-
25-
friend constexpr bool operator==(const Data& data, int ii) { return data.i == ii; }
26-
};
22+
// https://wg21.link/P3379R0
23+
static_assert(CanCompare<std::expected<EqualityComparable, EqualityComparable>, std::unexpected<int>>);
24+
static_assert(CanCompare<std::expected<EqualityComparable, int>, std::unexpected<EqualityComparable>>);
25+
static_assert(!CanCompare<std::expected<EqualityComparable, NonComparable>, std::unexpected<int>>);
2726

2827
constexpr bool test() {
2928
// x.has_value()
3029
{
31-
const std::expected<void, Data> e1;
30+
const std::expected<void, EqualityComparable> e1;
3231
std::unexpected<int> un2(10);
3332
std::unexpected<int> un3(5);
3433
assert(e1 != un2);
@@ -37,7 +36,7 @@ constexpr bool test() {
3736

3837
// !x.has_value()
3938
{
40-
const std::expected<void, Data> e1(std::unexpect, 5);
39+
const std::expected<void, EqualityComparable> e1(std::unexpect, 5);
4140
std::unexpected<int> un2(10);
4241
std::unexpected<int> un3(5);
4342
assert(e1 != un2);

libcxx/test/std/utilities/expected/types.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,4 +336,17 @@ struct CheckForInvalidWrites : public CheckForInvalidWritesBase<WithPaddedExpect
336336
}
337337
};
338338

339+
struct NonComparable {};
340+
341+
struct EqualityComparable {
342+
int i;
343+
constexpr EqualityComparable(int ii) : i(ii) {}
344+
345+
friend constexpr bool operator==(const EqualityComparable& data, int ii) { return data.i == ii; }
346+
};
347+
348+
// Test constraint
349+
template <class T1, class T2>
350+
concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; };
351+
339352
#endif // TEST_STD_UTILITIES_EXPECTED_TYPES_H

0 commit comments

Comments
 (0)