Skip to content

Commit 9f67143

Browse files
committed
[libc++] Implement LWG3938 (Cannot use std::expected monadic ops with move-only error_type)
Implement LWG3938 (Cannot use std::expected monadic ops with move-only error_type) https://wg21.link/LWG3938 Reviewed By: #libc, ldionne Differential Revision: https://reviews.llvm.org/D154116
1 parent 1f3fa96 commit 9f67143

File tree

8 files changed

+180
-46
lines changed

8 files changed

+180
-46
lines changed

libcxx/docs/Status/Cxx2cIssues.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"`3925 <https://wg21.link/LWG3925>`__","Concept ``formattable``'s definition is incorrect","Varna June 2023","|Complete|","17.0","|format|"
1515
"`3927 <https://wg21.link/LWG3927>`__","Unclear preconditions for ``operator[]`` for sequence containers","Varna June 2023","|Nothing To Do|","",""
1616
"`3935 <https://wg21.link/LWG3935>`__","``template<class X> constexpr complex& operator=(const complex<X>&)`` has no specification","Varna June 2023","|Complete|","3.4",""
17-
"`3938 <https://wg21.link/LWG3938>`__","Cannot use ``std::expected`` monadic ops with move-only ``error_type``","Varna June 2023","","",""
17+
"`3938 <https://wg21.link/LWG3938>`__","Cannot use ``std::expected`` monadic ops with move-only ``error_type``","Varna June 2023","|Complete|","18.0",""
1818
"`3940 <https://wg21.link/LWG3940>`__","``std::expected<void, E>::value()`` also needs ``E`` to be copy constructible","Varna June 2023","","",""
1919
"","","","","",""
2020
"`3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","Not Yet Adopted","|Complete|","16.0",""

