[libcxx-commits] [libcxx] [libc++] Implement Resolution of LWG 3886 (PR #155356)

William Tran-Viet via libcxx-commits libcxx-commits at lists.llvm.org
Thu Sep 4 11:12:36 PDT 2025


https://github.com/smallp-o-p updated https://github.com/llvm/llvm-project/pull/155356

>From bcdfe307bd7f958c57994c842b3932335997577d Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Mon, 25 Aug 2025 23:56:27 -0400
Subject: [PATCH 1/7] Implement resolution of LWG 3886

---
 libcxx/include/__expected/expected.h |  8 ++++----
 libcxx/include/optional              | 17 +++++++++--------
 2 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 38a34121040f6..8b3eeebd38ae7 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -555,7 +555,7 @@ class expected : private __expected_base<_Tp, _Err> {
           is_nothrow_constructible_v<_Tp, _Up> && is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
       : __base(__other.__has_val(), std::move(__other.__union())) {}
 
-  template <class _Up = _Tp>
+  template <class _Up = remove_cv_t<_Tp>>
     requires(!is_same_v<remove_cvref_t<_Up>, in_place_t> && !is_same_v<expected, remove_cvref_t<_Up>> &&
              !is_same_v<remove_cvref_t<_Up>, unexpect_t> && is_constructible_v<_Tp, _Up> &&
              !__is_std_unexpected<remove_cvref_t<_Up>>::value &&
@@ -669,7 +669,7 @@ class expected : private __expected_base<_Tp, _Err> {
     return *this;
   }
 
-  template <class _Up = _Tp>
+  template <class _Up = remove_cv_t<_Tp>>
   _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(_Up&& __v)
     requires(!is_same_v<expected, remove_cvref_t<_Up>> && !__is_std_unexpected<remove_cvref_t<_Up>>::value &&
              is_constructible_v<_Tp, _Up> && is_assignable_v<_Tp&, _Up> &&
@@ -887,14 +887,14 @@ class expected : private __expected_base<_Tp, _Err> {
     return std::move(this->__unex());
   }
 
-  template <class _Up>
+  template <class _Up = remove_cv_t<_Tp>>
   _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& {
     static_assert(is_copy_constructible_v<_Tp>, "value_type has to be copy constructible");
     static_assert(is_convertible_v<_Up, _Tp>, "argument has to be convertible to value_type");
     return this->__has_val() ? this->__val() : static_cast<_Tp>(std::forward<_Up>(__v));
   }
 
-  template <class _Up>
+  template <class _Up = remove_cv_t<_Tp>>
   _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
     static_assert(is_move_constructible_v<_Tp>, "value_type has to be move constructible");
     static_assert(is_convertible_v<_Up, _Tp>, "argument has to be convertible to value_type");
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 39fcaa2c2ec18..ef1bfd3ec44c0 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -119,7 +119,7 @@ namespace std {
       constexpr explicit optional(in_place_t, Args &&...);
     template<class U, class... Args>
       constexpr explicit optional(in_place_t, initializer_list<U>, Args &&...);
-    template<class U = T>
+    template<class U = remove_cv_t<T>>
       constexpr explicit(see-below) optional(U &&);
     template<class U>
       explicit(see-below) optional(const optional<U> &);                          // constexpr in C++20
@@ -133,7 +133,7 @@ namespace std {
     optional &operator=(nullopt_t) noexcept;                                      // constexpr in C++20
     constexpr optional &operator=(const optional &);
     constexpr optional &operator=(optional &&) noexcept(see below);
-    template<class U = T> optional &operator=(U &&);                              // constexpr in C++20
+    template<class U = remove_cv_t<T>> optional &operator=(U &&);                              // constexpr in C++20
     template<class U> optional &operator=(const optional<U> &);                   // constexpr in C++20
     template<class U> optional &operator=(optional<U> &&);                        // constexpr in C++20
     template<class... Args> T& emplace(Args &&...);                               // constexpr in C++20
@@ -161,8 +161,8 @@ namespace std {
     constexpr T &value() &;
     constexpr T &&value() &&;
     constexpr const T &&value() const &&;
-    template<class U> constexpr T value_or(U &&) const &;
-    template<class U> constexpr T value_or(U &&) &&;
+    template<class U = remove_cv_t<T>> constexpr T value_or(U &&) const &;
+    template<class U = remove_cv_t<T>> constexpr T value_or(U &&) &&;
 
     // [optional.monadic], monadic operations
     template<class F> constexpr auto and_then(F&& f) &;         // since C++23
@@ -730,7 +730,8 @@ public:
             enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) : __base(in_place, std::forward<_Up>(__v)) {}
 
-  template <class _Up, enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0>
+  template <class _Up                                                                        = remove_cv_t<_Tp>,
+            enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) : __base(in_place, std::forward<_Up>(__v)) {}
 
   // LWG2756: conditionally explicit conversion from const optional<_Up>&
@@ -771,7 +772,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(optional&&)      = default;
 
   // LWG2756
-  template <class _Up        = value_type,
+  template <class _Up        = remove_cv_t<value_type>,
             enable_if_t<_And<_IsNotSame<__remove_cvref_t<_Up>, optional>,
                              _Or<_IsNotSame<__remove_cvref_t<_Up>, value_type>, _Not<is_scalar<value_type>>>,
                              is_constructible<value_type, _Up>,
@@ -919,14 +920,14 @@ public:
     return std::move(this->__get());
   }
 
-  template <class _Up>
+  template <class _Up = remove_cv_t<_Tp>>
   _LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) const& {
     static_assert(is_copy_constructible_v<value_type>, "optional<T>::value_or: T must be copy constructible");
     static_assert(is_convertible_v<_Up, value_type>, "optional<T>::value_or: U must be convertible to T");
     return this->has_value() ? this->__get() : static_cast<value_type>(std::forward<_Up>(__v));
   }
 
-  template <class _Up>
+  template <class _Up = remove_cv_t<_Tp>>
   _LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) && {
     static_assert(is_move_constructible_v<value_type>, "optional<T>::value_or: T must be move constructible");
     static_assert(is_convertible_v<_Up, value_type>, "optional<T>::value_or: U must be convertible to T");

>From 288a9872ec053042cac942d0f9060a2861e4becb Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 26 Aug 2025 22:45:09 -0400
Subject: [PATCH 2/7] Add some extra tests

---
 .../expected.expected/ctor/ctor.u.pass.cpp    | 28 +++++++++++++++++++
 .../optional.object.ctor/U.pass.cpp           | 12 +++++++-
 .../optional.object.observe/value_or.pass.cpp |  5 ++++
 .../value_or_const.pass.cpp                   |  4 +++
 4 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp
index 13c0da27bc533..4c16176b6e394 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp
@@ -80,6 +80,18 @@ struct CopyOnly {
   friend constexpr bool operator==(const CopyOnly& mi, int ii) { return mi.i == ii; }
 };
 
+struct MoveOnly2{
+  int j;
+  bool used_move1 = false;
+  bool used_move2 = false;
+
+  constexpr explicit MoveOnly2(int j) : j(j) {}
+  constexpr MoveOnly2(const MoveOnly2&) = delete;
+  constexpr MoveOnly2(MoveOnly2&& m) : j(m.j), used_move1(true) {}
+  constexpr MoveOnly2(const MoveOnly2&& m) : j(m.j), used_move2(true) {}
+
+};
+
 struct BaseError {};
 struct DerivedError : BaseError {};
 
@@ -164,6 +176,22 @@ constexpr bool test() {
     assert(e2.has_value());
     assert(!e2.value()); // yes, e2 holds "false" since LWG3836
   }
+
+  // Check move constructor selection
+  {
+    MoveOnly2 t{1};
+    std::expected<MoveOnly2, BaseError> e1(std::move(t));
+    assert(e1.has_value());
+    assert(e1.value().used_move1 == true);
+    assert(e1.value().j == 1);
+  }
+  {
+    const MoveOnly2 t2{2};
+    std::expected<MoveOnly2, BaseError> e1(std::move(t2));
+    assert(e1.has_value());
+    assert(e1.value().used_move2 == true);
+    assert(e1.value().j == 2);
+  }
   return true;
 }
 
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp
index a5ee602ab7bce..edf9fa08d307a 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp
@@ -59,7 +59,8 @@ constexpr bool explicit_conversion(Input&& in, const Expect& v)
     static_assert(!std::is_constructible<O, void*>::value, "");
     static_assert(!std::is_constructible<O, Input, int>::value, "");
     optional<To> opt(std::forward<Input>(in));
-    return opt && *opt == static_cast<To>(v);
+    optional<To> opt2{std::forward<Input>(in)};
+    return opt && *opt == static_cast<To>(v) && (opt2 && *opt2 == static_cast<To>(v));
 }
 
 void test_implicit()
@@ -131,6 +132,15 @@ void test_explicit() {
             assert(T::copy_constructed == 0);
             assert(t.value().value == 42);
         }
+        T::reset();
+        {
+            optional<T> t{43};
+            assert(T::alive == 1);
+            assert(T::value_constructed == 1);
+            assert(T::move_constructed == 0);
+            assert(T::copy_constructed == 0);
+            assert(t.value().value == 43);
+        }
         assert(T::alive == 0);
     }
 #ifndef TEST_HAS_NO_EXCEPTIONS
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp
index 4f9b6993c6f4f..deb65dd0a56d0 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp
@@ -64,6 +64,11 @@ constexpr int test()
         assert(std::move(opt).value_or(Y(3)) == 4);
         assert(!opt);
     }
