https://gcc.gnu.org/g:9f40ec15a30a248d72c496614d7bcbaa9be8ca49

commit r15-7885-g9f40ec15a30a248d72c496614d7bcbaa9be8ca49
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Wed Mar 5 21:08:21 2025 +0000

    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 <tkami...@redhat.com>

Diff:
---
 libstdc++-v3/include/std/expected                  |  1 +
 libstdc++-v3/testsuite/20_util/expected/lwg3843.cc | 69 ++++++++++++++++++++++
 2 files changed, 70 insertions(+)

diff --git a/libstdc++-v3/include/std/expected 
b/libstdc++-v3/include/std/expected
index 7de2aeffc70c..5dc1dfbe5b8a 100644
--- a/libstdc++-v3/include/std/expected
+++ b/libstdc++-v3/include/std/expected
@@ -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)));
diff --git a/libstdc++-v3/testsuite/20_util/expected/lwg3843.cc 
b/libstdc++-v3/testsuite/20_util/expected/lwg3843.cc
new file mode 100644
index 000000000000..2afb9cc680bf
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/expected/lwg3843.cc
@@ -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" }

Reply via email to