libcxx/include/__expected/expected.h

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -651,11 +651,11 @@ class expected {
651651
requires is_constructible_v<_Err, _Err&>
652652
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
653653
using _Up = remove_cvref_t<invoke_result_t<_Func, _Tp&>>;
654-
static_assert(__is_std_expected<_Up>::value, "The result of f(value()) must be a specialization of std::expected");
654+
static_assert(__is_std_expected<_Up>::value, "The result of f(**this) must be a specialization of std::expected");
655655
static_assert(is_same_v<typename _Up::error_type, _Err>,
656-
"The result of f(value()) must have the same error_type as this expected");
656+
"The result of f(**this) must have the same error_type as this expected");
657657
if (has_value()) {
658-
return std::invoke(std::forward<_Func>(__f), value());
658+
return std::invoke(std::forward<_Func>(__f), __union_.__val_);
659659
}
660660
return _Up(unexpect, error());
661661
}
@@ -664,11 +664,11 @@ class expected {
664664
requires is_constructible_v<_Err, const _Err&>
665665
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& {
666666
using _Up = remove_cvref_t<invoke_result_t<_Func, const _Tp&>>;
667-
static_assert(__is_std_expected<_Up>::value, "The result of f(value()) must be a specialization of std::expected");
667+
static_assert(__is_std_expected<_Up>::value, "The result of f(**this) must be a specialization of std::expected");
668668
static_assert(is_same_v<typename _Up::error_type, _Err>,
669-
"The result of f(value()) must have the same error_type as this expected");
669+
"The result of f(**this) must have the same error_type as this expected");
670670
if (has_value()) {
671-
return std::invoke(std::forward<_Func>(__f), value());
671+
return std::invoke(std::forward<_Func>(__f), __union_.__val_);
672672
}
673673
return _Up(unexpect, error());
674674
}
@@ -678,11 +678,11 @@ class expected {
678678
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
679679
using _Up = remove_cvref_t<invoke_result_t<_Func, _Tp&&>>;
680680
static_assert(
681-
__is_std_expected<_Up>::value, "The result of f(std::move(value())) must be a specialization of std::expected");
681+
__is_std_expected<_Up>::value, "The result of f(std::move(**this)) must be a specialization of std::expected");
682682
static_assert(is_same_v<typename _Up::error_type, _Err>,
683-
"The result of f(std::move(value())) must have the same error_type as this expected");
683+
"The result of f(std::move(**this)) must have the same error_type as this expected");
684684
if (has_value()) {
685-
return std::invoke(std::forward<_Func>(__f), std::move(value()));
685+
return std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_));
686686
}
687687
return _Up(unexpect, std::move(error()));
688688
}
@@ -692,11 +692,11 @@ class expected {
692692
_LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
693693
using _Up = remove_cvref_t<invoke_result_t<_Func, const _Tp&&>>;
694694
static_assert(
695-
__is_std_expected<_Up>::value, "The result of f(std::move(value())) must be a specialization of std::expected");
695+
__is_std_expected<_Up>::value, "The result of f(std::move(**this)) must be a specialization of std::expected");
696696
static_assert(is_same_v<typename _Up::error_type, _Err>,
697-
"The result of f(std::move(value())) must have the same error_type as this expected");
697+
"The result of f(std::move(**this)) must have the same error_type as this expected");
698698
if (has_value()) {
699-
return std::invoke(std::forward<_Func>(__f), std::move(value()));
699+
return std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_));
700700
}
701701
return _Up(unexpect, std::move(error()));
702702
}
@@ -709,7 +709,7 @@ class expected {
709709
static_assert(is_same_v<typename _Gp::value_type, _Tp>,
710710
"The result of f(error()) must have the same value_type as this expected");
711711
if (has_value()) {
712-
return _Gp(in_place, value());
712+
return _Gp(in_place, __union_.__val_);
713713
}
714714
return std::invoke(std::forward<_Func>(__f), error());
715715
}
@@ -722,7 +722,7 @@ class expected {
722722
static_assert(is_same_v<typename _Gp::value_type, _Tp>,
723723
"The result of f(error()) must have the same value_type as this expected");
724724
if (has_value()) {
725-
return _Gp(in_place, value());
725+
return _Gp(in_place, __union_.__val_);
726726
}
727727
return std::invoke(std::forward<_Func>(__f), error());
728728
}
@@ -736,7 +736,7 @@ class expected {
736736
static_assert(is_same_v<typename _Gp::value_type, _Tp>,
737737
"The result of f(std::move(error())) must have the same value_type as this expected");
738738
if (has_value()) {
739-
return _Gp(in_place, std::move(value()));
739+
return _Gp(in_place, std::move(__union_.__val_));
740740
}
741741
return std::invoke(std::forward<_Func>(__f), std::move(error()));
742742
}
@@ -750,7 +750,7 @@ class expected {
750750
static_assert(is_same_v<typename _Gp::value_type, _Tp>,
751751
"The result of f(std::move(error())) must have the same value_type as this expected");
752752
if (has_value()) {
753-
return _Gp(in_place, std::move(value()));
753+
return _Gp(in_place, std::move(__union_.__val_));
754754
}
755755
return std::invoke(std::forward<_Func>(__f), std::move(error()));
756756
}
@@ -763,9 +763,9 @@ class expected {
763763
return expected<_Up, _Err>(unexpect, error());
764764
}
765765
if constexpr (!is_void_v<_Up>) {
766-
return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), value());
766+
return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), __union_.__val_);
767767
} else {
768-
std::invoke(std::forward<_Func>(__f), value());
768+
std::invoke(std::forward<_Func>(__f), __union_.__val_);
769769
return expected<_Up, _Err>();
770770
}
771771
}
@@ -778,9 +778,9 @@ class expected {
778778
return expected<_Up, _Err>(unexpect, error());
779779
}
780780
if constexpr (!is_void_v<_Up>) {
781-
return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), value());
781+
return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), __union_.__val_);
782782
} else {
783-
std::invoke(std::forward<_Func>(__f), value());
783+
std::invoke(std::forward<_Func>(__f), __union_.__val_);
784784
return expected<_Up, _Err>();
785785
}
786786
}
@@ -794,9 +794,9 @@ class expected {
794794
}
795795
if constexpr (!is_void_v<_Up>) {
796796
return expected<_Up, _Err>(
797-
__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
797+
__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(__union_.__val_));
798798
} else {
799-
std::invoke(std::forward<_Func>(__f), std::move(value()));
799+
std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_));
800800
return expected<_Up, _Err>();
801801
}
802802
}
@@ -810,9 +810,9 @@ class expected {
810810
}
811811
if constexpr (!is_void_v<_Up>) {
812812
return expected<_Up, _Err>(
813-
__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value()));
813+
__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(__union_.__val_));
814814
} else {
815-
std::invoke(std::forward<_Func>(__f), std::move(value()));
815+
std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_));
816816
return expected<_Up, _Err>();
817817
}
818818
}
@@ -824,7 +824,7 @@ class expected {
824824
static_assert(__valid_std_unexpected<_Gp>::value,
825825
"The result of f(error()) must be a valid template argument for unexpected");
826826
if (has_value()) {
827-
return expected<_Tp, _Gp>(in_place, value());
827+
return expected<_Tp, _Gp>(in_place, __union_.__val_);
828828
}
829829
return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error());
830830
}
@@ -836,7 +836,7 @@ class expected {
836836
static_assert(__valid_std_unexpected<_Gp>::value,
837837
"The result of f(error()) must be a valid template argument for unexpected");
838838
if (has_value()) {
839-
return expected<_Tp, _Gp>(in_place, value());
839+
return expected<_Tp, _Gp>(in_place, __union_.__val_);
840840
}
841841
return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error());
842842
}
@@ -848,7 +848,7 @@ class expected {
848848
static_assert(__valid_std_unexpected<_Gp>::value,
849849
"The result of f(std::move(error())) must be a valid template argument for unexpected");
850850
if (has_value()) {
851-
return expected<_Tp, _Gp>(in_place, std::move(value()));
851+
return expected<_Tp, _Gp>(in_place, std::move(__union_.__val_));
852852
}
853853
return expected<_Tp, _Gp>(
854854
__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error()));
@@ -861,7 +861,7 @@ class expected {
861861
static_assert(__valid_std_unexpected<_Gp>::value,
862862
"The result of f(std::move(error())) must be a valid template argument for unexpected");
863863
if (has_value()) {
864-
return expected<_Tp, _Gp>(in_place, std::move(value()));
864+
return expected<_Tp, _Gp>(in_place, std::move(__union_.__val_));
865865
}
866866
return expected<_Tp, _Gp>(
867867
__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error()));

