Skip to content
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

<expected> Implement P0323R12 #2643

Merged
merged 29 commits into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8a7b0a1
<expected> Implement P0323: expected
miscco Mar 29, 2022
f1f5ff1
Address review comments
miscco Apr 9, 2022
af61334
Add some transition comments and paper number
miscco Apr 9, 2022
b6300ad
Use guard type rather than try catch blocks
miscco Apr 10, 2022
a6a7c24
Use consistent condition for feature test checks
miscco Apr 11, 2022
366377d
Merge branch 'main' into p0323-expected
miscco Apr 18, 2022
c4e5abd
Address review comments
miscco Apr 18, 2022
e305bfa
More mandates instead of constraints
miscco Apr 19, 2022
64d19d4
Add debugger visualisation as suggested by @SuperWig
miscco Apr 19, 2022
6e0094a
Address review comments
miscco Apr 29, 2022
dcb6544
use is_same_v instead of same_as
CaseyCarter May 1, 2022
d213de9
Merge branch 'main' into p0323-expected
miscco May 2, 2022
3da07d6
Merge branch 'main' into p0323-expected
miscco May 17, 2022
854159d
Drop __cpp_explicit_this_parameter.
StephanTLavavej May 19, 2022
e339374
Code review feedback, fixing several bugs.
StephanTLavavej May 20, 2022
1d8c6e5
Drop tests comparing `expected<void, ...>` to `expected<NonVoid, ...>`.
StephanTLavavej May 20, 2022
4a00d41
Add tests for `expected<void, E>` constructors.
StephanTLavavej May 20, 2022
3198d49
Formatting: Drop `//` within `clang-format off`.
StephanTLavavej May 20, 2022
2b2eff4
Fix formatting damage
miscco May 20, 2022
b5ac746
Do not call trivial destructors
miscco May 20, 2022
3dd480b
Test review feedback.
StephanTLavavej May 20, 2022
86e4f11
Test: Use typedefs to shorten long lines.
StephanTLavavej May 20, 2022
691621a
Rename No to Not.
StephanTLavavej May 20, 2022
4f5cc25
Sort Not/Yes patterns, so they're tested in "binary order" consistently.
StephanTLavavej May 20, 2022
bb06fa1
Add IsYes() to reduce verbosity.
StephanTLavavej May 20, 2022
45a39ec
`expected<void, E>::error()` is now mandated to be noexcept.
StephanTLavavej May 20, 2022
0796610
Work around VSO-1543660.
StephanTLavavej May 21, 2022
2fec1f2
Casey's review comments
CaseyCarter May 23, 2022
bb261ad
Guard `_Old_val.~_Second()`, enable header unit test internally.
StephanTLavavej May 23, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 84 additions & 37 deletions stl/inc/expected
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ struct _Check_unexpected_argument : true_type {
"E must not be a specialization of unexpected. (N4910 [expected.un.object.general]/1)");
};