+    {
+        optional<X> opt;
+        assert(std::move(opt).value_or<X>({Y(3)}) == 4);
+        assert(!opt);
+    }
     return 0;
 }
 
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp
index cf782f1137876..355dae0ea498f 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp
@@ -75,6 +75,10 @@ int main(int, char**)
         const optional<X> opt;
         assert(opt.value_or(Y(3)) == 4);
     }
+    {
+        const optional<X> opt;
+        assert(opt.value_or<X>({Y(3)}) == 4);
+    }
 
   return 0;
 }

>From de1c135189923fbada590999bcb6cfbf460bf78d Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 26 Aug 2025 22:50:40 -0400
Subject: [PATCH 3/7] Formatting

---
 .../expected/expected.expected/ctor/ctor.u.pass.cpp         | 3 +--
 .../optional.object.observe/value_or.pass.cpp               | 6 +++---
 .../optional.object.observe/value_or_const.pass.cpp         | 4 ++--
 3 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp
index 4c16176b6e394..63f2fb5e947f8 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp
@@ -80,7 +80,7 @@ struct CopyOnly {
   friend constexpr bool operator==(const CopyOnly& mi, int ii) { return mi.i == ii; }
 };
 
-struct MoveOnly2{
+struct MoveOnly2 {
   int j;
   bool used_move1 = false;
   bool used_move2 = false;
@@ -89,7 +89,6 @@ struct MoveOnly2{
   constexpr MoveOnly2(const MoveOnly2&) = delete;
   constexpr MoveOnly2(MoveOnly2&& m) : j(m.j), used_move1(true) {}
   constexpr MoveOnly2(const MoveOnly2&& m) : j(m.j), used_move2(true) {}
-
 };
 
 struct BaseError {};
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp
index deb65dd0a56d0..f67643ef2e4cf 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp
@@ -65,9 +65,9 @@ constexpr int test()
         assert(!opt);
     }
     {
-        optional<X> opt;
-        assert(std::move(opt).value_or<X>({Y(3)}) == 4);
-        assert(!opt);
+      optional<X> opt;
+      assert(std::move(opt).value_or<X>({Y(3)}) == 4);
+      assert(!opt);
     }
     return 0;
 }
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp
index 355dae0ea498f..5b3ff9131e6ce 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp
@@ -76,8 +76,8 @@ int main(int, char**)
         assert(opt.value_or(Y(3)) == 4);
     }
     {
-        const optional<X> opt;
-        assert(opt.value_or<X>({Y(3)}) == 4);
+      const optional<X> opt;
+      assert(opt.value_or<X>({Y(3)}) == 4);
     }
 
   return 0;