libcxx/test/libcxx/utilities/expected/expected.expected/and_then.mandates.verify.cpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,22 @@
1111
// Test the mandates
1212
// template<class F> constexpr auto and_then(F&& f) &;
1313
// Mandates:
14-
// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(value())>>
14+
// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(**this)>>
1515
// U is a specialization of std::expected and std::is_same_v<U:error_type, E> is true
1616

1717
// template<class F> constexpr auto and_then(F&& f) const &;
1818
// Mandates:
19-
// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(value())>>
19+
// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(**this)>>
2020
// U is a specialization of std::expected and std::is_same_v<U:error_type, E> is true
2121

2222
// template<class F> constexpr auto and_then(F&& f) &&;
2323
// Mandates:
24-
// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(value())>>
24+
// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(**this)>>
2525
// U is a specialization of std::expected and std::is_same_v<U:error_type, E> is true
2626

2727
// template<class F> constexpr auto and_then(F&& f) const &&;
2828
// Mandates:
29-
// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(value())>>
29+
// Let U be std::remove_cvref_t<std::invoke_result<F, decltype(**this)>>
3030
// U is a specialization of std::expected and std::is_same_v<U:error_type, E> is true
3131

3232
#include <expected>
@@ -52,7 +52,7 @@ void test() {
5252
{
5353
std::expected<int, int> f1(1);
5454
f1.and_then(lval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<int (&)(int &)>' requested here}}
55-
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(value()) must be a specialization of std::expected}}
55+
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(**this) must be a specialization of std::expected}}
5656
// expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}}
5757
// expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}}
5858
}
@@ -61,7 +61,7 @@ void test() {
6161
{
6262
std::expected<int, int> f1(1);
6363
f1.and_then(lval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<std::expected<int, NotSameAsInt> (&)(int &)>' requested here}}
64-
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(value()) must have the same error_type as this expected}}
64+
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(**this) must have the same error_type as this expected}}
6565
}
6666
}
6767

@@ -71,7 +71,7 @@ void test() {
7171
{
7272
const std::expected<int, int> f1(1);
7373
f1.and_then(clval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<int (&)(const int &)>' requested here}}
74-
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(value()) must be a specialization of std::expected}}
74+
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(**this) must be a specialization of std::expected}}
7575
// expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}}
7676
// expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}}
7777
}
@@ -80,7 +80,7 @@ void test() {
8080
{
8181
const std::expected<int, int> f1(1);
8282
f1.and_then(clval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<std::expected<int, NotSameAsInt> (&)(const int &)>' requested here}}
83-
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(value()) must have the same error_type as this expected}}
83+
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(**this) must have the same error_type as this expected}}
8484

