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

Workaround for the interaction of constraints in expected and explicit template instantiation in permissive modes #4658

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
17 changes: 15 additions & 2 deletions stl/inc/expected
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ public:
}

friend constexpr void swap(unexpected& _Left, unexpected& _Right) noexcept(is_nothrow_swappable_v<_Err>)
#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-10652420
requires is_swappable_v<_Err>
#else // ^^^ no workaround / workaround vvv
requires is_swappable<_Err>::value
#endif // ^^ workaround ^^^
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
{
_Left.swap(_Right);
}
Expand Down Expand Up @@ -601,7 +605,11 @@ public:
friend constexpr void swap(expected& _Lhs, expected& _Rhs) noexcept(
is_nothrow_move_constructible_v<_Ty> && is_nothrow_swappable_v<_Ty> && is_nothrow_move_constructible_v<_Err>
&& is_nothrow_swappable_v<_Err>)
requires is_swappable_v<_Ty> && is_swappable_v<_Err> //
#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-10652420
requires is_swappable_v<_Ty> && is_swappable_v<_Err>
#else // ^^^ no workaround / workaround vvv
requires is_swappable<_Ty>::value && is_swappable<_Err>::value
#endif // ^^^ workaround ^^^
&& is_move_constructible_v<_Ty> && is_move_constructible_v<_Err>
&& (is_nothrow_move_constructible_v<_Ty> || is_nothrow_move_constructible_v<_Err>)
{
Expand Down Expand Up @@ -1429,7 +1437,12 @@ public:

friend constexpr void swap(expected& _Left, expected& _Right) noexcept(
is_nothrow_move_constructible_v<_Err> && is_nothrow_swappable_v<_Err>)
requires is_swappable_v<_Err> && is_move_constructible_v<_Err>
#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-10652420
requires is_swappable_v<_Err>
#else // ^^^ no workaround / workaround vvv
requires is_swappable<_Err>::value
#endif // ^^^ workaround ^^^
&& is_move_constructible_v<_Err>
{
_Left.swap(_Right);
}
Expand Down
10 changes: 10 additions & 0 deletions stl/inc/iterator
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,11 @@ _NODISCARD bool operator!=(
}
#endif // !_HAS_CXX20

#if _HAS_CXX20 && !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-10652420, avoid checking constraints
template <class _Elem, class _Traits>
constexpr bool _Iterator_is_contiguous<istreambuf_iterator<_Elem, _Traits>> = false;
#endif // _HAS_CXX20 && !defined(__clang__) && !defined(__EDG__)
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved

_EXPORT_STD template <class _Elem, class _Traits>
class ostreambuf_iterator {
public:
Expand Down Expand Up @@ -548,6 +553,11 @@ private:
streambuf_type* _Strbuf;
};

#if _HAS_CXX20 && !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-10652420, avoid checking constraints
template <class _Elem, class _Traits>
constexpr bool _Iterator_is_contiguous<ostreambuf_iterator<_Elem, _Traits>> = false;
#endif // _HAS_CXX20 && !defined(__clang__) && !defined(__EDG__)

#if _HAS_CXX20
enum class _Variantish_state : unsigned char { _Nothing, _Holds_first, _Holds_second };

Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ tests\GH_004388_unordered_meow_operator_equal
tests\GH_004477_mdspan_warning_5246
tests\GH_004618_mixed_operator_usage_keeps_statistical_properties
tests\GH_004618_normal_distribution_avoids_resets
tests\GH_004657_expected_constraints_permissive
tests\LWG2381_num_get_floating_point
tests\LWG2597_complex_branch_cut
tests\LWG3018_shared_ptr_function
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_latest_matrix.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <expected>
#include <locale>
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
#include <string>
#include <utility>

using namespace std;

expected<int, string> test_expected_int_constraints() {
return 42;
}

expected<pair<int, int>, string> test_expected_pair_constraints() {
return pair<int, int>{42, 1729};
}

expected<void, string> test_expected_void_constraints() {
return {};
}

unexpected<string> test_unexpected_constraints() {
return unexpected<string>{string{}};
}
2 changes: 1 addition & 1 deletion tests/std/tests/P0323R12_expected/env.lst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\strict_latest_matrix.lst
RUNALL_INCLUDE ..\usual_latest_matrix.lst
48 changes: 25 additions & 23 deletions tests/std/tests/P0323R12_expected/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <utility>
#include <vector>

#include <is_permissive.hpp> // noexcept operator possibly gives wrong values in permissive modes

using namespace std;

enum class IsDefaultConstructible : bool { Not, Yes };
Expand Down Expand Up @@ -82,31 +84,31 @@ namespace test_unexpected {
// [expected.un.ctor]
const int& input = 1;
Unexpect in_place_lvalue_constructed{in_place, input};
static_assert(noexcept(Unexpect{in_place, input}) == copy_construction_is_noexcept);
static_assert(noexcept(Unexpect{in_place, input}) == copy_construction_is_noexcept || is_permissive);
assert(in_place_lvalue_constructed == Unexpect{test_error{1}});

Unexpect in_place_rvalue_constructed{in_place, 42};
static_assert(noexcept(Unexpect{in_place, 42}) == move_construction_is_noexcept);
static_assert(noexcept(Unexpect{in_place, 42}) == move_construction_is_noexcept || is_permissive);
assert(in_place_rvalue_constructed == Unexpect{test_error{42}});

Unexpect in_place_ilist_lvalue_constructed{in_place, {2}, input};
static_assert(noexcept(Unexpect{in_place, {2}, input}) == copy_construction_is_noexcept);
static_assert(noexcept(Unexpect{in_place, {2}, input}) == copy_construction_is_noexcept || is_permissive);
assert(in_place_ilist_lvalue_constructed == Unexpect{test_error{1}});

Unexpect in_place_ilist_rvalue_constructed{in_place, {2}, 1337};
static_assert(noexcept(Unexpect{in_place, {2}, 1337}) == move_construction_is_noexcept);
static_assert(noexcept(Unexpect{in_place, {2}, 1337}) == move_construction_is_noexcept || is_permissive);
assert(in_place_ilist_rvalue_constructed == Unexpect{test_error{1337}});

Unexpect base_error_constructed{test_error{3}};
static_assert(noexcept(Unexpect{test_error{3}}) == move_construction_is_noexcept);
static_assert(noexcept(Unexpect{test_error{3}}) == move_construction_is_noexcept || is_permissive);
assert(base_error_constructed.error()._val == 3);

Unexpect conversion_error_constructed{convertible{4}};
static_assert(noexcept(Unexpect{convertible{4}}) == move_construction_is_noexcept);
static_assert(noexcept(Unexpect{convertible{4}}) == move_construction_is_noexcept || is_permissive);
assert(conversion_error_constructed.error()._val == 4);

Unexpect brace_error_constructed{{5}};
static_assert(noexcept(Unexpect{{5}}) == move_construction_is_noexcept);
static_assert(noexcept(Unexpect{{5}}) == move_construction_is_noexcept || is_permissive);
assert(brace_error_constructed.error()._val == 5);

// [expected.un.eq]
Expand Down Expand Up @@ -500,12 +502,12 @@ namespace test_expected {
const Expected copy_constructed_value{const_input_value};
assert(copy_constructed_value);
assert(copy_constructed_value.value() == 3);
static_assert(noexcept(Expected{const_input_value}) == should_be_noexcept);
static_assert(noexcept(Expected{const_input_value}) == should_be_noexcept || is_permissive);

const Expected move_constructed_value{Input{}};
assert(move_constructed_value);
assert(move_constructed_value.value() == 42);
static_assert(noexcept(Expected{Input{}}) == should_be_noexcept);
static_assert(noexcept(Expected{Input{}}) == should_be_noexcept || is_permissive);

const Expected brace_constructed_value{{}};
assert(brace_constructed_value);
Expand All @@ -528,7 +530,7 @@ namespace test_expected {
const Expected move_constructed_value{Input{in_place}};
assert(move_constructed_value);
assert(move_constructed_value.value() == 42);
static_assert(noexcept(Expected{Input{in_place}}) == should_be_noexcept);
static_assert(noexcept(Expected{Input{in_place}}) == should_be_noexcept || is_permissive);

const Input const_input_error{unexpect};
const Expected copy_constructed_error{const_input_error};
Expand All @@ -539,7 +541,7 @@ namespace test_expected {
const Expected move_constructed_error{Input{unexpect}};
assert(!move_constructed_error);
assert(move_constructed_error.error() == 42);
static_assert(noexcept(Expected{Input{unexpect}}) == should_be_noexcept);
static_assert(noexcept(Expected{Input{unexpect}}) == should_be_noexcept || is_permissive);
}

{ // converting from unexpected
Expand All @@ -550,12 +552,12 @@ namespace test_expected {
const Expected copy_constructed{const_input};
assert(!copy_constructed);
assert(copy_constructed.error() == 3);
static_assert(noexcept(Expected{const_input}) == should_be_noexcept);
static_assert(noexcept(Expected{const_input}) == should_be_noexcept || is_permissive);

const Expected move_constructed{Input{in_place}};
assert(!move_constructed);
assert(move_constructed.error() == 42);
static_assert(noexcept(Expected{Input{in_place}}) == should_be_noexcept);
static_assert(noexcept(Expected{Input{in_place}}) == should_be_noexcept || is_permissive);
}

{ // in place payload
Expand All @@ -568,12 +570,12 @@ namespace test_expected {
const Expected value_constructed{in_place, convertible{}};
assert(value_constructed);
assert(value_constructed.value() == 42);
static_assert(noexcept(Expected{in_place, convertible{}}) == should_be_noexcept);
static_assert(noexcept(Expected{in_place, convertible{}}) == should_be_noexcept || is_permissive);

const Expected ilist_value_constructed{in_place, {1}, convertible{}};
assert(ilist_value_constructed);
assert(ilist_value_constructed.value() == 1337);
static_assert(noexcept(Expected{in_place, {1}, convertible{}}) == should_be_noexcept);
static_assert(noexcept(Expected{in_place, {1}, convertible{}}) == should_be_noexcept || is_permissive);
}

{ // in place error
Expand All @@ -586,12 +588,12 @@ namespace test_expected {
const Expected value_constructed{unexpect, convertible{}};
assert(!value_constructed);
assert(value_constructed.error() == 42);
static_assert(noexcept(Expected{unexpect, convertible{}}) == should_be_noexcept);
static_assert(noexcept(Expected{unexpect, convertible{}}) == should_be_noexcept || is_permissive);

const Expected ilist_value_constructed{unexpect, {1}, convertible{}};
assert(!ilist_value_constructed);
assert(ilist_value_constructed.error() == 1337);
static_assert(noexcept(Expected{unexpect, {1}, convertible{}}) == should_be_noexcept);
static_assert(noexcept(Expected{unexpect, {1}, convertible{}}) == should_be_noexcept || is_permissive);
}

{ // expected<void, E>: converting from different expected
Expand All @@ -609,7 +611,7 @@ namespace test_expected {
const Expected move_constructed_value{Input{in_place}};
assert(move_constructed_value);
move_constructed_value.value();
static_assert(noexcept(Expected{Input{in_place}}) == should_be_noexcept);
static_assert(noexcept(Expected{Input{in_place}}) == should_be_noexcept || is_permissive);

const Input const_input_error{unexpect};
const Expected copy_constructed_error{const_input_error};
Expand All @@ -620,7 +622,7 @@ namespace test_expected {
const Expected move_constructed_error{Input{unexpect}};
assert(!move_constructed_error);
assert(move_constructed_error.error() == 42);
static_assert(noexcept(Expected{Input{unexpect}}) == should_be_noexcept);
static_assert(noexcept(Expected{Input{unexpect}}) == should_be_noexcept || is_permissive);
}

{ // expected<void, E>: converting from unexpected
Expand All @@ -631,12 +633,12 @@ namespace test_expected {
const Expected copy_constructed{const_input};
assert(!copy_constructed);
assert(copy_constructed.error() == 3);
static_assert(noexcept(Expected{const_input}) == should_be_noexcept);
static_assert(noexcept(Expected{const_input}) == should_be_noexcept || is_permissive);

const Expected move_constructed{Input{in_place}};
assert(!move_constructed);
assert(move_constructed.error() == 42);
static_assert(noexcept(Expected{Input{in_place}}) == should_be_noexcept);
static_assert(noexcept(Expected{Input{in_place}}) == should_be_noexcept || is_permissive);
}

{ // expected<void, E>: in place payload
Expand All @@ -657,12 +659,12 @@ namespace test_expected {
const Expected value_constructed{unexpect, convertible{}};
assert(!value_constructed);
assert(value_constructed.error() == 42);
static_assert(noexcept(Expected{unexpect, convertible{}}) == should_be_noexcept);
static_assert(noexcept(Expected{unexpect, convertible{}}) == should_be_noexcept || is_permissive);

const Expected ilist_value_constructed{unexpect, {1}, convertible{}};
assert(!ilist_value_constructed);
assert(ilist_value_constructed.error() == 1337);
static_assert(noexcept(Expected{unexpect, {1}, convertible{}}) == should_be_noexcept);
static_assert(noexcept(Expected{unexpect, {1}, convertible{}}) == should_be_noexcept || is_permissive);
}
}

Expand Down