>From 915663c3c51d899876e26eb77c4c00e00a1eb873 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 26 Aug 2025 22:57:05 -0400
Subject: [PATCH 4/7] Deduce value_or template

---
 .../optional.object/optional.object.observe/value_or.pass.cpp   | 2 +-
 .../optional.object.observe/value_or_const.pass.cpp             | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp
index f67643ef2e4cf..5f3ca4bbb0b6d 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp
@@ -66,7 +66,7 @@ constexpr int test()
     }
     {
       optional<X> opt;
-      assert(std::move(opt).value_or<X>({Y(3)}) == 4);
+      assert(std::move(opt).value_or({Y(3)}) == 4);
       assert(!opt);
     }
     return 0;
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp
index 5b3ff9131e6ce..ec42890a3b995 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp
@@ -77,7 +77,7 @@ int main(int, char**)
     }
     {
       const optional<X> opt;
-      assert(opt.value_or<X>({Y(3)}) == 4);
+      assert(opt.value_or({Y(3)}) == 4);
     }
 
   return 0;

>From 872a9aa149a4ad9ea320aa9a1ffb379ccdfb4802 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Tue, 26 Aug 2025 23:16:42 -0400
Subject: [PATCH 5/7] expect assign, formatting and fix shadow

---
 .../assign/assign.U.pass.cpp                  | 35 +++++++++++++++++++
 .../expected.expected/ctor/ctor.u.pass.cpp    |  2 +-
 .../optional.object.ctor/U.pass.cpp           | 12 +++----
 3 files changed, 42 insertions(+), 7 deletions(-)