// [expected.un.object]
// [expected.un.general]
template <class _Err>
class unexpected {
static_assert(_Check_unexpected_argument<_Err>::value);
Expand All @@ -48,6 +48,15 @@ class unexpected {

public:
// [expected.un.ctor]

// clang-format off
template <class _UError = _Err>
requires (!is_same_v<remove_cvref_t<_UError>, unexpected> && !is_same_v<remove_cvref_t<_UError>, in_place_t>
&& is_constructible_v<_Err, _UError>)
constexpr explicit unexpected(_UError&& _Unex) noexcept(is_nothrow_constructible_v<_Err, _UError>) // strengthened
: _Unexpected(_STD forward<_UError>(_Unex)) {}
// clang-format on

template <class... _Args>
requires is_constructible_v<_Err, _Args...>
constexpr explicit unexpected(in_place_t, _Args&&... _Vals) noexcept(
Expand All @@ -60,12 +69,6 @@ public:
constexpr explicit unexpected(in_place_t, initializer_list<_Uty> _Ilist, _Args&&... _Vals) noexcept(
is_nothrow_constructible_v<_Err, initializer_list<_Uty>&, _Args...>) // strengthened
: _Unexpected(_Ilist, _STD forward<_Args>(_Vals)...) {}

template <class _UError = _Err>
requires (!is_same_v<remove_cvref_t<_UError>, unexpected> && !is_same_v<remove_cvref_t<_UError>, in_place_t>
&& is_constructible_v<_Err, _UError>)
constexpr explicit unexpected(_UError&& _Unex) noexcept(is_nothrow_constructible_v<_Err, _UError>) // strengthened
: _Unexpected(_STD forward<_UError>(_Unex)) {}
// clang-format on

// [expected.un.obs]
Expand Down Expand Up @@ -226,22 +229,22 @@ public:
// clang-format on

template <class _Uty, class _UErr>
static constexpr bool _Is_not_convertible = !is_constructible_v<_Ty, expected<_Uty, _UErr>&> //
&& !is_constructible_v<_Ty, expected<_Uty, _UErr>> //
&& !is_constructible_v<_Ty, const expected<_Uty, _UErr>&> //
&& !is_constructible_v<_Ty, const expected<_Uty, _UErr>> //
&& !is_convertible_v<expected<_Uty, _UErr>&, _Ty> //
&& !is_convertible_v<expected<_Uty, _UErr>&&, _Ty> //
&& !is_convertible_v<const expected<_Uty, _UErr>&, _Ty> //
&& !is_convertible_v<const expected<_Uty, _UErr>&&, _Ty> //
&& !is_constructible_v<unexpected<_Err>, expected<_Uty, _UErr>&> //
&& !is_constructible_v<unexpected<_Err>, expected<_Uty, _UErr>> //
&& !is_constructible_v<unexpected<_Err>, const expected<_Uty, _UErr>&> //
&& !is_constructible_v<unexpected<_Err>, const expected<_Uty, _UErr>>;
static constexpr bool _Allow_unwrapping = !is_constructible_v<_Ty, expected<_Uty, _UErr>&> //
&& !is_constructible_v<_Ty, expected<_Uty, _UErr>> //
&& !is_constructible_v<_Ty, const expected<_Uty, _UErr>&> //
&& !is_constructible_v<_Ty, const expected<_Uty, _UErr>> //
&& !is_convertible_v<expected<_Uty, _UErr>&, _Ty> //
&& !is_convertible_v<expected<_Uty, _UErr>&&, _Ty> //
&& !is_convertible_v<const expected<_Uty, _UErr>&, _Ty> //
&& !is_convertible_v<const expected<_Uty, _UErr>&&, _Ty> //
&& !is_constructible_v<unexpected<_Err>, expected<_Uty, _UErr>&> //
&& !is_constructible_v<unexpected<_Err>, expected<_Uty, _UErr>> //
&& !is_constructible_v<unexpected<_Err>, const expected<_Uty, _UErr>&> //
&& !is_constructible_v<unexpected<_Err>, const expected<_Uty, _UErr>>;

template <class _Uty, class _UErr>
requires is_constructible_v<_Ty, const _Uty&> && is_constructible_v<_Err, const _UErr&> //
&& _Is_not_convertible<_Uty, _UErr>
&& _Allow_unwrapping<_Uty, _UErr>
constexpr explicit(!is_convertible_v<const _Uty&, _Ty> || !is_convertible_v<const _UErr&, _Err>)
expected(const expected<_Uty, _UErr>& _Other) noexcept(is_nothrow_constructible_v<_Ty, const _Uty&> //
&& is_nothrow_constructible_v<_Err, const _UErr&>) // strengthened
Expand All @@ -254,7 +257,7 @@ public:
}

template <class _Uty, class _UErr>
requires is_constructible_v<_Ty, _Uty> && is_constructible_v<_Err, _UErr> && _Is_not_convertible<_Uty, _UErr>
requires is_constructible_v<_Ty, _Uty> && is_constructible_v<_Err, _UErr> && _Allow_unwrapping<_Uty, _UErr>
constexpr explicit(!is_convertible_v<_Uty, _Ty> || !is_convertible_v<_UErr, _Err>)
expected(expected<_Uty, _UErr>&& _Other) noexcept(
is_nothrow_constructible_v<_Ty, _Uty>&& is_nothrow_constructible_v<_Err, _UErr>) // strengthened
Expand Down Expand Up @@ -333,7 +336,6 @@ public:
~expected() requires is_trivially_destructible_v<_Ty> && is_trivially_destructible_v<_Err> = default;
// clang-format on

CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved

// [expected.object.assign]
template <class _Uty>
requires is_nothrow_move_constructible_v<_Uty>
Expand All @@ -350,7 +352,7 @@ public:

template <class _First, class _Second, class... _Args>
static constexpr void _Reinit_expected(_First& _New_val, _Second& _Old_val, _Args&&... _Vals) noexcept(
is_nothrow_constructible_v<_First, _Args...> || is_nothrow_move_constructible_v<_First>) // strengthened
is_nothrow_constructible_v<_First, _Args...>) // strengthened
{
if constexpr (is_nothrow_constructible_v<_First, _Args...>) {
_Old_val.~_Second();
Expand Down Expand Up @@ -561,22 +563,40 @@ public:

// [expected.object.obs]
_NODISCARD constexpr const _Ty* operator->() const noexcept {
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Has_value, "expected stores an error, not a value");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _STD addressof(_Value);
}
_NODISCARD constexpr _Ty* operator->() noexcept {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Has_value, "expected stores an error, not a value");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _STD addressof(_Value);
}

_NODISCARD constexpr const _Ty& operator*() const& noexcept {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Has_value, "expected stores an error, not a value");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _Value;
}
_NODISCARD constexpr _Ty& operator*() & noexcept {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Has_value, "expected stores an error, not a value");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _Value;
}
_NODISCARD constexpr const _Ty&& operator*() const&& noexcept {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Has_value, "expected stores an error, not a value");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _STD move(_Value);
}
_NODISCARD constexpr _Ty&& operator*() && noexcept {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Has_value, "expected stores an error, not a value");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _STD move(_Value);
}

