Skip to content

Commit 8578314

Browse files
Implement LWG-3886 Monad mo' problems (in optional and expected) (#5232)
Co-authored-by: Stephan T. Lavavej <[email protected]>
1 parent 9ec1309 commit 8578314

File tree

4 files changed

+315
-37
lines changed

4 files changed

+315
-37
lines changed

stl/inc/expected

+24-18
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ public:
304304
}
305305
}
306306

307-
template <class _Uty = _Ty>
307+
template <class _Uty = remove_cv_t<_Ty>>
308308
requires (!is_same_v<remove_cvref_t<_Uty>, in_place_t> && !is_same_v<remove_cvref_t<_Uty>, expected>
309309
&& !_Is_specialization_v<remove_cvref_t<_Uty>, unexpected>
310310
&& (!is_same_v<remove_cv_t<_Ty>, bool>
@@ -469,7 +469,7 @@ public:
469469
&& _Trivially_move_constructible_assignable_destructible<_Err>
470470
= default;
471471

472-
template <class _Uty = _Ty>
472+
template <class _Uty = remove_cv_t<_Ty>>
473473
requires (!is_same_v<remove_cvref_t<_Uty>, expected> && !_Is_specialization_v<remove_cvref_t<_Uty>, unexpected>
474474
&& is_constructible_v<_Ty, _Uty> && is_assignable_v<_Ty&, _Uty>
475475
&& (is_nothrow_constructible_v<_Ty, _Uty> || is_nothrow_move_constructible_v<_Ty>
@@ -730,32 +730,38 @@ public:
730730
return _STD move(_Unexpected);
731731
}
732732

733-
template <class _Uty>
734-
_NODISCARD constexpr _Ty value_or(_Uty&& _Other) const& noexcept(
735-
is_nothrow_copy_constructible_v<_Ty> && is_nothrow_convertible_v<_Uty, _Ty>) /* strengthened */ {
736-
static_assert(
737-
is_copy_constructible_v<_Ty>, "is_copy_constructible_v<T> must be true. (N4950 [expected.object.obs]/18)");
738-
static_assert(
739-
is_convertible_v<_Uty, _Ty>, "is_convertible_v<U, T> must be true. (N4950 [expected.object.obs]/18)");
733+
template <class _Uty = remove_cv_t<_Ty>>
734+
_NODISCARD constexpr remove_cv_t<_Ty> value_or(_Uty&& _Other) const& noexcept(
735+
is_nothrow_convertible_v<const _Ty&, remove_cv_t<_Ty>>
736+
&& is_nothrow_convertible_v<_Uty, remove_cv_t<_Ty>>) /* strengthened */ {
737+
static_assert(is_convertible_v<const _Ty&, remove_cv_t<_Ty>>,
738+
"is_convertible_v<const T&, remove_cv_t<T>> must be true. "
739+
"(N5001 [expected.object.obs]/18 as modified by LWG-3424)");
740+
static_assert(is_convertible_v<_Uty, remove_cv_t<_Ty>>,
741+
"is_convertible_v<U, remove_cv_t<T>> must be true. "
742+
"(N5001 [expected.object.obs]/18 as modified by LWG-3424)");
740743

741744
if (_Has_value) {
742745
return _Value;
743746
} else {
744-
return static_cast<_Ty>(_STD forward<_Uty>(_Other));
747+
return static_cast<remove_cv_t<_Ty>>(_STD forward<_Uty>(_Other));
745748
}
746749
}
747-
template <class _Uty>
748-
_NODISCARD constexpr _Ty value_or(_Uty&& _Other) && noexcept(
749-
is_nothrow_move_constructible_v<_Ty> && is_nothrow_convertible_v<_Uty, _Ty>) /* strengthened */ {
750-
static_assert(
751-
is_move_constructible_v<_Ty>, "is_move_constructible_v<T> must be true. (N4950 [expected.object.obs]/20)");
752-
static_assert(
753-
is_convertible_v<_Uty, _Ty>, "is_convertible_v<U, T> must be true. (N4950 [expected.object.obs]/20)");
750+
template <class _Uty = remove_cv_t<_Ty>>
751+
_NODISCARD constexpr remove_cv_t<_Ty> value_or(_Uty&& _Other) && noexcept(
752+
is_nothrow_convertible_v<_Ty, remove_cv_t<_Ty>>
753+
&& is_nothrow_convertible_v<_Uty, remove_cv_t<_Ty>>) /* strengthened */ {
754+
static_assert(is_convertible_v<_Ty, remove_cv_t<_Ty>>,
755+
"is_convertible_v<T, remove_cv_t<T>> must be true. "
756+
"(N5001 [expected.object.obs]/20 as modified by LWG-3424)");
757+
static_assert(is_convertible_v<_Uty, remove_cv_t<_Ty>>,
758+
"is_convertible_v<U, remove_cv_t<T>> must be true. "
759+
"(N5001 [expected.object.obs]/20 as modified by LWG-3424)");
754760

755761
if (_Has_value) {
756762
return _STD move(_Value);
757763
} else {
758-
return static_cast<_Ty>(_STD forward<_Uty>(_Other));
764+
return static_cast<remove_cv_t<_Ty>>(_STD forward<_Uty>(_Other));
759765
}
760766
}
761767

stl/inc/optional

+14-11
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ public:
252252
negation<conjunction<is_same<remove_cv_t<_Ty>, bool>, _Is_specialization<_Remove_cvref_t<_Ty2>, optional>>>,
253253
is_constructible<_Ty, _Ty2>>>;
254254

255-
template <class _Ty2 = _Ty, enable_if_t<_AllowDirectConversion<_Ty2>::value, int> = 0>
255+
template <class _Ty2 = remove_cv_t<_Ty>, enable_if_t<_AllowDirectConversion<_Ty2>::value, int> = 0>
256256
constexpr explicit(!is_convertible_v<_Ty2, _Ty>) optional(_Ty2&& _Right)
257257
noexcept(is_nothrow_constructible_v<_Ty, _Ty2>) // strengthened
258258
: _Mybase(in_place, _STD forward<_Ty2>(_Right)) {}
@@ -288,10 +288,11 @@ public:
288288
return *this;
289289
}
290290

291-
template <class _Ty2 = _Ty, enable_if_t<conjunction_v<negation<is_same<optional, _Remove_cvref_t<_Ty2>>>,
292-
negation<conjunction<is_scalar<_Ty>, is_same<_Ty, decay_t<_Ty2>>>>,
293-
is_constructible<_Ty, _Ty2>, is_assignable<_Ty&, _Ty2>>,
294-
int> = 0>
291+
template <class _Ty2 = remove_cv_t<_Ty>,
292+
enable_if_t<conjunction_v<negation<is_same<optional, _Remove_cvref_t<_Ty2>>>,
293+
negation<conjunction<is_scalar<_Ty>, is_same<_Ty, decay_t<_Ty2>>>>, is_constructible<_Ty, _Ty2>,
294+
is_assignable<_Ty&, _Ty2>>,
295+
int> = 0>
295296
_CONSTEXPR20 optional& operator=(_Ty2&& _Right)
296297
noexcept(is_nothrow_assignable_v<_Ty&, _Ty2> && is_nothrow_constructible_v<_Ty, _Ty2>) /* strengthened */ {
297298
this->_Assign(_STD forward<_Ty2>(_Right));
@@ -425,27 +426,29 @@ public:
425426
return _STD move(this->_Value);
426427
}
427428

428-
template <class _Ty2>
429+
template <class _Ty2 = remove_cv_t<_Ty>>
429430
_NODISCARD constexpr remove_cv_t<_Ty> value_or(_Ty2&& _Right) const& {
430431
static_assert(is_convertible_v<const _Ty&, remove_cv_t<_Ty>>,
431432
"The const overload of optional<T>::value_or requires const T& to be convertible to remove_cv_t<T> "
432433
"(N4950 [optional.observe]/15 as modified by LWG-3424).");
433-
static_assert(is_convertible_v<_Ty2, _Ty>,
434-
"optional<T>::value_or(U) requires U to be convertible to T (N4950 [optional.observe]/15).");
434+
static_assert(is_convertible_v<_Ty2, remove_cv_t<_Ty>>,
435+
"optional<T>::value_or(U) requires U to be convertible to remove_cv_t<T> "
436+
"(N4950 [optional.observe]/15 as modified by LWG-3424).");
435437

436438
if (this->_Has_value) {
437439
return static_cast<const _Ty&>(this->_Value);
438440
}
439441

440442
return static_cast<remove_cv_t<_Ty>>(_STD forward<_Ty2>(_Right));
441443
}
442-
template <class _Ty2>
444+
template <class _Ty2 = remove_cv_t<_Ty>>
443445
_NODISCARD constexpr remove_cv_t<_Ty> value_or(_Ty2&& _Right) && {
444446
static_assert(is_convertible_v<_Ty, remove_cv_t<_Ty>>,
445447
"The rvalue overload of optional<T>::value_or requires T to be convertible to remove_cv_t<T> "
446448
"(N4950 [optional.observe]/17 as modified by LWG-3424).");
447-
static_assert(is_convertible_v<_Ty2, _Ty>,
448-
"optional<T>::value_or(U) requires U to be convertible to T (N4950 [optional.observe]/17).");
449+
static_assert(is_convertible_v<_Ty2, remove_cv_t<_Ty>>,
450+
"optional<T>::value_or(U) requires U to be convertible to remove_cv_t<T> "
451+
"(N4950 [optional.observe]/17 as modified by LWG-3424).");
449452

450453
if (this->_Has_value) {
451454
return static_cast<_Ty&&>(this->_Value);

tests/std/tests/P0220R1_optional/test.cpp

+146-8
Original file line numberDiff line numberDiff line change
@@ -8372,6 +8372,11 @@ int run_test()
83728372
#include <type_traits>
83738373
#include <utility>
83748374

8375+
#if _HAS_CXX20
8376+
#define CONSTEXPR20 constexpr
8377+
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
8378+
#define CONSTEXPR20 inline
8379+
#endif // ^^^ !_HAS_CXX20 ^^^
83758380

83768381
namespace msvc {
83778382
namespace size {
@@ -8422,12 +8427,6 @@ namespace msvc {
84228427
namespace lwg3836 {
84238428
static_assert(std::is_convertible_v<std::optional<int>, std::optional<bool>>);
84248429
static_assert(std::is_convertible_v<const std::optional<int>&, std::optional<bool>>);
8425-
8426-
#if _HAS_CXX20
8427-
#define CONSTEXPR20 constexpr
8428-
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
8429-
#define CONSTEXPR20 inline
8430-
#endif // ^^^ !_HAS_CXX20 ^^^
84318430
CONSTEXPR20 bool run_test() {
84328431
std::optional<int> oi = 0;
84338432
std::optional<bool> ob = oi;
@@ -8436,13 +8435,151 @@ namespace msvc {
84368435

84378436
return true;
84388437
}
8439-
#undef CONSTEXPR20
8440-
84418438
#if _HAS_CXX20
84428439
static_assert(run_test());
84438440
#endif // _HAS_CXX20
84448441
} // namespace lwg3836
84458442

8443+
namespace lwg3886 {
8444+
enum class Qualification {
8445+
None,
8446+
Const,
8447+
Volatile,
8448+
ConstVolatile,
8449+
};
8450+
8451+
template <class T>
8452+
constexpr Qualification CvQualOf =
8453+
std::is_const_v<std::remove_reference_t<T>>
8454+
? (std::is_volatile_v<std::remove_reference_t<T>> ? Qualification::ConstVolatile : Qualification::Const)
8455+
: (std::is_volatile_v<std::remove_reference_t<T>> ? Qualification::Volatile : Qualification::None);
8456+
8457+
struct QualDistinction {
8458+
QualDistinction() = default;
8459+
8460+
constexpr QualDistinction(QualDistinction&&) noexcept : qual_{Qualification::None} {}
8461+
constexpr QualDistinction(const QualDistinction&) noexcept : qual_{Qualification::Const} {}
8462+
template <class T,
8463+
std::enable_if_t<std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, QualDistinction>, int> =
8464+
0>
8465+
constexpr QualDistinction(T&&) noexcept : qual_{CvQualOf<T>} {}
8466+
8467+
constexpr QualDistinction& operator=(QualDistinction&&) noexcept {
8468+
qual_ = Qualification::None;
8469+
return *this;
8470+
}
8471+
constexpr QualDistinction& operator=(const QualDistinction&) noexcept {
8472+
qual_ = Qualification::Const;
8473+
return *this;
8474+
}
8475+
template <class T,
8476+
std::enable_if_t<std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, QualDistinction>, int> =
8477+
0>
8478+
constexpr QualDistinction& operator=(T&&) noexcept {
8479+
qual_ = CvQualOf<T>;
8480+
return *this;
8481+
}
8482+
template <class T,
8483+
std::enable_if_t<std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, QualDistinction>, int> =
8484+
0>
8485+
constexpr const QualDistinction& operator=(T&&) const noexcept {
8486+
qual_ = CvQualOf<T>;
8487+
return *this;
8488+
}
8489+
template <class T,
8490+
std::enable_if_t<std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, QualDistinction>, int> =
8491+
0>
8492+
volatile QualDistinction& operator=(T&&) volatile noexcept {
8493+
qual_ = CvQualOf<T>;
8494+
return *this;
8495+
}
8496+
template <class T,
8497+
std::enable_if_t<std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, QualDistinction>, int> =
8498+
0>
8499+
const volatile QualDistinction& operator=(T&&) const volatile noexcept {
8500+
qual_ = CvQualOf<T>;
8501+
return *this;
8502+
}
8503+
8504+
mutable Qualification qual_ = Qualification::None;
8505+
};
8506+
8507+
constexpr bool test_value_or() {
8508+
assert(std::optional<QualDistinction>{}.value_or({}).qual_ == Qualification::None);
8509+
assert(std::optional<const QualDistinction>{}.value_or({}).qual_ == Qualification::None);
8510+
{
8511+
std::optional<QualDistinction> opt;
8512+
assert(opt.value_or({}).qual_ == Qualification::None);
8513+
}
8514+
{
8515+
std::optional<const QualDistinction> opt;
8516+
assert(opt.value_or({}).qual_ == Qualification::None);
8517+
}
8518+
return true;
8519+
}
8520+
8521+
CONSTEXPR20 bool test_assignment() {
8522+
assert((std::optional<QualDistinction>{} = {QualDistinction{}}).value().qual_ == Qualification::None);
8523+
assert((std::optional<const QualDistinction>{} = {QualDistinction{}}).value().qual_ == Qualification::None);
8524+
{
8525+
std::optional<QualDistinction> opt{std::in_place};
8526+
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
8527+
opt.reset();
8528+
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
8529+
}
8530+
{
8531+
std::optional<const QualDistinction> opt{std::in_place};
8532+
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
8533+
opt.reset();
8534+
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
8535+
}
8536+
return true;
8537+
}
8538+
8539+
bool test_volatile() {
8540+
assert(std::optional<volatile QualDistinction>{}.value_or({}).qual_ == Qualification::None);
8541+
assert(std::optional<const volatile QualDistinction>{}.value_or({}).qual_ == Qualification::None);
8542+
{
8543+
std::optional<volatile QualDistinction> opt;
8544+
assert(opt.value_or({}).qual_ == Qualification::None);
8545+
}
8546+
{
8547+
std::optional<const volatile QualDistinction> opt;
8548+
assert(opt.value_or({}).qual_ == Qualification::None);
8549+
}
8550+
8551+
assert(
8552+
(std::optional<volatile QualDistinction>{} = {QualDistinction{}}).value().qual_ == Qualification::None);
8553+
assert((std::optional<const volatile QualDistinction>{} = {QualDistinction{}}).value().qual_
8554+
== Qualification::None);
8555+
{
8556+
std::optional<volatile QualDistinction> opt{std::in_place};
8557+
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
8558+
opt.reset();
8559+
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
8560+
}
8561+
{
8562+
std::optional<const volatile QualDistinction> opt{std::in_place};
8563+
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
8564+
opt.reset();
8565+
assert((opt = {QualDistinction{}}).value().qual_ == Qualification::None);
8566+
}
8567+
8568+
return true;
8569+
}
8570+
8571+
static_assert(test_value_or());
8572+
#if _HAS_CXX20
8573+
static_assert(test_assignment());
8574+
#endif // _HAS_CXX20
8575+
8576+
void run_test() {
8577+
test_value_or();
8578+
test_assignment();
8579+
test_volatile();
8580+
}
8581+
} // namespace lwg3886
8582+
84468583
namespace vso406124 {
84478584
// Defend against regression of VSO-406124
84488585
void run_test() {
@@ -8719,6 +8856,7 @@ int main() {
87198856
optional_includes_initializer_list::run_test();
87208857

87218858
msvc::lwg3836::run_test();
8859+
msvc::lwg3886::run_test();
87228860

87238861
msvc::vso406124::run_test();
87248862
msvc::vso508126::run_test();

0 commit comments

Comments
 (0)