libstdc++: Add missing static_assert to std::expected<void,E>::value()&&
The r15-2326-gea435261ad58ea change missed a static_assert for is_move_constructible_v in expected<cv void, E>::value()&&. When exceptions are enabled, the program is ill-formed if the error type is not move constructible, because we can't construct the std::bad_expected_access. But prior to r15-7856-gd87c0d5443ba86, using -fno-exceptions meant that we never constructed an exception, so didn't need to copy/move the error value. So that we don't rely on the r15-7856-gd87c0d5443ba86 change to the _GLIBCXX_THROW_OR_ABORT macro to consistently enforce the Mandates: conditions whether exceptions are enabled or not, we should check the requirement explicitly. This adds the missing static_assert. It also adds a test that verifies the Mandates: conditions added by LWG 3843 and 3490 are enforced even with -fno-exceptions. libstdc++-v3/ChangeLog: * include/std/expected (expected<cv void,E>::value()&&): Add missing static_assert for LWG 3940. * testsuite/20_util/expected/lwg3843.cc: New test. Reviewed-by: Tomasz Kamiński <tkaminsk@redhat.com>
This commit is contained in:
parent
9798ba2c6b
commit
9f40ec15a3
2 changed files with 70 additions and 0 deletions
|
@ -1541,6 +1541,7 @@ namespace __expected
|
|||
value() &&
|
||||
{
|
||||
static_assert( is_copy_constructible_v<_Er> );
|
||||
static_assert( is_move_constructible_v<_Er> );
|
||||
if (_M_has_value) [[likely]]
|
||||
return;
|
||||
_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(std::move(_M_unex)));
|
||||
|
|
69
libstdc++-v3/testsuite/20_util/expected/lwg3843.cc
Normal file
69
libstdc++-v3/testsuite/20_util/expected/lwg3843.cc
Normal file
|
@ -0,0 +1,69 @@
|
|||
// { dg-do compile { target c++23 } }
|
||||
// { dg-options "-fno-exceptions" }
|
||||
|
||||
#include <expected>
|
||||
|
||||
// 3843. std::expected<T,E>::value() & assumes E is copy constructible
|
||||
void
|
||||
test_lwg3843()
|
||||
{
|
||||
struct E1 {
|
||||
E1(int) { }
|
||||
E1(E1&) { }
|
||||
E1(const E1&) = delete;
|
||||
};
|
||||
std::expected<int, E1> v1;
|
||||
v1.value(); // { dg-error "here" }
|
||||
const auto& v1c = v1;
|
||||
v1c.value(); // { dg-error "here" }
|
||||
|
||||
struct E2 {
|
||||
E2(int) { }
|
||||
E2(const E2&) { }
|
||||
E2(E2&&) = delete;
|
||||
};
|
||||
std::expected<int, E2> v2;
|
||||
v2.value();
|
||||
std::move(v2).value(); // { dg-error "here" }
|
||||
const auto& v2c = v2;
|
||||
v2c.value();
|
||||
std::move(v2c).value();
|
||||
|
||||
struct E3 {
|
||||
E3(int) { }
|
||||
E3(const E3&) { }
|
||||
E3(E3&&) { }
|
||||
E3(const E3&&) = delete;
|
||||
};
|
||||
std::expected<int, E3> v3;
|
||||
v3.value();
|
||||
std::move(v3).value();
|
||||
const auto& v3c = v3;
|
||||
v3c.value();
|
||||
std::move(v3c).value(); // { dg-error "here" }
|
||||
}
|
||||
|
||||
// 3940. std::expected<void, E>::value() also needs E to be copy constructible
|
||||
void
|
||||
test_lwg3940()
|
||||
{
|
||||
struct E1 {
|
||||
E1(int) { }
|
||||
E1(E1&) { }
|
||||
E1(const E1&) = delete;
|
||||
};
|
||||
std::expected<void, E1> v1;
|
||||
v1.value(); // { dg-error "here" }
|
||||
|
||||
struct E2 {
|
||||
E2(int) { }
|
||||
E2(const E2&) { }
|
||||
E2(E2&&) = delete;
|
||||
};
|
||||
std::expected<void, E2> v2;
|
||||
std::move(v2).value(); // { dg-error "here" }
|
||||
}
|
||||
|
||||
// { dg-error "static assertion failed" "" { target *-*-* } 0 }
|
||||
// { dg-prune-output "use of deleted function" }
|
||||
// { dg-prune-output "control reaches end of non-void function" }
|
Loading…
Add table
Reference in a new issue