Expand Down Expand Up @@ -617,15 +637,27 @@ public:
}

_NODISCARD constexpr const _Err& error() const& noexcept {
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(!_Has_value, "expected stores a value, not an error");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _Unexpected;
}
_NODISCARD constexpr _Err& error() & noexcept {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(!_Has_value, "expected stores a value, not an error");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _Unexpected;
}
_NODISCARD constexpr const _Err&& error() const&& noexcept {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(!_Has_value, "expected stores a value, not an error");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _STD move(_Unexpected);
}
_NODISCARD constexpr _Err&& error() && noexcept {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(!_Has_value, "expected stores a value, not an error");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _STD move(_Unexpected);
}

Expand Down Expand Up @@ -658,7 +690,6 @@ public:
}
}

CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved

// [expected.object.eq]

// clang-format off
Expand All @@ -679,21 +710,21 @@ public:

template <class _Uty>
_NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const _Uty& _Right) noexcept(
noexcept(_Implicitly_convert_to<bool>(_Left._Value == _Right))) { // strengthened
noexcept(static_cast<bool>(_Left._Value == _Right))) { // strengthened
if (_Left._Has_value) {
return _Left._Value == _Right;
return static_cast<bool>(_Left._Value == _Right);
} else {
return false;
}
}

template <class _UErr>
_NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right) noexcept(
noexcept(_Implicitly_convert_to<bool>(_Left._Unexpected == _Right.error()))) { // strengthened
noexcept(static_cast<bool>(_Left._Unexpected == _Right.error()))) { // strengthened
if (_Left._Has_value) {
return false;
} else {
return _Left._Unexpected == _Right.error();
return static_cast<bool>(_Left._Unexpected == _Right.error());
}
}

Expand Down Expand Up @@ -760,13 +791,13 @@ public:
// clang-format on

template <class _Uty, class _UErr>
static constexpr bool _Is_not_convertible = !is_constructible_v<unexpected<_Err>, expected<_Uty, _UErr>&> //
&& !is_constructible_v<unexpected<_Err>, expected<_Uty, _UErr>> //
&& !is_constructible_v<unexpected<_Err>, const expected<_Uty, _UErr>&> //
&& !is_constructible_v<unexpected<_Err>, const expected<_Uty, _UErr>>;
static constexpr bool _Allow_unwrapping = !is_constructible_v<unexpected<_Err>, expected<_Uty, _UErr>&> //
&& !is_constructible_v<unexpected<_Err>, expected<_Uty, _UErr>> //
&& !is_constructible_v<unexpected<_Err>, const expected<_Uty, _UErr>&> //
&& !is_constructible_v<unexpected<_Err>, const expected<_Uty, _UErr>>;