diff --git a/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp
index 807a8af5bb5f6..0c0b0e3546006 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp
@@ -325,6 +325,41 @@ constexpr bool test() {
     }
   }
 
+  // Check move constructor selection
+  {
+    struct MoveOnlyMulti {
+      bool used_move1 = false;
+      bool used_move2 = false;
+
+      constexpr MoveOnlyMulti()                                = default;
+      constexpr MoveOnlyMulti(const MoveOnlyMulti&)            = delete;
+      constexpr MoveOnlyMulti& operator=(const MoveOnlyMulti&) = delete;
+      constexpr MoveOnlyMulti& operator=(MoveOnlyMulti&&) {
+        used_move1 = true;
+        return *this;
+      }
+      constexpr MoveOnlyMulti& operator=(const MoveOnlyMulti&&) {
+        used_move2 = true;
+        return *this;
+      };
+      constexpr MoveOnlyMulti(MoveOnlyMulti&&) : used_move1(true) {}
+      constexpr MoveOnlyMulti(const MoveOnlyMulti&&) : used_move2(true) {}
+    };
+
+    {
+      MoveOnlyMulti t{};
+      std::expected<MoveOnlyMulti, int> e1(std::unexpect);
+      e1 = std::move(t);
+      assert(e1.value().used_move1);
+    }
+    {
+      const MoveOnlyMulti t{};
+      std::expected<MoveOnlyMulti, int> e1(std::unexpect);
+      e1 = std::move(t);
+      assert(e1.value().used_move2);
+    }
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp
index 63f2fb5e947f8..fe664dfc97cfe 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp
@@ -85,7 +85,7 @@ struct MoveOnly2 {
   bool used_move1 = false;
   bool used_move2 = false;
 
-  constexpr explicit MoveOnly2(int j) : j(j) {}
+  constexpr explicit MoveOnly2(int jj) : j(jj) {}
   constexpr MoveOnly2(const MoveOnly2&) = delete;
   constexpr MoveOnly2(MoveOnly2&& m) : j(m.j), used_move1(true) {}
   constexpr MoveOnly2(const MoveOnly2&& m) : j(m.j), used_move2(true) {}
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp
index edf9fa08d307a..9d2badbf145b5 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp
@@ -134,12 +134,12 @@ void test_explicit() {
         }
         T::reset();
         {
-            optional<T> t{43};
-            assert(T::alive == 1);
-            assert(T::value_constructed == 1);
-            assert(T::move_constructed == 0);
-            assert(T::copy_constructed == 0);
-            assert(t.value().value == 43);
+          optional<T> t{43};
+          assert(T::alive == 1);
+          assert(T::value_constructed == 1);
+          assert(T::move_constructed == 0);
+          assert(T::copy_constructed == 0);
+          assert(t.value().value == 43);
         }
         assert(T::alive == 0);
     }

>From ad83ff847fa9ad75d5c46c4a4e12ee3b0f3ba6fb Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Wed, 27 Aug 2025 19:24:43 -0400
Subject: [PATCH 6/7] Update Cxx2cIssues

---
 libcxx/docs/Status/Cxx2cIssues.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 082a86a65c852..fea5da0506ba5 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -78,7 +78,7 @@
 "","","","","",""
 "`LWG3216 <https://wg21.link/LWG3216>`__","Rebinding the allocator before calling ``construct``/``destroy`` in ``allocate_shared``","2024-11 (Wrocław)","","",""
 "`LWG3436 <https://wg21.link/LWG3436>`__","``std::construct_at`` should support arrays","2024-11 (Wrocław)","","",""
-"`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","","",""
+"`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22",""
 "`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","","",""
 "`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","",""
 "`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","","",""

>From c2814519cdfe988c7594d58c9e012790483801a7 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Thu, 4 Sep 2025 14:12:20 -0400
Subject: [PATCH 7/7] Update tests

---
 .../expected.expected/assign/assign.U.pass.cpp     |  9 ++++++---
 .../optional.object.ctor/U.pass.cpp                | 14 +++++---------
 .../optional.object.observe/value_or.pass.cpp      | 11 +++++++++++
 3 files changed, 22 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp
index 0c0b0e3546006..795fa77e8338c 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp
@@ -349,14 +349,17 @@ constexpr bool test() {
     {
       MoveOnlyMulti t{};
       std::expected<MoveOnlyMulti, int> e1(std::unexpect);
-      e1 = std::move(t);
+      static_assert(std::is_same_v<decltype(std::move(t)), MoveOnlyMulti&&>);
+      e1 = {std::move(t)};
       assert(e1.value().used_move1);
     }
     {
       const MoveOnlyMulti t{};
       std::expected<MoveOnlyMulti, int> e1(std::unexpect);
-      e1 = std::move(t);
-      assert(e1.value().used_move2);
+      static_assert(std::is_same_v<decltype(std::move(t)), const MoveOnlyMulti&&>);
+      // _Up = remove_cv_t<const MoveOnlyMulti&&> --> should use MoveOnlyMulti(MoveOnlyMulti&&)
+      e1 = {std::move(t)};
+      assert(e1.value().used_move1);
     }
   }
 
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp
index 9d2badbf145b5..a90fecfd075fe 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp
@@ -84,6 +84,11 @@ void test_implicit()
         using T = TestTypes::TestType;
         assert(implicit_conversion<T>(3, T(3)));
     }