8585
}
8686
}
@@ -91,7 +91,7 @@ void test() {
9191
{
9292
std::expected<int, int> f1(1);
9393
std::move(f1).and_then(rval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<int (&)(int &&)>' requested here}}
94-
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(value())) must be a specialization of std::expected}}
94+
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(**this)) must be a specialization of std::expected}}
9595
// expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}}
9696
// expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}}
9797
}
@@ -100,7 +100,7 @@ void test() {
100100
{
101101
std::expected<int, int> f1(1);
102102
std::move(f1).and_then(rval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<std::expected<int, NotSameAsInt> (&)(int &&)>' requested here}}
103-
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(value())) must have the same error_type as this expected}}
103+
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(**this)) must have the same error_type as this expected}}
104104
}
105105
}
106106

@@ -110,7 +110,7 @@ void test() {
110110
{
111111
const std::expected<int, int> f1(1);
112112
std::move(f1).and_then(crval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<int (&)(const int &&)>' requested here}}
113-
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(value())) must be a specialization of std::expected}}
113+
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(**this)) must be a specialization of std::expected}}
114114
// expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}}
115115
// expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}}
116116
}
@@ -119,7 +119,7 @@ void test() {
119119
{
120120
const std::expected<int, int> f1(1);
121121
std::move(f1).and_then(crval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected<int, int>::and_then<std::expected<int, NotSameAsInt> (&)(const int &&)>' requested here}}
122-
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(value())) must have the same error_type as this expected}}
122+
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(**this)) must have the same error_type as this expected}}
123123
}
124124
}
125125
}

libcxx/test/std/utilities/expected/expected.expected/monadic/and_then.pass.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include <type_traits>
2323
#include <utility>
2424

25+
#include "../../types.h"
26+
2527
struct LVal {
2628
constexpr std::expected<int, int> operator()(int&) { return 1; }
2729
std::expected<int, int> operator()(const int&) = delete;
@@ -153,6 +155,7 @@ concept has_and_then = requires(E&& e, F&& f) {
153155
{std::forward<E>(e).and_then(std::forward<F>(f))};
154156
};
155157

158+
// clang-format off
156159
static_assert( has_and_then<std::expected<int, int>&, std::expected<int, int>(int&)>);
157160
static_assert(!has_and_then<std::expected<int, NonCopyable>&, std::expected<int, NonCopyable>(int&)>);
158161
static_assert( has_and_then<const std::expected<int, int>&, std::expected<int, int>(const int&)>);
@@ -166,7 +169,11 @@ static_assert(!has_and_then<const std::expected<int, NonMovable>&&, std::expecte
166169
static_assert(!has_and_then<const std::expected<int, std::unique_ptr<int>>&, int()>);
167170
static_assert(!has_and_then<const std::expected<int, std::unique_ptr<int>>&&, int()>);
168171

169-
// clang-format off
172+
// [LWG 3983] https://cplusplus.github.io/LWG/issue3938, check std::expected monadic ops well-formed with move-only error_type.
173+
// There are no effects for `&` and `const &` overload, because the constraints requires is_constructible_v<E, decltype(error())> is true.
174+
static_assert(has_and_then<std::expected<int, MoveOnlyErrorType>&&, std::expected<int, MoveOnlyErrorType>(int)>);
175+
static_assert(has_and_then<const std::expected<int, MoveOnlyErrorType>&&, std::expected<int, MoveOnlyErrorType>(const int)>);
176+
170177
constexpr void test_val_types() {
171178
// Test & overload
172179
{
@@ -260,9 +267,26 @@ constexpr void test_sfinae() {
260267
std::move(e).and_then(l);
261268
}
262269

270+
constexpr void test_move_only_error_type() {
271+
// Test &&
272+
{
273+
std::expected<int, MoveOnlyErrorType> e;
274+
auto l = [](int) { return std::expected<int, MoveOnlyErrorType>{}; };
275+
std::move(e).and_then(l);
276+
}
277+
278+
// Test const&&
279+
{
280+
const std::expected<int, MoveOnlyErrorType> e;
281+
auto l = [](const int) { return std::expected<int, MoveOnlyErrorType>{}; };
282+
std::move(e).and_then(l);
283+
}
284+
}
285+
263286
constexpr bool test() {
264287
test_sfinae();
265288
test_val_types();
289+
test_move_only_error_type();
266290

267291
std::expected<int, int> e(std::unexpected<int>(1));
268292
const auto& ce = e;

0 commit comments

Comments
 (0)