template <class _Uty, class _UErr>
requires is_void_v<_Uty> && is_constructible_v<_Err, const _UErr&> && _Is_not_convertible<_Uty, _UErr>
requires is_void_v<_Uty> && is_constructible_v<_Err, const _UErr&> && _Allow_unwrapping<_Uty, _UErr>
constexpr explicit(!is_convertible_v<const _UErr&, _Err>) expected(const expected<_Uty, _UErr>& _Other) noexcept(
is_nothrow_constructible_v<_Err, const _UErr&>) // strengthened
: _Has_value(_Other._Has_value) {
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -776,7 +807,7 @@ public:
}

template <class _Uty, class _UErr>
requires is_void_v<_Uty> && is_constructible_v<_Err, _UErr> && _Is_not_convertible<_Uty, _UErr>
requires is_void_v<_Uty> && is_constructible_v<_Err, _UErr> && _Allow_unwrapping<_Uty, _UErr>
constexpr explicit(!is_convertible_v<_UErr, _Err>)
expected(expected<_Uty, _UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) // strengthened
: _Has_value(_Other._Has_value) {
Expand Down Expand Up @@ -960,7 +991,11 @@ public:
return _Has_value;
}

constexpr void operator*() const noexcept {}
constexpr void operator*() const noexcept {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Has_value, "expected stores an error, not a value");
#endif // _CONTAINER_DEBUG_LEVEL > 0
}

constexpr void value() const& {
if (!_Has_value) {
Expand All @@ -974,15 +1009,27 @@ public:
}

_NODISCARD constexpr const _Err& error() const& noexcept {
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(!_Has_value, "expected stores a value, not an error");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _Unexpected;
}
_NODISCARD constexpr _Err& error() & noexcept {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(!_Has_value, "expected stores a value, not an error");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _Unexpected;
}
_NODISCARD constexpr const _Err&& error() const&& noexcept {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(!_Has_value, "expected stores a value, not an error");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _STD move(_Unexpected);
}
_NODISCARD constexpr _Err&& error() && noexcept {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(!_Has_value, "expected stores a value, not an error");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return _STD move(_Unexpected);
}

Expand All @@ -991,7 +1038,7 @@ public:
template <class _Uty, class _UErr>
requires is_void_v<_Uty>
_NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const expected<_Uty, _UErr>& _Right) noexcept(
noexcept(_Implicitly_convert_to<bool>(_Left._Unexpected == _Right.error()))) { // strengthened
noexcept(static_cast<bool>(_Left._Unexpected == _Right.error()))) { // strengthened
// clang-format on
if (_Left._Has_value != _Right.has_value()) {
return false;
Expand All @@ -1002,7 +1049,7 @@ public:

template <class _UErr>
_NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right) noexcept(
noexcept(_Implicitly_convert_to<bool>(_Left._Unexpected == _Right.error()))) { // strengthened
noexcept(static_cast<bool>(_Left._Unexpected == _Right.error()))) { // strengthened
if (_Left._Has_value) {
return false;
} else {
Expand Down
27 changes: 27 additions & 0 deletions tests/std/tests/P0323R12_expected/test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#define _CONTAINER_DEBUG_LEVEL 1

#include <cassert>
#include <concepts>
#include <exception>
Expand Down Expand Up @@ -2038,6 +2040,29 @@ namespace test_expected {
}
} // namespace test_expected

void test_reinit_regression() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have preferred to have this as part of the assignment tests but meh...

// _Reinit_expected had a bug in its conditional noexcept that would terminate the program
// when switching from error state to value state when the value type is nothrow-movable
// but the conversion throws.

constexpr int magic = 1729;

struct throwing_int_conversion {
[[noreturn]] operator int() const {
throw magic;
}
};

expected<int, bool> e{unexpect, false};

try {
e = throwing_int_conversion{};
assert(false);
} catch (const int& i) {
assert(i == magic);
}
}

int main() {
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
test_unexpected::test_all();
static_assert(test_unexpected::test_all());
Expand All @@ -2051,4 +2076,6 @@ int main() {
static_assert(is_convertible_v<bad_expected_access<int>*, bad_expected_access<void>*>);
static_assert(is_convertible_v<bad_expected_access<void>*, exception*>);
static_assert(is_convertible_v<bad_expected_access<int>*, exception*>);

test_reinit_regression();
}