https://gcc.gnu.org/g:dca6a9a940e46d0c2d115a4702d648529a42efa9

commit r15-2307-gdca6a9a940e46d0c2d115a4702d648529a42efa9
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Wed Jul 24 18:08:03 2024 +0100

    libstdc++: Implement LWG 3836 for std::expected bool conversions
    
    libstdc++-v3/ChangeLog:
    
            * include/std/expected (expected): Constrain constructors to
            prevent problematic bool conversions, as per LWG 3836.
            * testsuite/20_util/expected/lwg3836.cc: New test.

Diff:
---
 libstdc++-v3/include/std/expected                  | 59 ++++++++++++++++------
 libstdc++-v3/testsuite/20_util/expected/lwg3836.cc | 34 +++++++++++++
 2 files changed, 77 insertions(+), 16 deletions(-)

diff --git a/libstdc++-v3/include/std/expected 
b/libstdc++-v3/include/std/expected
index 86026c3947a1..2594cfe131c0 100644
--- a/libstdc++-v3/include/std/expected
+++ b/libstdc++-v3/include/std/expected
@@ -314,6 +314,17 @@ namespace __expected
          __guard.release();
        }
     }
+
+  // _GLIBCXX_RESOLVE_LIB_DEFECTS
+  // 3836. std::expected<bool, E1> conversion constructor
+  // expected(const expected<U, G>&) should take precedence over
+  // expected(U&&) with operator bool
+
+  // If T is cv bool, remove_cvref_t<U> is not a specialization of expected.
+  template<typename _Tp, typename _Up>
+    concept __not_constructing_bool_from_expected
+      = ! is_same_v<remove_cv_t<_Tp>, bool>
+         || ! __is_expected<remove_cvref_t<_Up>>;
 }
 /// @endcond
 
@@ -327,26 +338,41 @@ namespace __expected
       static_assert( ! __expected::__is_unexpected<remove_cv_t<_Tp>> );
       static_assert( __expected::__can_be_unexpected<_Er> );
 
-      template<typename _Up, typename _Err, typename _Unex = unexpected<_Er>>
+      // If T is not cv bool, converts-from-any-cvref<T, expected<U, G>> and
+      // is_constructible<unexpected<E>, cv expected<U, G> ref-qual> are false.
+      template<typename _Up, typename _Gr, typename _Unex = unexpected<_Er>,
+              typename = remove_cv_t<_Tp>>
        static constexpr bool __cons_from_expected
-         = __or_v<is_constructible<_Tp, expected<_Up, _Err>&>,
-                  is_constructible<_Tp, expected<_Up, _Err>>,
-                  is_constructible<_Tp, const expected<_Up, _Err>&>,
-                  is_constructible<_Tp, const expected<_Up, _Err>>,
-                  is_convertible<expected<_Up, _Err>&, _Tp>,
-                  is_convertible<expected<_Up, _Err>, _Tp>,
-                  is_convertible<const expected<_Up, _Err>&, _Tp>,
-                  is_convertible<const expected<_Up, _Err>, _Tp>,
-                  is_constructible<_Unex, expected<_Up, _Err>&>,
-                  is_constructible<_Unex, expected<_Up, _Err>>,
-                  is_constructible<_Unex, const expected<_Up, _Err>&>,
-                  is_constructible<_Unex, const expected<_Up, _Err>>
+         = __or_v<is_constructible<_Tp, expected<_Up, _Gr>&>,
+                  is_constructible<_Tp, expected<_Up, _Gr>>,
+                  is_constructible<_Tp, const expected<_Up, _Gr>&>,
+                  is_constructible<_Tp, const expected<_Up, _Gr>>,
+                  is_convertible<expected<_Up, _Gr>&, _Tp>,
+                  is_convertible<expected<_Up, _Gr>, _Tp>,
+                  is_convertible<const expected<_Up, _Gr>&, _Tp>,
+                  is_convertible<const expected<_Up, _Gr>, _Tp>,
+                  is_constructible<_Unex, expected<_Up, _Gr>&>,
+                  is_constructible<_Unex, expected<_Up, _Gr>>,
+                  is_constructible<_Unex, const expected<_Up, _Gr>&>,
+                  is_constructible<_Unex, const expected<_Up, _Gr>>
                  >;
 
-      template<typename _Up, typename _Err>
+      // _GLIBCXX_RESOLVE_LIB_DEFECTS
+      // If t is cv bool, we know it can be constructed from expected<U, G>,
+      // but we don't want to cause the expected(U&&) constructor to be used,
+      // so we only check the is_constructible<unexpected<E>, ...> cases.
+      template<typename _Up, typename _Gr, typename _Unex>
+       static constexpr bool __cons_from_expected<_Up, _Gr, _Unex, bool>
+         = __or_v<is_constructible<_Unex, expected<_Up, _Gr>&>,
+                  is_constructible<_Unex, expected<_Up, _Gr>>,
+                  is_constructible<_Unex, const expected<_Up, _Gr>&>,
+                  is_constructible<_Unex, const expected<_Up, _Gr>>
+                 >;
+
+      template<typename _Up, typename _Gr>
        constexpr static bool __explicit_conv
          = __or_v<__not_<is_convertible<_Up, _Tp>>,
-                  __not_<is_convertible<_Err, _Er>>
+                  __not_<is_convertible<_Gr, _Er>>
                  >;
 
       template<typename _Up>
@@ -445,8 +471,9 @@ namespace __expected
       template<typename _Up = _Tp>
        requires (!is_same_v<remove_cvref_t<_Up>, expected>)
          && (!is_same_v<remove_cvref_t<_Up>, in_place_t>)
-         && (!__expected::__is_unexpected<remove_cvref_t<_Up>>)
          && is_constructible_v<_Tp, _Up>
+         && (!__expected::__is_unexpected<remove_cvref_t<_Up>>)
+         && __expected::__not_constructing_bool_from_expected<_Tp, _Up>
        constexpr explicit(!is_convertible_v<_Up, _Tp>)
        expected(_Up&& __v)
        noexcept(is_nothrow_constructible_v<_Tp, _Up>)
diff --git a/libstdc++-v3/testsuite/20_util/expected/lwg3836.cc 
b/libstdc++-v3/testsuite/20_util/expected/lwg3836.cc
new file mode 100644
index 000000000000..cd029c449632
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/expected/lwg3836.cc
@@ -0,0 +1,34 @@
+// { dg-do run { target c++23 } }
+
+#include <expected>
+#include <testsuite_hooks.h>
+
+constexpr void
+test_convert_contained_value_to_bool()
+{
+  struct BaseError { };
+  struct DerivedError : BaseError { };
+
+  std::expected<bool, DerivedError> e = false;
+
+  // Should use expected(const expected<U, G>&) ctor, not expected(U&&):
+  std::expected<bool, BaseError> e2 = e;
+
+  // Contained value should be e.value() not static_cast<bool>(e):
+  VERIFY( e2.value() == false );
+
+  std::expected<bool, DerivedError> e3(std::unexpect);
+  std::expected<const bool, BaseError> e4 = e3;
+  // Should have error, not static_cast<bool>(e3):
+  VERIFY( ! e4.has_value() );
+}
+
+int main()
+{
+  test_convert_contained_value_to_bool();
+
+  static_assert([] {
+    test_convert_contained_value_to_bool();
+    return true;
+  }());
+}

Reply via email to