+    {
+      using T = TestTypes::TestType;
+      optional<T> opt({3});
+      assert(opt && *opt == static_cast<T>(3));
+    }
   {
     using O = optional<ImplicitAny>;
     static_assert(!test_convertible<O, std::in_place_t>(), "");
@@ -132,15 +137,6 @@ void test_explicit() {
             assert(T::copy_constructed == 0);
             assert(t.value().value == 42);
         }
-        T::reset();
-        {
-          optional<T> t{43};
-          assert(T::alive == 1);
-          assert(T::value_constructed == 1);
-          assert(T::move_constructed == 0);
-          assert(T::copy_constructed == 0);
-          assert(t.value().value == 43);
-        }
         assert(T::alive == 0);
     }
 #ifndef TEST_HAS_NO_EXCEPTIONS
diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp
index 5f3ca4bbb0b6d..8c063ae1a799c 100644
--- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp
@@ -40,6 +40,12 @@ struct X
         {return x.i_ == y.i_;}
 };
 
+struct Z {
+  int i_, j_;
+  constexpr Z(int i, int j) : i_(i), j_(j) {}
+  friend constexpr bool operator==(const Z& z1, const Z& z2) { return z1.i_ == z2.i_ && z1.j_ == z2.j_; }
+};
+
 constexpr int test()
 {
     {
@@ -69,6 +75,11 @@ constexpr int test()
       assert(std::move(opt).value_or({Y(3)}) == 4);
       assert(!opt);
     }
+    {
+      optional<Z> opt;
+      assert((std::move(opt).value_or({2, 3}) == Z{2, 3}));
+      assert(!opt);
+    }
     return 0;
 }
 



More information about the libcxx-commits mailing list