[libcxx-commits] [libcxx] [libc++] Ensure that `std::expected` has no tail padding (PR #69673)

Jan Kokemüller via libcxx-commits libcxx-commits at lists.llvm.org
Sat Oct 21 02:41:13 PDT 2023


https://github.com/jiixyj updated https://github.com/llvm/llvm-project/pull/69673

>From 9832a97756cdb800947827ff881f0509159bcc12 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 10 Oct 2023 20:24:06 +0200
Subject: [PATCH 01/24] [libc++] Fix UB in <expected> related to "has value"
 flag (#68552)

The calls to `std::construct_at` might overwrite the previously set
`__has_value_` flag, so reverse the order everywhere. Where possible,
avoid calling `std::construct_at` and construct the value/error
directly into the union.
---
 libcxx/include/__expected/expected.h          | 168 ++++++++----------
 .../observers/has_value.pass.cpp              |  39 ++++
 2 files changed, 117 insertions(+), 90 deletions(-)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 045370a486fae6b..08f35b1111f6bf9 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -119,9 +119,7 @@ class expected {
   _LIBCPP_HIDE_FROM_ABI constexpr expected()
     noexcept(is_nothrow_default_constructible_v<_Tp>) // strengthened
     requires is_default_constructible_v<_Tp>
-      : __has_val_(true) {
-    std::construct_at(std::addressof(__union_.__val_));
-  }
+      : __union_(__construct_in_place_tag{}), __has_val_(true) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
 
@@ -136,14 +134,7 @@ class expected {
     noexcept(is_nothrow_copy_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Err>) // strengthened
     requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err> &&
              !(is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Err>))
-      : __has_val_(__other.__has_val_) {
-    if (__has_val_) {
-      std::construct_at(std::addressof(__union_.__val_), __other.__union_.__val_);
-    } else {
-      std::construct_at(std::addressof(__union_.__unex_), __other.__union_.__unex_);
-    }
-  }
-
+      : __union_(__union_from_expected(__other)), __has_val_(__other.__has_val_) { }
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
     requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err>
@@ -154,13 +145,7 @@ class expected {
     noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_move_constructible_v<_Err>)
     requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err> &&
              !(is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>))
-      : __has_val_(__other.__has_val_) {
-    if (__has_val_) {
-      std::construct_at(std::addressof(__union_.__val_), std::move(__other.__union_.__val_));
-    } else {
-      std::construct_at(std::addressof(__union_.__unex_), std::move(__other.__union_.__unex_));
-    }
-  }
+      : __union_(__union_from_expected(std::move(__other))), __has_val_(__other.__has_val_) { }
 
 private:
   template <class _Up, class _OtherErr, class _UfQual, class _OtherErrQual>
@@ -200,26 +185,14 @@ class expected {
   expected(const expected<_Up, _OtherErr>& __other)
     noexcept(is_nothrow_constructible_v<_Tp, const _Up&> &&
              is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __has_val_(__other.__has_val_) {
-    if (__has_val_) {
-      std::construct_at(std::addressof(__union_.__val_), __other.__union_.__val_);
-    } else {
-      std::construct_at(std::addressof(__union_.__unex_), __other.__union_.__unex_);
-    }
-  }
+      : __union_(__union_from_expected(__other)), __has_val_(__other.__has_val_) {}
 
   template <class _Up, class _OtherErr>
     requires __can_convert<_Up, _OtherErr, _Up, _OtherErr>::value
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp> || !is_convertible_v<_OtherErr, _Err>)
   expected(expected<_Up, _OtherErr>&& __other)
     noexcept(is_nothrow_constructible_v<_Tp, _Up> && is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __has_val_(__other.__has_val_) {
-    if (__has_val_) {
-      std::construct_at(std::addressof(__union_.__val_), std::move(__other.__union_.__val_));
-    } else {
-      std::construct_at(std::addressof(__union_.__unex_), std::move(__other.__union_.__unex_));
-    }
-  }
+      : __union_(__union_from_expected(std::move(__other))), __has_val_(__other.__has_val_) {}
 
   template <class _Up = _Tp>
     requires(!is_same_v<remove_cvref_t<_Up>, in_place_t> && !is_same_v<expected, remove_cvref_t<_Up>> &&
@@ -227,61 +200,47 @@ class expected {
              (!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_expected<remove_cvref_t<_Up>>::value))
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp>)
       expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
-      : __has_val_(true) {
-    std::construct_at(std::addressof(__union_.__val_), std::forward<_Up>(__u));
-  }
+      : __union_(__construct_in_place_tag{}, std::forward<_Up>(__u)), __has_val_(true) {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, const _OtherErr&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
   expected(const unexpected<_OtherErr>& __unex)
     noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __has_val_(false) {
-    std::construct_at(std::addressof(__union_.__unex_), __unex.error());
-  }
+      : __union_(__construct_unexpected_tag{}, __unex.error()), __has_val_(false) {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
   expected(unexpected<_OtherErr>&& __unex)
     noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __has_val_(false) {
-    std::construct_at(std::addressof(__union_.__unex_), std::move(__unex.error()));
-  }
+      : __union_(__construct_unexpected_tag{}, std::move(__unex.error())), __has_val_(false) {}
 
   template <class... _Args>
     requires is_constructible_v<_Tp, _Args...>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Tp, _Args...>) // strengthened
-      : __has_val_(true) {
-    std::construct_at(std::addressof(__union_.__val_), std::forward<_Args>(__args)...);
-  }
+      : __union_(__construct_in_place_tag{}, std::forward<_Args>(__args)...), __has_val_(true) {}
 
   template <class _Up, class... _Args>
     requires is_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
   _LIBCPP_HIDE_FROM_ABI constexpr explicit
   expected(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, _Args...>) // strengthened
-      : __has_val_(true) {
-    std::construct_at(std::addressof(__union_.__val_), __il, std::forward<_Args>(__args)...);
-  }
+      : __union_(__construct_in_place_tag{}, __il, std::forward<_Args>(__args)...), __has_val_(true) {}
 
   template <class... _Args>
     requires is_constructible_v<_Err, _Args...>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
-    noexcept(is_nothrow_constructible_v<_Err, _Args...>)  // strengthened
-      : __has_val_(false) {
-    std::construct_at(std::addressof(__union_.__unex_), std::forward<_Args>(__args)...);
-  }
+    noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
+      : __union_(__construct_unexpected_tag{}, std::forward<_Args>(__args)...), __has_val_(false) {}
 
   template <class _Up, class... _Args>
     requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
   _LIBCPP_HIDE_FROM_ABI constexpr explicit
   expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
-      : __has_val_(false) {
-    std::construct_at(std::addressof(__union_.__unex_), __il, std::forward<_Args>(__args)...);
-  }
+      : __union_(__construct_unexpected_tag{}, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
 
   // [expected.object.dtor], destructor
 
@@ -440,9 +399,10 @@ class expected {
       std::destroy_at(std::addressof(__union_.__val_));
     } else {
       std::destroy_at(std::addressof(__union_.__unex_));
-      __has_val_ = true;
     }
-    return *std::construct_at(std::addressof(__union_.__val_), std::forward<_Args>(__args)...);
+    std::construct_at(std::addressof(__union_.__val_), std::forward<_Args>(__args)...);
+    __has_val_ = true;
+    return *std::addressof(__union_.__val_);
   }
 
   template <class _Up, class... _Args>
@@ -452,9 +412,10 @@ class expected {
       std::destroy_at(std::addressof(__union_.__val_));
     } else {
       std::destroy_at(std::addressof(__union_.__unex_));
-      __has_val_ = true;
     }
-    return *std::construct_at(std::addressof(__union_.__val_), __il, std::forward<_Args>(__args)...);
+    std::construct_at(std::addressof(__union_.__val_), __il, std::forward<_Args>(__args)...);
+    __has_val_ = true;
+    return *std::addressof(__union_.__val_);
   }
 
 
@@ -894,11 +855,21 @@ class expected {
 
 private:
   struct __empty_t {};
+  struct __construct_in_place_tag {};
+  struct __construct_unexpected_tag {};
 
   template <class _ValueType, class _ErrorType>
   union __union_t {
     _LIBCPP_HIDE_FROM_ABI constexpr __union_t() {}
 
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_in_place_tag, _Args&&... __args)
+        : __val_(std::forward<_Args>(__args)...) {}
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+        : __unex_(std::forward<_Args>(__args)...) {}
+
     template <class _Func, class... _Args>
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(
         std::__expected_construct_in_place_from_invoke_tag, _Func&& __f, _Args&&... __args)
@@ -931,6 +902,14 @@ class expected {
     _LIBCPP_HIDE_FROM_ABI constexpr __union_t(const __union_t&) = default;
     _LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(const __union_t&) = default;
 
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_in_place_tag, _Args&&... __args)
+        : __val_(std::forward<_Args>(__args)...) {}
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+        : __unex_(std::forward<_Args>(__args)...) {}
+
     template <class _Func, class... _Args>
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(
         std::__expected_construct_in_place_from_invoke_tag, _Func&& __f, _Args&&... __args)
@@ -955,6 +934,18 @@ class expected {
     _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
   };
 
+  template <class _Up, class _OtherErr>
+  _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
+    return __other.__has_val_ ? __union_t<_Tp, _Err>(__construct_in_place_tag{}, __other.__union_.__val_)
+                              : __union_t<_Tp, _Err>(__construct_unexpected_tag{}, __other.__union_.__unex_);
+  }
+
+  template <class _Up, class _OtherErr>
+  _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
+    return __other.__has_val_ ? __union_t<_Tp, _Err>(__construct_in_place_tag{}, std::move(__other.__union_.__val_))
+                              : __union_t<_Tp, _Err>(__construct_unexpected_tag{}, std::move(__other.__union_.__unex_));
+  }
+
   _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
   bool __has_val_;
 };
@@ -998,11 +989,7 @@ class expected<_Tp, _Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr expected(const expected& __rhs)
     noexcept(is_nothrow_copy_constructible_v<_Err>) // strengthened
     requires(is_copy_constructible_v<_Err> && !is_trivially_copy_constructible_v<_Err>)
-      : __has_val_(__rhs.__has_val_) {
-    if (!__rhs.__has_val_) {
-      std::construct_at(std::addressof(__union_.__unex_), __rhs.__union_.__unex_);
-    }
-  }
+      : __union_(__union_from_expected(__rhs)), __has_val_(__rhs.__has_val_) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
     requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
@@ -1011,51 +998,35 @@ class expected<_Tp, _Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr expected(expected&& __rhs)
     noexcept(is_nothrow_move_constructible_v<_Err>)
     requires(is_move_constructible_v<_Err> && !is_trivially_move_constructible_v<_Err>)
-      : __has_val_(__rhs.__has_val_) {
-    if (!__rhs.__has_val_) {
-      std::construct_at(std::addressof(__union_.__unex_), std::move(__rhs.__union_.__unex_));
-    }
-  }
+      : __union_(__union_from_expected(std::move(__rhs))), __has_val_(__rhs.__has_val_) {}
 
   template <class _Up, class _OtherErr>
     requires __can_convert<_Up, _OtherErr, const _OtherErr&>::value
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
   expected(const expected<_Up, _OtherErr>& __rhs)
     noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __has_val_(__rhs.__has_val_) {
-    if (!__rhs.__has_val_) {
-      std::construct_at(std::addressof(__union_.__unex_), __rhs.__union_.__unex_);
-    }
-  }
+      : __union_(__union_from_expected(__rhs)), __has_val_(__rhs.__has_val_) {}
 
   template <class _Up, class _OtherErr>
     requires __can_convert<_Up, _OtherErr, _OtherErr>::value
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
   expected(expected<_Up, _OtherErr>&& __rhs)
     noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __has_val_(__rhs.__has_val_) {
-    if (!__rhs.__has_val_) {
-      std::construct_at(std::addressof(__union_.__unex_), std::move(__rhs.__union_.__unex_));
-    }
-  }
+      : __union_(__union_from_expected(std::move(__rhs))), __has_val_(__rhs.__has_val_) {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, const _OtherErr&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
   expected(const unexpected<_OtherErr>& __unex)
     noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __has_val_(false) {
-    std::construct_at(std::addressof(__union_.__unex_), __unex.error());
-  }
+      : __union_(__construct_unexpected_tag{}, __unex.error()), __has_val_(false) {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
   expected(unexpected<_OtherErr>&& __unex)
     noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __has_val_(false) {
-    std::construct_at(std::addressof(__union_.__unex_), std::move(__unex.error()));
-  }
+      : __union_(__construct_unexpected_tag{}, std::move(__unex.error())), __has_val_(false) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __has_val_(true) {}
 
@@ -1063,17 +1034,13 @@ class expected<_Tp, _Err> {
     requires is_constructible_v<_Err, _Args...>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
-      : __has_val_(false) {
-    std::construct_at(std::addressof(__union_.__unex_), std::forward<_Args>(__args)...);
-  }
+      : __union_(__construct_unexpected_tag{}, std::forward<_Args>(__args)...), __has_val_(false) {}
 
   template <class _Up, class... _Args>
     requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
-      : __has_val_(false) {
-    std::construct_at(std::addressof(__union_.__unex_), __il, std::forward<_Args>(__args)...);
-  }
+      : __union_(__construct_unexpected_tag{}, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
 
 private:
   template <class _Func>
@@ -1502,11 +1469,16 @@ class expected<_Tp, _Err> {
 
 private:
   struct __empty_t {};
+  struct __construct_unexpected_tag {};
 
   template <class _ErrorType>
   union __union_t {
     _LIBCPP_HIDE_FROM_ABI constexpr __union_t() : __empty_() {}
 
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+        : __unex_(std::forward<_Args>(__args)...) {}
+
     template <class _Func, class... _Args>
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(
         __expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args)
@@ -1534,6 +1506,10 @@ class expected<_Tp, _Err> {
     _LIBCPP_HIDE_FROM_ABI constexpr __union_t(const __union_t&) = default;
     _LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(const __union_t&) = default;
 
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+        : __unex_(std::forward<_Args>(__args)...) {}
+
     template <class _Func, class... _Args>
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(
         __expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args)
@@ -1552,6 +1528,18 @@ class expected<_Tp, _Err> {
     _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
   };
 
+  template <class _Up, class _OtherErr>
+  _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
+    return __other.__has_val_ ? __union_t<_Err>()
+                              : __union_t<_Err>(__construct_unexpected_tag{}, __other.__union_.__unex_);
+  }
+
+  template <class _Up, class _OtherErr>
+  _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
+    return __other.__has_val_ ? __union_t<_Err>()
+                              : __union_t<_Err>(__construct_unexpected_tag{}, std::move(__other.__union_.__unex_));
+  }
+
   _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
   bool __has_val_;
 };
diff --git a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
index 27d657a065699ea..8979e0f45d44f50 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
@@ -12,6 +12,7 @@
 #include <cassert>
 #include <concepts>
 #include <expected>
+#include <optional>
 #include <type_traits>
 #include <utility>
 
@@ -30,6 +31,22 @@ static_assert(!HasValueNoexcept<Foo>);
 static_assert(HasValueNoexcept<std::expected<int, int>>);
 static_assert(HasValueNoexcept<const std::expected<int, int>>);
 
+// This type has one byte of tail padding where `std::expected` will put its
+// "has value" flag. The constructor will clobber all bytes including the
+// tail padding. With this type we can check that `std::expected` will set
+// its "has value" flag _after_ the value/error object is constructed.
+template <int c>
+struct tail_clobberer {
+  constexpr tail_clobberer() {
+    if (!std::is_constant_evaluated()) {
+      // This `memset` might actually be UB (?) but suffices to reproduce bugs
+      // related to the "has value" flag.
+      std::memset(this, c, sizeof(*this));
+    }
+  }
+  alignas(2) bool b;
+};
+
 constexpr bool test() {
   // has_value
   {
@@ -43,6 +60,28 @@ constexpr bool test() {
     assert(!e.has_value());
   }
 
+  // See https://github.com/llvm/llvm-project/issues/68552
+  {
+    static constexpr auto f1 = [] -> std::expected<std::optional<int>, long> { return 0; };
+
+    static constexpr auto f2 = [] -> std::expected<std::optional<int>, int> {
+      return f1().transform_error([](auto) { return 0; });
+    };
+
+    auto e = f2();
+    assert(e.has_value());
+  }
+  {
+    const std::expected<tail_clobberer<0>, bool> e = {};
+    static_assert(sizeof(tail_clobberer<0>) == sizeof(e));
+    assert(e.has_value());
+  }
+  {
+    const std::expected<void, tail_clobberer<1>> e(std::unexpect);
+    static_assert(sizeof(tail_clobberer<1>) == sizeof(e));
+    assert(!e.has_value());
+  }
+
   return true;
 }
 

>From 1f9028a0de836e8e30a58f4da4bcc1b4610c21fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sun, 15 Oct 2023 13:32:14 +0200
Subject: [PATCH 02/24] replace home-grown constructor tags with standard ones

---
 libcxx/include/__expected/expected.h | 51 +++++++++++++---------------
 1 file changed, 24 insertions(+), 27 deletions(-)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 08f35b1111f6bf9..6440b31e7183201 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -119,7 +119,7 @@ class expected {
   _LIBCPP_HIDE_FROM_ABI constexpr expected()
     noexcept(is_nothrow_default_constructible_v<_Tp>) // strengthened
     requires is_default_constructible_v<_Tp>
-      : __union_(__construct_in_place_tag{}), __has_val_(true) {}
+      : __union_(std::in_place), __has_val_(true) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
 
@@ -200,47 +200,47 @@ class expected {
              (!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_expected<remove_cvref_t<_Up>>::value))
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp>)
       expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
-      : __union_(__construct_in_place_tag{}, std::forward<_Up>(__u)), __has_val_(true) {}
+      : __union_(std::in_place, std::forward<_Up>(__u)), __has_val_(true) {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, const _OtherErr&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
   expected(const unexpected<_OtherErr>& __unex)
     noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __union_(__construct_unexpected_tag{}, __unex.error()), __has_val_(false) {}
+      : __union_(std::unexpect, __unex.error()), __has_val_(false) {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
   expected(unexpected<_OtherErr>&& __unex)
     noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __union_(__construct_unexpected_tag{}, std::move(__unex.error())), __has_val_(false) {}
+      : __union_(std::unexpect, std::move(__unex.error())), __has_val_(false) {}
 
   template <class... _Args>
     requires is_constructible_v<_Tp, _Args...>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Tp, _Args...>) // strengthened
-      : __union_(__construct_in_place_tag{}, std::forward<_Args>(__args)...), __has_val_(true) {}
+      : __union_(std::in_place, std::forward<_Args>(__args)...), __has_val_(true) {}
 
   template <class _Up, class... _Args>
     requires is_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
   _LIBCPP_HIDE_FROM_ABI constexpr explicit
   expected(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, _Args...>) // strengthened
-      : __union_(__construct_in_place_tag{}, __il, std::forward<_Args>(__args)...), __has_val_(true) {}
+      : __union_(std::in_place, __il, std::forward<_Args>(__args)...), __has_val_(true) {}
 
   template <class... _Args>
     requires is_constructible_v<_Err, _Args...>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
-      : __union_(__construct_unexpected_tag{}, std::forward<_Args>(__args)...), __has_val_(false) {}
+      : __union_(std::unexpect, std::forward<_Args>(__args)...), __has_val_(false) {}
 
   template <class _Up, class... _Args>
     requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
   _LIBCPP_HIDE_FROM_ABI constexpr explicit
   expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
-      : __union_(__construct_unexpected_tag{}, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
+      : __union_(std::unexpect, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
 
   // [expected.object.dtor], destructor
 
@@ -855,19 +855,17 @@ class expected {
 
 private:
   struct __empty_t {};
-  struct __construct_in_place_tag {};
-  struct __construct_unexpected_tag {};
 
   template <class _ValueType, class _ErrorType>
   union __union_t {
     _LIBCPP_HIDE_FROM_ABI constexpr __union_t() {}
 
     template <class... _Args>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_in_place_tag, _Args&&... __args)
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::in_place_t, _Args&&... __args)
         : __val_(std::forward<_Args>(__args)...) {}
 
     template <class... _Args>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::unexpect_t, _Args&&... __args)
         : __unex_(std::forward<_Args>(__args)...) {}
 
     template <class _Func, class... _Args>
@@ -903,11 +901,11 @@ class expected {
     _LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(const __union_t&) = default;
 
     template <class... _Args>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_in_place_tag, _Args&&... __args)
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::in_place_t, _Args&&... __args)
         : __val_(std::forward<_Args>(__args)...) {}
 
     template <class... _Args>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::unexpect_t, _Args&&... __args)
         : __unex_(std::forward<_Args>(__args)...) {}
 
     template <class _Func, class... _Args>
@@ -936,14 +934,14 @@ class expected {
 
   template <class _Up, class _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
-    return __other.__has_val_ ? __union_t<_Tp, _Err>(__construct_in_place_tag{}, __other.__union_.__val_)
-                              : __union_t<_Tp, _Err>(__construct_unexpected_tag{}, __other.__union_.__unex_);
+    return __other.__has_val_ ? __union_t<_Tp, _Err>(std::in_place, __other.__union_.__val_)
+                              : __union_t<_Tp, _Err>(std::unexpect, __other.__union_.__unex_);
   }
 
   template <class _Up, class _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
-    return __other.__has_val_ ? __union_t<_Tp, _Err>(__construct_in_place_tag{}, std::move(__other.__union_.__val_))
-                              : __union_t<_Tp, _Err>(__construct_unexpected_tag{}, std::move(__other.__union_.__unex_));
+    return __other.__has_val_ ? __union_t<_Tp, _Err>(std::in_place, std::move(__other.__union_.__val_))
+                              : __union_t<_Tp, _Err>(std::unexpect, std::move(__other.__union_.__unex_));
   }
 
   _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
@@ -1019,14 +1017,14 @@ class expected<_Tp, _Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
   expected(const unexpected<_OtherErr>& __unex)
     noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __union_(__construct_unexpected_tag{}, __unex.error()), __has_val_(false) {}
+      : __union_(std::unexpect, __unex.error()), __has_val_(false) {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
   expected(unexpected<_OtherErr>&& __unex)
     noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __union_(__construct_unexpected_tag{}, std::move(__unex.error())), __has_val_(false) {}
+      : __union_(std::unexpect, std::move(__unex.error())), __has_val_(false) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __has_val_(true) {}
 
@@ -1034,13 +1032,13 @@ class expected<_Tp, _Err> {
     requires is_constructible_v<_Err, _Args...>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
-      : __union_(__construct_unexpected_tag{}, std::forward<_Args>(__args)...), __has_val_(false) {}
+      : __union_(std::unexpect, std::forward<_Args>(__args)...), __has_val_(false) {}
 
   template <class _Up, class... _Args>
     requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
-      : __union_(__construct_unexpected_tag{}, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
+      : __union_(std::unexpect, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
 
 private:
   template <class _Func>
@@ -1469,14 +1467,13 @@ class expected<_Tp, _Err> {
 
 private:
   struct __empty_t {};
-  struct __construct_unexpected_tag {};
 
   template <class _ErrorType>
   union __union_t {
     _LIBCPP_HIDE_FROM_ABI constexpr __union_t() : __empty_() {}
 
     template <class... _Args>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::unexpect_t, _Args&&... __args)
         : __unex_(std::forward<_Args>(__args)...) {}
 
     template <class _Func, class... _Args>
@@ -1507,7 +1504,7 @@ class expected<_Tp, _Err> {
     _LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(const __union_t&) = default;
 
     template <class... _Args>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::unexpect_t, _Args&&... __args)
         : __unex_(std::forward<_Args>(__args)...) {}
 
     template <class _Func, class... _Args>
@@ -1531,13 +1528,13 @@ class expected<_Tp, _Err> {
   template <class _Up, class _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
     return __other.__has_val_ ? __union_t<_Err>()
-                              : __union_t<_Err>(__construct_unexpected_tag{}, __other.__union_.__unex_);
+                              : __union_t<_Err>(std::unexpect, __other.__union_.__unex_);
   }
 
   template <class _Up, class _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
     return __other.__has_val_ ? __union_t<_Err>()
-                              : __union_t<_Err>(__construct_unexpected_tag{}, std::move(__other.__union_.__unex_));
+                              : __union_t<_Err>(std::unexpect, std::move(__other.__union_.__unex_));
   }
 
   _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;

>From ecac62ff16a0253c91c2ff261212085d0eadb615 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sun, 15 Oct 2023 13:37:48 +0200
Subject: [PATCH 03/24] replace use of ternary operator by explicit if/else

---
 libcxx/include/__expected/expected.h | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 6440b31e7183201..55036d3f55c2432 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -934,14 +934,18 @@ class expected {
 
   template <class _Up, class _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
-    return __other.__has_val_ ? __union_t<_Tp, _Err>(std::in_place, __other.__union_.__val_)
-                              : __union_t<_Tp, _Err>(std::unexpect, __other.__union_.__unex_);
+    if (__other.__has_val_)
+      return __union_t<_Tp, _Err>(std::in_place, __other.__union_.__val_);
+    else
+      return __union_t<_Tp, _Err>(std::unexpect, __other.__union_.__unex_);
   }
 
   template <class _Up, class _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
-    return __other.__has_val_ ? __union_t<_Tp, _Err>(std::in_place, std::move(__other.__union_.__val_))
-                              : __union_t<_Tp, _Err>(std::unexpect, std::move(__other.__union_.__unex_));
+    if (__other.__has_val_)
+      return __union_t<_Tp, _Err>(std::in_place, std::move(__other.__union_.__val_));
+    else
+      return __union_t<_Tp, _Err>(std::unexpect, std::move(__other.__union_.__unex_));
   }
 
   _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
@@ -1527,14 +1531,18 @@ class expected<_Tp, _Err> {
 
   template <class _Up, class _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
-    return __other.__has_val_ ? __union_t<_Err>()
-                              : __union_t<_Err>(std::unexpect, __other.__union_.__unex_);
+    if (__other.__has_val_)
+      return __union_t<_Err>();
+    else
+      return __union_t<_Err>(std::unexpect, __other.__union_.__unex_);
   }
 
   template <class _Up, class _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
-    return __other.__has_val_ ? __union_t<_Err>()
-                              : __union_t<_Err>(std::unexpect, std::move(__other.__union_.__unex_));
+    if (__other.__has_val_)
+      return __union_t<_Err>();
+    else
+      return __union_t<_Err>(std::unexpect, std::move(__other.__union_.__unex_));
   }
 
   _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;

>From 83488c8485cb206295995098d432ae82c31143c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sun, 15 Oct 2023 13:47:06 +0200
Subject: [PATCH 04/24] add a short description to the tests

---
 .../expected.expected/observers/has_value.pass.cpp   | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
index 8979e0f45d44f50..b203253e897795e 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
@@ -60,7 +60,17 @@ constexpr bool test() {
     assert(!e.has_value());
   }
 
-  // See https://github.com/llvm/llvm-project/issues/68552
+  // The following tests check that the "has_value" flag is not overwritten
+  // by the constructor of the value. This could happen because the flag is
+  // stored in the tail padding of the value.
+  //
+  // The first test is a simplified version of the real code where this was
+  // first observed.
+  //
+  // The other tests use a synthetic struct that clobbers its tail padding
+  // on construction, making the issue easier to reproduce.
+  //
+  // See https://github.com/llvm/llvm-project/issues/68552 and the linked PR.
   {
     static constexpr auto f1 = [] -> std::expected<std::optional<int>, long> { return 0; };
 

>From 7b81fb560f60f9742f754e8f7adbffcdab8a7efb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sun, 15 Oct 2023 13:50:57 +0200
Subject: [PATCH 05/24] remove comment about memset being UB here

---
 .../expected/expected.expected/observers/has_value.pass.cpp     | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
index b203253e897795e..d7591718ae784b6 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
@@ -39,8 +39,6 @@ template <int c>
 struct tail_clobberer {
   constexpr tail_clobberer() {
     if (!std::is_constant_evaluated()) {
-      // This `memset` might actually be UB (?) but suffices to reproduce bugs
-      // related to the "has value" flag.
       std::memset(this, c, sizeof(*this));
     }
   }

>From 238d1ca6560069d4d8e2558ee171782e14e50195 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 17 Oct 2023 15:59:37 +0200
Subject: [PATCH 06/24] add constructors to __union_t from expected parts and
 refactor

GCC 13 didn't like the previous approach.
---
 libcxx/include/__expected/expected.h | 80 ++++++++++++++--------------
 1 file changed, 40 insertions(+), 40 deletions(-)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 55036d3f55c2432..06c9a26c8e3a88f 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -134,7 +134,7 @@ class expected {
     noexcept(is_nothrow_copy_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Err>) // strengthened
     requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err> &&
              !(is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Err>))
-      : __union_(__union_from_expected(__other)), __has_val_(__other.__has_val_) { }
+      : __union_(__other.__has_val_, __other.__union_), __has_val_(__other.__has_val_) { }
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
     requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err>
@@ -145,7 +145,7 @@ class expected {
     noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_move_constructible_v<_Err>)
     requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err> &&
              !(is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>))
-      : __union_(__union_from_expected(std::move(__other))), __has_val_(__other.__has_val_) { }
+      : __union_(__other.__has_val_, std::move(__other.__union_)), __has_val_(__other.__has_val_) { }
 
 private:
   template <class _Up, class _OtherErr, class _UfQual, class _OtherErrQual>
@@ -185,14 +185,14 @@ class expected {
   expected(const expected<_Up, _OtherErr>& __other)
     noexcept(is_nothrow_constructible_v<_Tp, const _Up&> &&
              is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __union_(__union_from_expected(__other)), __has_val_(__other.__has_val_) {}
+      : __union_(__other.__has_val_, __other.__union_), __has_val_(__other.__has_val_) {}
 
   template <class _Up, class _OtherErr>
     requires __can_convert<_Up, _OtherErr, _Up, _OtherErr>::value
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp> || !is_convertible_v<_OtherErr, _Err>)
   expected(expected<_Up, _OtherErr>&& __other)
     noexcept(is_nothrow_constructible_v<_Tp, _Up> && is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __union_(__union_from_expected(std::move(__other))), __has_val_(__other.__has_val_) {}
+      : __union_(__other.__has_val_, std::move(__other.__union_)), __has_val_(__other.__has_val_) {}
 
   template <class _Up = _Tp>
     requires(!is_same_v<remove_cvref_t<_Up>, in_place_t> && !is_same_v<expected, remove_cvref_t<_Up>> &&
@@ -878,6 +878,14 @@ class expected {
         std::__expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args)
         : __unex_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {}
 
+    template <class _Union>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
+      if (__has_val)
+        std::construct_at(this, std::in_place, std::forward<_Union>(__other).__val_);
+      else
+        std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+    }
+
     _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
       requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>)
     = default;
@@ -918,6 +926,14 @@ class expected {
         std::__expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args)
         : __unex_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {}
 
+    template <class _Union>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
+      if (__has_val)
+        std::construct_at(this, std::in_place, std::forward<_Union>(__other).__val_);
+      else
+        std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+    }
+
     _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
       requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>)
     = default;
@@ -932,22 +948,6 @@ class expected {
     _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
   };
 
-  template <class _Up, class _OtherErr>
-  _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
-    if (__other.__has_val_)
-      return __union_t<_Tp, _Err>(std::in_place, __other.__union_.__val_);
-    else
-      return __union_t<_Tp, _Err>(std::unexpect, __other.__union_.__unex_);
-  }
-
-  template <class _Up, class _OtherErr>
-  _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
-    if (__other.__has_val_)
-      return __union_t<_Tp, _Err>(std::in_place, std::move(__other.__union_.__val_));
-    else
-      return __union_t<_Tp, _Err>(std::unexpect, std::move(__other.__union_.__unex_));
-  }
-
   _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
   bool __has_val_;
 };
@@ -991,7 +991,7 @@ class expected<_Tp, _Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr expected(const expected& __rhs)
     noexcept(is_nothrow_copy_constructible_v<_Err>) // strengthened
     requires(is_copy_constructible_v<_Err> && !is_trivially_copy_constructible_v<_Err>)
-      : __union_(__union_from_expected(__rhs)), __has_val_(__rhs.__has_val_) {}
+      : __union_(__rhs.__has_val_, __rhs.__union_), __has_val_(__rhs.__has_val_) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
     requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
@@ -1000,21 +1000,21 @@ class expected<_Tp, _Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr expected(expected&& __rhs)
     noexcept(is_nothrow_move_constructible_v<_Err>)
     requires(is_move_constructible_v<_Err> && !is_trivially_move_constructible_v<_Err>)
-      : __union_(__union_from_expected(std::move(__rhs))), __has_val_(__rhs.__has_val_) {}
+      : __union_(__rhs.__has_val_, std::move(__rhs.__union_)), __has_val_(__rhs.__has_val_) {}
 
   template <class _Up, class _OtherErr>
     requires __can_convert<_Up, _OtherErr, const _OtherErr&>::value
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
   expected(const expected<_Up, _OtherErr>& __rhs)
     noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __union_(__union_from_expected(__rhs)), __has_val_(__rhs.__has_val_) {}
+      : __union_(__rhs.__has_val_, __rhs.__union_), __has_val_(__rhs.__has_val_) {}
 
   template <class _Up, class _OtherErr>
     requires __can_convert<_Up, _OtherErr, _OtherErr>::value
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
   expected(expected<_Up, _OtherErr>&& __rhs)
     noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __union_(__union_from_expected(std::move(__rhs))), __has_val_(__rhs.__has_val_) {}
+      : __union_(__rhs.__has_val_, std::move(__rhs.__union_)), __has_val_(__rhs.__has_val_) {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, const _OtherErr&>
@@ -1485,6 +1485,14 @@ class expected<_Tp, _Err> {
         __expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args)
         : __unex_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {}
 
+    template <class _Union>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
+      if (__has_val)
+        std::construct_at(this);
+      else
+        std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+    }
+
     _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
       requires(is_trivially_destructible_v<_ErrorType>)
     = default;
@@ -1516,6 +1524,14 @@ class expected<_Tp, _Err> {
         __expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args)
         : __unex_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {}
 
+    template <class _Union>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
+      if (__has_val)
+        std::construct_at(this);
+      else
+        std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+    }
+
     _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
       requires(is_trivially_destructible_v<_ErrorType>)
     = default;
@@ -1529,22 +1545,6 @@ class expected<_Tp, _Err> {
     _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
   };
 
-  template <class _Up, class _OtherErr>
-  _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
-    if (__other.__has_val_)
-      return __union_t<_Err>();
-    else
-      return __union_t<_Err>(std::unexpect, __other.__union_.__unex_);
-  }
-
-  template <class _Up, class _OtherErr>
-  _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
-    if (__other.__has_val_)
-      return __union_t<_Err>();
-    else
-      return __union_t<_Err>(std::unexpect, std::move(__other.__union_.__unex_));
-  }
-
   _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
   bool __has_val_;
 };

>From dfa9b3cf6e5951b4935e3e310b3e338f721beada Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 17 Oct 2023 16:06:19 +0200
Subject: [PATCH 07/24] put new expected<void> test into appropriate folder

---
 .../observers/has_value.pass.cpp              | 24 +++----------------
 .../observers/has_value.pass.cpp              |  8 +++++++
 libcxx/test/std/utilities/expected/types.h    | 16 +++++++++++++
 3 files changed, 27 insertions(+), 21 deletions(-)

diff --git a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
index d7591718ae784b6..74569cfa2083e92 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
@@ -17,6 +17,7 @@
 #include <utility>
 
 #include "test_macros.h"
+#include "../../types.h"
 
 // Test noexcept
 template <class T>
@@ -31,20 +32,6 @@ static_assert(!HasValueNoexcept<Foo>);
 static_assert(HasValueNoexcept<std::expected<int, int>>);
 static_assert(HasValueNoexcept<const std::expected<int, int>>);
 
-// This type has one byte of tail padding where `std::expected` will put its
-// "has value" flag. The constructor will clobber all bytes including the
-// tail padding. With this type we can check that `std::expected` will set
-// its "has value" flag _after_ the value/error object is constructed.
-template <int c>
-struct tail_clobberer {
-  constexpr tail_clobberer() {
-    if (!std::is_constant_evaluated()) {
-      std::memset(this, c, sizeof(*this));
-    }
-  }
-  alignas(2) bool b;
-};
-
 constexpr bool test() {
   // has_value
   {
@@ -80,15 +67,10 @@ constexpr bool test() {
     assert(e.has_value());
   }
   {
-    const std::expected<tail_clobberer<0>, bool> e = {};
-    static_assert(sizeof(tail_clobberer<0>) == sizeof(e));
+    const std::expected<TailClobberer<0>, bool> e = {};
+    static_assert(sizeof(TailClobberer<0>) == sizeof(e));
     assert(e.has_value());
   }
-  {
-    const std::expected<void, tail_clobberer<1>> e(std::unexpect);
-    static_assert(sizeof(tail_clobberer<1>) == sizeof(e));
-    assert(!e.has_value());
-  }
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
index 42a173d60c898c6..89c79541f995526 100644
--- a/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
@@ -16,6 +16,7 @@
 #include <utility>
 
 #include "test_macros.h"
+#include "../../types.h"
 
 // Test noexcept
 template <class T>
@@ -43,6 +44,13 @@ constexpr bool test() {
     assert(!e.has_value());
   }
 
+  // See comments of the corresponding test in "expected.expected".
+  {
+    const std::expected<void, TailClobberer<1>> e(std::unexpect);
+    static_assert(sizeof(TailClobberer<1>) == sizeof(e));
+    assert(!e.has_value());
+  }
+
   return true;
 }
 
diff --git a/libcxx/test/std/utilities/expected/types.h b/libcxx/test/std/utilities/expected/types.h
index 7c7e517785b4f78..874b72b64e8ae11 100644
--- a/libcxx/test/std/utilities/expected/types.h
+++ b/libcxx/test/std/utilities/expected/types.h
@@ -9,7 +9,9 @@
 #ifndef TEST_STD_UTILITIES_EXPECTED_TYPES_H
 #define TEST_STD_UTILITIES_EXPECTED_TYPES_H
 
+#include <cstring>
 #include <utility>
+#include <type_traits>
 #include "test_macros.h"
 
 template <bool copyMoveNoexcept, bool convertNoexcept = true>
@@ -102,6 +104,20 @@ struct TrackedMove {
   }
 };
 
+// This type has one byte of tail padding where `std::expected` will put its
+// "has value" flag. The constructor will clobber all bytes including the
+// tail padding. With this type we can check that `std::expected` will set
+// its "has value" flag _after_ the value/error object is constructed.
+template <int c>
+struct TailClobberer {
+  constexpr TailClobberer() {
+    if (!std::is_constant_evaluated()) {
+      std::memset(this, c, sizeof(*this));
+    }
+  }
+  alignas(2) bool b;
+};
+
 #ifndef TEST_HAS_NO_EXCEPTIONS
 struct Except {};
 

>From 3028a7c37c3f7208fc55ca11029d86da2f06c0da Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 17 Oct 2023 18:21:47 +0200
Subject: [PATCH 08/24] disable new tests on compilers that cannot build them

---
 .../expected/expected.expected/observers/has_value.pass.cpp  | 5 +++++
 .../expected/expected.void/observers/has_value.pass.cpp      | 3 +++
 2 files changed, 8 insertions(+)

diff --git a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
index 74569cfa2083e92..3cb46bcf79abee3 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
@@ -57,6 +57,7 @@ constexpr bool test() {
   //
   // See https://github.com/llvm/llvm-project/issues/68552 and the linked PR.
   {
+#if !defined(TEST_COMPILER_CLANG) || TEST_CLANG_VER >= 1600
     static constexpr auto f1 = [] -> std::expected<std::optional<int>, long> { return 0; };
 
     static constexpr auto f2 = [] -> std::expected<std::optional<int>, int> {
@@ -65,10 +66,14 @@ constexpr bool test() {
 
     auto e = f2();
     assert(e.has_value());
+#endif
   }
   {
     const std::expected<TailClobberer<0>, bool> e = {};
+    // clang-cl does not support [[no_unique_address]] yet.
+#if !(defined(TEST_COMPILER_CLANG) && defined(_MSC_VER))
     static_assert(sizeof(TailClobberer<0>) == sizeof(e));
+#endif
     assert(e.has_value());
   }
 
diff --git a/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
index 89c79541f995526..50098d10977b702 100644
--- a/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
@@ -47,7 +47,10 @@ constexpr bool test() {
   // See comments of the corresponding test in "expected.expected".
   {
     const std::expected<void, TailClobberer<1>> e(std::unexpect);
+    // clang-cl does not support [[no_unique_address]] yet.
+#if !(defined(TEST_COMPILER_CLANG) && defined(_MSC_VER))
     static_assert(sizeof(TailClobberer<1>) == sizeof(e));
+#endif
     assert(!e.has_value());
   }
 

>From 8df519aabf2c02931dafc6543165518c3033dfcd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 17 Oct 2023 19:57:07 +0200
Subject: [PATCH 09/24] make comment in
 libcxx/test/std/utilities/expected/types.h more general

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/test/std/utilities/expected/types.h | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/utilities/expected/types.h b/libcxx/test/std/utilities/expected/types.h
index 874b72b64e8ae11..e6fd5ac479266ad 100644
--- a/libcxx/test/std/utilities/expected/types.h
+++ b/libcxx/test/std/utilities/expected/types.h
@@ -104,10 +104,12 @@ struct TrackedMove {
   }
 };
 
-// This type has one byte of tail padding where `std::expected` will put its
+// This type has one byte of tail padding where `std::expected` may put its
 // "has value" flag. The constructor will clobber all bytes including the
-// tail padding. With this type we can check that `std::expected` will set
-// its "has value" flag _after_ the value/error object is constructed.
+// tail padding. With this type we can check that `std::expected` handles
+// the case where the "has value" flag is an overlapping subobject correctly.
+//
+// See https://github.com/llvm/llvm-project/issues/68552 for details.
 template <int c>
 struct TailClobberer {
   constexpr TailClobberer() {

>From bea099d230a4fa5dfa32c88051bbdaf180f0ef3c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 17 Oct 2023 20:12:00 +0200
Subject: [PATCH 10/24] spell out full path to corresponding test file

---
 .../expected/expected.void/observers/has_value.pass.cpp        | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
index 50098d10977b702..06a45ed43018041 100644
--- a/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
@@ -44,7 +44,8 @@ constexpr bool test() {
     assert(!e.has_value());
   }
 
-  // See comments of the corresponding test in "expected.expected".
+  // See comments of the corresponding test in
+  // "expected.expected/observers/has_value.pass.cpp".
   {
     const std::expected<void, TailClobberer<1>> e(std::unexpect);
     // clang-cl does not support [[no_unique_address]] yet.

>From bf28eba0392a151d08d0b0b04761305afbd4930c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 17 Oct 2023 20:14:28 +0200
Subject: [PATCH 11/24] directly construct into union member instead of trying
 to delegate to another constructor

---
 libcxx/include/__expected/expected.h | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 06c9a26c8e3a88f..9d09485d22ae4de 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -881,9 +881,9 @@ class expected {
     template <class _Union>
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
       if (__has_val)
-        std::construct_at(this, std::in_place, std::forward<_Union>(__other).__val_);
+        std::construct_at(std::addressof(__val_), std::forward<_Union>(__other).__val_);
       else
-        std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+        std::construct_at(std::addressof(__unex_), std::forward<_Union>(__other).__unex_);
     }
 
     _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
@@ -929,9 +929,9 @@ class expected {
     template <class _Union>
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
       if (__has_val)
-        std::construct_at(this, std::in_place, std::forward<_Union>(__other).__val_);
+        std::construct_at(std::addressof(__val_), std::forward<_Union>(__other).__val_);
       else
-        std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+        std::construct_at(std::addressof(__unex_), std::forward<_Union>(__other).__unex_);
     }
 
     _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
@@ -1488,9 +1488,9 @@ class expected<_Tp, _Err> {
     template <class _Union>
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
       if (__has_val)
-        std::construct_at(this);
+        std::construct_at(std::addressof(__empty_));
       else
-        std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+        std::construct_at(std::addressof(__unex_), std::forward<_Union>(__other).__unex_);
     }
 
     _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
@@ -1527,9 +1527,9 @@ class expected<_Tp, _Err> {
     template <class _Union>
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
       if (__has_val)
-        std::construct_at(this);
+        std::construct_at(std::addressof(__empty_));
       else
-        std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+        std::construct_at(std::addressof(__unex_), std::forward<_Union>(__other).__unex_);
     }
 
     _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()

>From 5922253fbed8e3c161829860d461819741532b63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 17 Oct 2023 20:24:03 +0200
Subject: [PATCH 12/24] remove unneeded '__empty_t' in union of non-void
 expected

---
 libcxx/include/__expected/expected.h | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 9d09485d22ae4de..f874a3a126a58f3 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -854,12 +854,8 @@ class expected {
   }
 
 private:
-  struct __empty_t {};
-
   template <class _ValueType, class _ErrorType>
   union __union_t {
-    _LIBCPP_HIDE_FROM_ABI constexpr __union_t() {}
-
     template <class... _Args>
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::in_place_t, _Args&&... __args)
         : __val_(std::forward<_Args>(__args)...) {}
@@ -904,7 +900,6 @@ class expected {
   template <class _ValueType, class _ErrorType>
     requires(is_trivially_move_constructible_v<_ValueType> && is_trivially_move_constructible_v<_ErrorType>)
   union __union_t<_ValueType, _ErrorType> {
-    _LIBCPP_HIDE_FROM_ABI constexpr __union_t() : __empty_() {}
     _LIBCPP_HIDE_FROM_ABI constexpr __union_t(const __union_t&) = default;
     _LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(const __union_t&) = default;
 
@@ -943,7 +938,6 @@ class expected {
       requires(!is_trivially_destructible_v<_ValueType> || !is_trivially_destructible_v<_ErrorType>)
     {}
 
-    _LIBCPP_NO_UNIQUE_ADDRESS __empty_t __empty_;
     _LIBCPP_NO_UNIQUE_ADDRESS _ValueType __val_;
     _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
   };

>From 6167b0e718b741c2b8e0a9aece0e9999320967d5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Wed, 18 Oct 2023 03:22:39 +0200
Subject: [PATCH 13/24] make lambdas automatic variables

---
 .../expected/expected.expected/observers/has_value.pass.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
index 3cb46bcf79abee3..0e19da065594644 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
@@ -57,16 +57,14 @@ constexpr bool test() {
   //
   // See https://github.com/llvm/llvm-project/issues/68552 and the linked PR.
   {
-#if !defined(TEST_COMPILER_CLANG) || TEST_CLANG_VER >= 1600
-    static constexpr auto f1 = [] -> std::expected<std::optional<int>, long> { return 0; };
+    auto f1 = [] -> std::expected<std::optional<int>, long> { return 0; };
 
-    static constexpr auto f2 = [] -> std::expected<std::optional<int>, int> {
+    auto f2 = [&f1] -> std::expected<std::optional<int>, int> {
       return f1().transform_error([](auto) { return 0; });
     };
 
     auto e = f2();
     assert(e.has_value());
-#endif
   }
   {
     const std::expected<TailClobberer<0>, bool> e = {};

>From 74b266db78accc3f23e0cc1d96aa7277eeccdb2f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sun, 15 Oct 2023 22:09:51 +0200
Subject: [PATCH 14/24] ensure that expected has no tail padding

---
 libcxx/include/__expected/expected.h          | 20 +++++++++++++++++++
 .../expected.expected/assign/emplace.pass.cpp | 16 +++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 045370a486fae6b..463060702235742 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -18,6 +18,7 @@
 #include <__memory/addressof.h>
 #include <__memory/construct_at.h>
 #include <__type_traits/conjunction.h>
+#include <__type_traits/datasizeof.h>
 #include <__type_traits/disjunction.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/is_assignable.h>
@@ -955,8 +956,17 @@ class expected {
     _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
   };
 
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __calculate_padding() {
+    struct __calc_expected {
+      _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
+      bool __has_val_;
+    };
+    return sizeof(__calc_expected) - __libcpp_datasizeof<__calc_expected>::value;
+  }
+
   _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
   bool __has_val_;
+  char __padding_[__calculate_padding()]{};
 };
 
 template <class _Tp, class _Err>
@@ -1552,8 +1562,18 @@ class expected<_Tp, _Err> {
     _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
   };
 
+
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __calculate_padding() {
+    struct __calc_expected {
+      _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
+      bool __has_val_;
+    };
+    return sizeof(__calc_expected) - __libcpp_datasizeof<__calc_expected>::value;
+  }
+
   _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
   bool __has_val_;
+  char __padding_[__calculate_padding()]{};
 };
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/std/utilities/expected/expected.expected/assign/emplace.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/assign/emplace.pass.cpp
index c62e6289350201a..5be8d2b5fb74e40 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/assign/emplace.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/assign/emplace.pass.cpp
@@ -73,6 +73,22 @@ constexpr bool test() {
     assert(e.value() == 10);
   }
 
+  // Test that `expected` has no tail padding.
+  {
+    struct BoolWithPadding {
+      explicit operator bool() { return b; }
+
+    private:
+      alignas(4) bool b = false;
+    };
+
+    static_assert(sizeof(std::expected<BoolWithPadding, bool>) ==
+                  std::__libcpp_datasizeof<std::expected<BoolWithPadding, bool>>::value);
+
+    static_assert(sizeof(std::expected<void, BoolWithPadding>) ==
+                  std::__libcpp_datasizeof<std::expected<void, BoolWithPadding>>::value);
+  }
+
   return true;
 }
 

>From d16dde504b5672ec59c26465accbd23dc5b555ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Fri, 20 Oct 2023 18:28:34 +0200
Subject: [PATCH 15/24] rename template parameter from "c" to "constant"

Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
 libcxx/test/std/utilities/expected/types.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/utilities/expected/types.h b/libcxx/test/std/utilities/expected/types.h
index e6fd5ac479266ad..697a410cea5fe48 100644
--- a/libcxx/test/std/utilities/expected/types.h
+++ b/libcxx/test/std/utilities/expected/types.h
@@ -110,7 +110,7 @@ struct TrackedMove {
 // the case where the "has value" flag is an overlapping subobject correctly.
 //
 // See https://github.com/llvm/llvm-project/issues/68552 for details.
-template <int c>
+template <int constant>
 struct TailClobberer {
   constexpr TailClobberer() {
     if (!std::is_constant_evaluated()) {

>From 71bc5da89260fd31a284651ccf870d00f8bc41db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Fri, 20 Oct 2023 19:39:15 +0200
Subject: [PATCH 16/24] remove tests from emplace.pass.cpp

---
 .../expected.expected/assign/emplace.pass.cpp    | 16 ----------------
 1 file changed, 16 deletions(-)

diff --git a/libcxx/test/std/utilities/expected/expected.expected/assign/emplace.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/assign/emplace.pass.cpp
index 5be8d2b5fb74e40..c62e6289350201a 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/assign/emplace.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/assign/emplace.pass.cpp
@@ -73,22 +73,6 @@ constexpr bool test() {
     assert(e.value() == 10);
   }
 
-  // Test that `expected` has no tail padding.
-  {
-    struct BoolWithPadding {
-      explicit operator bool() { return b; }
-
-    private:
-      alignas(4) bool b = false;
-    };
-
-    static_assert(sizeof(std::expected<BoolWithPadding, bool>) ==
-                  std::__libcpp_datasizeof<std::expected<BoolWithPadding, bool>>::value);
-
-    static_assert(sizeof(std::expected<void, BoolWithPadding>) ==
-                  std::__libcpp_datasizeof<std::expected<void, BoolWithPadding>>::value);
-  }
-
   return true;
 }
 

>From f07fd5f821e292d49e30cd97ddfc019c8df97474 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Fri, 20 Oct 2023 19:40:18 +0200
Subject: [PATCH 17/24] handle GCC's warning in __libcpp_datasizeof

---
 libcxx/include/__type_traits/datasizeof.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__type_traits/datasizeof.h b/libcxx/include/__type_traits/datasizeof.h
index 5688e3293a69eb5..96404ad15a73376 100644
--- a/libcxx/include/__type_traits/datasizeof.h
+++ b/libcxx/include/__type_traits/datasizeof.h
@@ -51,6 +51,7 @@ struct __libcpp_datasizeof {
   // the use as an extension.
   _LIBCPP_DIAGNOSTIC_PUSH
   _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Winvalid-offsetof")
+  _LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Winvalid-offsetof")
   static const size_t value = offsetof(_FirstPaddingByte<>, __first_padding_byte_);
   _LIBCPP_DIAGNOSTIC_POP
 };

>From a17243c8d78e8ab09d60665ef08087f81e4ffd98 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Fri, 20 Oct 2023 19:53:22 +0200
Subject: [PATCH 18/24] implement different approach to padding calculation

This tries to fix two issues:

- There should be no artificial padding if the parameter type(s) don't
  need it, for example if they have no tail padding
- Prevent the padding array being zero length
---
 libcxx/include/__expected/expected.h | 53 +++++++++++++++++-----------
 1 file changed, 32 insertions(+), 21 deletions(-)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 463060702235742..0728d3af7bb47ca 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -89,6 +89,34 @@ _LIBCPP_HIDE_FROM_ABI void __throw_bad_expected_access(_Arg&& __arg) {
 #  endif
 }
 
+template <size_t __Padding>
+struct __expected_padding {
+  using type = char[__Padding];
+};
+
+template <>
+struct __expected_padding<0> {
+  using type = struct {};
+};
+
+template <class _Union>
+_LIBCPP_HIDE_FROM_ABI constexpr size_t __expected_calculate_padding() {
+  struct __calc_expected {
+    _LIBCPP_NO_UNIQUE_ADDRESS _Union __union_;
+    bool __has_val_;
+  };
+
+  size_t __datasize = __libcpp_datasizeof<__calc_expected>::value;
+  return sizeof(_Union) < __datasize ? 0 : sizeof(_Union) - __datasize;
+}
+
+// An object of this type must be the last member of the `expected` class to
+// ensure `expected`'s datasize is large enough to fit the parameter type(s).
+// It should be value-initialized so it is safe to copy when `expected`'s
+// copy operators are invoked.
+template <typename _Union>
+using __expected_padding_t = __expected_padding<__expected_calculate_padding<_Union>()>::type;
+
 template <class _Tp, class _Err>
 class expected {
   static_assert(
@@ -956,17 +984,9 @@ class expected {
     _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
   };
 
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto __calculate_padding() {
-    struct __calc_expected {
-      _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
-      bool __has_val_;
-    };
-    return sizeof(__calc_expected) - __libcpp_datasizeof<__calc_expected>::value;
-  }
-
   _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
-  bool __has_val_;
-  char __padding_[__calculate_padding()]{};
+  _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __expected_padding_t<__union_t<_Tp, _Err>> __padding_{};
 };
 
 template <class _Tp, class _Err>
@@ -1562,18 +1582,9 @@ class expected<_Tp, _Err> {
     _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
   };
 
-
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto __calculate_padding() {
-    struct __calc_expected {
-      _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
-      bool __has_val_;
-    };
-    return sizeof(__calc_expected) - __libcpp_datasizeof<__calc_expected>::value;
-  }
-
   _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
-  bool __has_val_;
-  char __padding_[__calculate_padding()]{};
+  _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
+  _LIBCPP_NO_UNIQUE_ADDRESS __expected_padding_t<__union_t<_Err>> __padding_{};
 };
 
 _LIBCPP_END_NAMESPACE_STD

>From cc8cb38f9d21262af9225d2644c52f550b0594c0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Fri, 20 Oct 2023 19:56:59 +0200
Subject: [PATCH 19/24] add tests

---
 .../no_unique_address.compile.pass.cpp          | 17 +++++++++++++++++
 .../no_unique_address.compile.pass.cpp          | 15 +++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
index c8c217054ae6c43..06f597d54871ee1 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
@@ -26,6 +26,13 @@ struct B : public A {
   virtual ~B() = default;
 };
 
+struct BoolWithPadding {
+  explicit operator bool() { return b; }
+
+private:
+  alignas(1024) bool b = false;
+};
+
 static_assert(sizeof(std::expected<Empty, Empty>) == sizeof(bool));
 static_assert(sizeof(std::expected<Empty, A>) == 2 * sizeof(int) + alignof(std::expected<Empty, A>));
 static_assert(sizeof(std::expected<Empty, B>) == sizeof(B) + alignof(std::expected<Empty, B>));
@@ -33,3 +40,13 @@ static_assert(sizeof(std::expected<A, Empty>) == 2 * sizeof(int) + alignof(std::
 static_assert(sizeof(std::expected<A, A>) == 2 * sizeof(int) + alignof(std::expected<A, A>));
 static_assert(sizeof(std::expected<B, Empty>) == sizeof(B) + alignof(std::expected<B, Empty>));
 static_assert(sizeof(std::expected<B, B>) == sizeof(B) + alignof(std::expected<B, B>));
+
+// Check that `expected`'s datasize is large enough for the parameter type(s).
+static_assert(sizeof(std::expected<BoolWithPadding, Empty>) ==
+              std::__libcpp_datasizeof<std::expected<BoolWithPadding, Empty>>::value);
+static_assert(sizeof(std::expected<Empty, BoolWithPadding>) ==
+              std::__libcpp_datasizeof<std::expected<Empty, BoolWithPadding>>::value);
+
+// In this case, there should be tail padding in the `expected` because `A`
+// itself does _not_ have tail padding.
+static_assert(sizeof(std::expected<A, A>) > std::__libcpp_datasizeof<std::expected<A, A>>::value);
diff --git a/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
index dc59a6228386bcd..29cf8dff0d2b288 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
@@ -26,6 +26,21 @@ struct B : public A {
   virtual ~B() = default;
 };
 
+struct BoolWithPadding {
+  explicit operator bool() { return b; }
+
+private:
+  alignas(1024) bool b = false;
+};
+
 static_assert(sizeof(std::expected<void, Empty>) == sizeof(bool));
 static_assert(sizeof(std::expected<void, A>) == 2 * sizeof(int) + alignof(std::expected<void, A>));
 static_assert(sizeof(std::expected<void, B>) == sizeof(B) + alignof(std::expected<void, B>));
+
+// Check that `expected`'s datasize is large enough for the parameter type(s).
+static_assert(sizeof(std::expected<void, BoolWithPadding>) ==
+              std::__libcpp_datasizeof<std::expected<void, BoolWithPadding>>::value);
+
+// In this case, there should be tail padding in the `expected` because `A`
+// itself does _not_ have tail padding.
+static_assert(sizeof(std::expected<void, A>) > std::__libcpp_datasizeof<std::expected<void, A>>::value);

>From 6229c978ab245cfa343089ed1bdd3a684f30c427 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Fri, 20 Oct 2023 20:55:20 +0200
Subject: [PATCH 20/24] fix build

---
 libcxx/test/std/utilities/expected/types.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/utilities/expected/types.h b/libcxx/test/std/utilities/expected/types.h
index 697a410cea5fe48..6a7990383ada8b5 100644
--- a/libcxx/test/std/utilities/expected/types.h
+++ b/libcxx/test/std/utilities/expected/types.h
@@ -114,7 +114,7 @@ template <int constant>
 struct TailClobberer {
   constexpr TailClobberer() {
     if (!std::is_constant_evaluated()) {
-      std::memset(this, c, sizeof(*this));
+      std::memset(this, constant, sizeof(*this));
     }
   }
   alignas(2) bool b;

>From 7f3144c38f17edf0be20789e69ee00e277110ec0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Fri, 20 Oct 2023 22:02:20 +0200
Subject: [PATCH 21/24] implement repr/compact_pair approach

---
 libcxx/include/__expected/expected.h          | 417 +++++++++---------
 .../no_unique_address.compile.pass.cpp        |  18 +
 .../no_unique_address.compile.pass.cpp        |  16 +
 3 files changed, 246 insertions(+), 205 deletions(-)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index f874a3a126a58f3..0ae5c36acb1ef30 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -88,6 +88,12 @@ _LIBCPP_HIDE_FROM_ABI void __throw_bad_expected_access(_Arg&& __arg) {
 #  endif
 }
 
+template <class _Union>
+struct __expected_repr {
+    [[no_unique_address]] _Union __union_;
+    [[no_unique_address]] bool __has_val_;
+};
+
 template <class _Tp, class _Err>
 class expected {
   static_assert(
@@ -119,7 +125,7 @@ class expected {
   _LIBCPP_HIDE_FROM_ABI constexpr expected()
     noexcept(is_nothrow_default_constructible_v<_Tp>) // strengthened
     requires is_default_constructible_v<_Tp>
-      : __union_(std::in_place), __has_val_(true) {}
+      : __repr_{.__union_{std::in_place}, .__has_val_ = true} {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
 
@@ -134,7 +140,7 @@ class expected {
     noexcept(is_nothrow_copy_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Err>) // strengthened
     requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err> &&
              !(is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Err>))
-      : __union_(__other.__has_val_, __other.__union_), __has_val_(__other.__has_val_) { }
+      : __repr_{.__union_{__other.__repr_.__has_val_, __other.__repr_.__union_}, .__has_val_ = __other.__repr_.__has_val_} { }
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
     requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err>
@@ -145,7 +151,8 @@ class expected {
     noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_move_constructible_v<_Err>)
     requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err> &&
              !(is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>))
-      : __union_(__other.__has_val_, std::move(__other.__union_)), __has_val_(__other.__has_val_) { }
+      : __repr_{.__union_{__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)},
+                .__has_val_ = __other.__repr_.__has_val_} { }
 
 private:
   template <class _Up, class _OtherErr, class _UfQual, class _OtherErrQual>
@@ -170,12 +177,12 @@ class expected {
   template <class _Func, class... _Args>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
       std::__expected_construct_in_place_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)
-      : __union_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...), __has_val_(true) {}
+      : __repr_{.__union_{__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...}, .__has_val_ = true} {}
 
   template <class _Func, class... _Args>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
       std::__expected_construct_unexpected_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)
-      : __union_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...), __has_val_(false) {}
+      : __repr_{.__union_{__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...}, .__has_val_ = false} {}
 
 public:
   template <class _Up, class _OtherErr>
@@ -185,14 +192,16 @@ class expected {
   expected(const expected<_Up, _OtherErr>& __other)
     noexcept(is_nothrow_constructible_v<_Tp, const _Up&> &&
              is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __union_(__other.__has_val_, __other.__union_), __has_val_(__other.__has_val_) {}
+      : __repr_{.__union_{__other.__repr_.__has_val_, __other.__repr_.__union_},
+                .__has_val_ = __other.__repr_.__has_val_} {}
 
   template <class _Up, class _OtherErr>
     requires __can_convert<_Up, _OtherErr, _Up, _OtherErr>::value
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp> || !is_convertible_v<_OtherErr, _Err>)
   expected(expected<_Up, _OtherErr>&& __other)
     noexcept(is_nothrow_constructible_v<_Tp, _Up> && is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __union_(__other.__has_val_, std::move(__other.__union_)), __has_val_(__other.__has_val_) {}
+      : __repr_{.__union_{__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)},
+                .__has_val_ = __other.__repr_.__has_val_} {}
 
   template <class _Up = _Tp>
     requires(!is_same_v<remove_cvref_t<_Up>, in_place_t> && !is_same_v<expected, remove_cvref_t<_Up>> &&
@@ -200,47 +209,47 @@ class expected {
              (!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_expected<remove_cvref_t<_Up>>::value))
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp>)
       expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
-      : __union_(std::in_place, std::forward<_Up>(__u)), __has_val_(true) {}
+      : __repr_{.__union_{std::in_place, std::forward<_Up>(__u)}, .__has_val_ = true} {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, const _OtherErr&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
   expected(const unexpected<_OtherErr>& __unex)
     noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __union_(std::unexpect, __unex.error()), __has_val_(false) {}
+      : __repr_{.__union_{std::unexpect, __unex.error()}, .__has_val_ = false} {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
   expected(unexpected<_OtherErr>&& __unex)
     noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __union_(std::unexpect, std::move(__unex.error())), __has_val_(false) {}
+      : __repr_{.__union_{std::unexpect, std::move(__unex.error())}, .__has_val_ = false} {}
 
   template <class... _Args>
     requires is_constructible_v<_Tp, _Args...>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Tp, _Args...>) // strengthened
-      : __union_(std::in_place, std::forward<_Args>(__args)...), __has_val_(true) {}
+      : __repr_{.__union_{std::in_place, std::forward<_Args>(__args)...}, .__has_val_ = true} {}
 
   template <class _Up, class... _Args>
     requires is_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
   _LIBCPP_HIDE_FROM_ABI constexpr explicit
   expected(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, _Args...>) // strengthened
-      : __union_(std::in_place, __il, std::forward<_Args>(__args)...), __has_val_(true) {}
+      : __repr_{.__union_{std::in_place, __il, std::forward<_Args>(__args)...}, .__has_val_ = true} {}
 
   template <class... _Args>
     requires is_constructible_v<_Err, _Args...>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
-      : __union_(std::unexpect, std::forward<_Args>(__args)...), __has_val_(false) {}
+      : __repr_{.__union_{std::unexpect, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
 
   template <class _Up, class... _Args>
     requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
   _LIBCPP_HIDE_FROM_ABI constexpr explicit
   expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
-      : __union_(std::unexpect, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
+      : __repr_{.__union_{std::unexpect, __il, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
 
   // [expected.object.dtor], destructor
 
@@ -251,10 +260,10 @@ class expected {
   _LIBCPP_HIDE_FROM_ABI constexpr ~expected()
     requires(!is_trivially_destructible_v<_Tp> || !is_trivially_destructible_v<_Err>)
   {
-    if (__has_val_) {
-      std::destroy_at(std::addressof(__union_.__val_));
+    if (__repr_.__has_val_) {
+      std::destroy_at(std::addressof(__repr_.__union_.__val_));
     } else {
-      std::destroy_at(std::addressof(__union_.__unex_));
+      std::destroy_at(std::addressof(__repr_.__union_.__unex_));
     }
   }
 
@@ -298,17 +307,17 @@ class expected {
              (is_nothrow_move_constructible_v<_Tp> ||
               is_nothrow_move_constructible_v<_Err>))
   {
-    if (__has_val_ && __rhs.__has_val_) {
-      __union_.__val_ = __rhs.__union_.__val_;
-    } else if (__has_val_) {
-      __reinit_expected(__union_.__unex_, __union_.__val_, __rhs.__union_.__unex_);
-    } else if (__rhs.__has_val_) {
-      __reinit_expected(__union_.__val_, __union_.__unex_, __rhs.__union_.__val_);
+    if (__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
+      __repr_.__union_.__val_ = __rhs.__repr_.__union_.__val_;
+    } else if (__repr_.__has_val_) {
+      __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, __rhs.__repr_.__union_.__unex_);
+    } else if (__rhs.__repr_.__has_val_) {
+      __reinit_expected(__repr_.__union_.__val_, __repr_.__union_.__unex_, __rhs.__repr_.__union_.__val_);
     } else {
-      __union_.__unex_ = __rhs.__union_.__unex_;
+      __repr_.__union_.__unex_ = __rhs.__repr_.__union_.__unex_;
     }
     // note: only reached if no exception+rollback was done inside __reinit_expected
-    __has_val_ = __rhs.__has_val_;
+    __repr_.__has_val_ = __rhs.__repr_.__has_val_;
     return *this;
   }
 
@@ -324,17 +333,17 @@ class expected {
              (is_nothrow_move_constructible_v<_Tp> ||
               is_nothrow_move_constructible_v<_Err>))
   {
-    if (__has_val_ && __rhs.__has_val_) {
-      __union_.__val_ = std::move(__rhs.__union_.__val_);
-    } else if (__has_val_) {
-      __reinit_expected(__union_.__unex_, __union_.__val_, std::move(__rhs.__union_.__unex_));
-    } else if (__rhs.__has_val_) {
-      __reinit_expected(__union_.__val_, __union_.__unex_, std::move(__rhs.__union_.__val_));
+    if (__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
+      __repr_.__union_.__val_ = std::move(__rhs.__repr_.__union_.__val_);
+    } else if (__repr_.__has_val_) {
+      __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, std::move(__rhs.__repr_.__union_.__unex_));
+    } else if (__rhs.__repr_.__has_val_) {
+      __reinit_expected(__repr_.__union_.__val_, __repr_.__union_.__unex_, std::move(__rhs.__repr_.__union_.__val_));
     } else {
-      __union_.__unex_ = std::move(__rhs.__union_.__unex_);
+      __repr_.__union_.__unex_ = std::move(__rhs.__repr_.__union_.__unex_);
     }
     // note: only reached if no exception+rollback was done inside __reinit_expected
-    __has_val_ = __rhs.__has_val_;
+    __repr_.__has_val_ = __rhs.__repr_.__has_val_;
     return *this;
   }
 
@@ -348,11 +357,11 @@ class expected {
               is_nothrow_move_constructible_v<_Tp> ||
               is_nothrow_move_constructible_v<_Err>))
   {
-    if (__has_val_) {
-      __union_.__val_ = std::forward<_Up>(__v);
+    if (__repr_.__has_val_) {
+      __repr_.__union_.__val_ = std::forward<_Up>(__v);
     } else {
-      __reinit_expected(__union_.__val_, __union_.__unex_, std::forward<_Up>(__v));
-      __has_val_ = true;
+      __reinit_expected(__repr_.__union_.__val_, __repr_.__union_.__unex_, std::forward<_Up>(__v));
+      __repr_.__has_val_ = true;
     }
     return *this;
   }
@@ -371,11 +380,11 @@ class expected {
   template <class _OtherErr>
     requires(__can_assign_from_unexpected<const _OtherErr&>)
   _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const unexpected<_OtherErr>& __un) {
-    if (__has_val_) {
-      __reinit_expected(__union_.__unex_, __union_.__val_, __un.error());
-      __has_val_ = false;
+    if (__repr_.__has_val_) {
+      __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, __un.error());
+      __repr_.__has_val_ = false;
     } else {
-      __union_.__unex_ = __un.error();
+      __repr_.__union_.__unex_ = __un.error();
     }
     return *this;
   }
@@ -383,11 +392,11 @@ class expected {
   template <class _OtherErr>
     requires(__can_assign_from_unexpected<_OtherErr>)
   _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(unexpected<_OtherErr>&& __un) {
-    if (__has_val_) {
-      __reinit_expected(__union_.__unex_, __union_.__val_, std::move(__un.error()));
-      __has_val_ = false;
+    if (__repr_.__has_val_) {
+      __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, std::move(__un.error()));
+      __repr_.__has_val_ = false;
     } else {
-      __union_.__unex_ = std::move(__un.error());
+      __repr_.__union_.__unex_ = std::move(__un.error());
     }
     return *this;
   }
@@ -395,27 +404,27 @@ class expected {
   template <class... _Args>
     requires is_nothrow_constructible_v<_Tp, _Args...>
   _LIBCPP_HIDE_FROM_ABI constexpr _Tp& emplace(_Args&&... __args) noexcept {
-    if (__has_val_) {
-      std::destroy_at(std::addressof(__union_.__val_));
+    if (__repr_.__has_val_) {
+      std::destroy_at(std::addressof(__repr_.__union_.__val_));
     } else {
-      std::destroy_at(std::addressof(__union_.__unex_));
+      std::destroy_at(std::addressof(__repr_.__union_.__unex_));
     }
-    std::construct_at(std::addressof(__union_.__val_), std::forward<_Args>(__args)...);
-    __has_val_ = true;
-    return *std::addressof(__union_.__val_);
+    std::construct_at(std::addressof(__repr_.__union_.__val_), std::forward<_Args>(__args)...);
+    __repr_.__has_val_ = true;
+    return *std::addressof(__repr_.__union_.__val_);
   }
 
   template <class _Up, class... _Args>
     requires is_nothrow_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
   _LIBCPP_HIDE_FROM_ABI constexpr _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) noexcept {
-    if (__has_val_) {
-      std::destroy_at(std::addressof(__union_.__val_));
+    if (__repr_.__has_val_) {
+      std::destroy_at(std::addressof(__repr_.__union_.__val_));
     } else {
-      std::destroy_at(std::addressof(__union_.__unex_));
+      std::destroy_at(std::addressof(__repr_.__union_.__unex_));
     }
-    std::construct_at(std::addressof(__union_.__val_), __il, std::forward<_Args>(__args)...);
-    __has_val_ = true;
-    return *std::addressof(__union_.__val_);
+    std::construct_at(std::addressof(__repr_.__union_.__val_), __il, std::forward<_Args>(__args)...);
+    __repr_.__has_val_ = true;
+    return *std::addressof(__repr_.__union_.__val_);
   }
 
 
@@ -435,46 +444,46 @@ class expected {
   {
     auto __swap_val_unex_impl = [&](expected& __with_val, expected& __with_err) {
       if constexpr (is_nothrow_move_constructible_v<_Err>) {
-        _Err __tmp(std::move(__with_err.__union_.__unex_));
-        std::destroy_at(std::addressof(__with_err.__union_.__unex_));
+        _Err __tmp(std::move(__with_err.__repr_.__union_.__unex_));
+        std::destroy_at(std::addressof(__with_err.__repr_.__union_.__unex_));
         auto __trans = std::__make_exception_guard([&] {
-          std::construct_at(std::addressof(__with_err.__union_.__unex_), std::move(__tmp));
+          std::construct_at(std::addressof(__with_err.__repr_.__union_.__unex_), std::move(__tmp));
         });
-        std::construct_at(std::addressof(__with_err.__union_.__val_), std::move(__with_val.__union_.__val_));
+        std::construct_at(std::addressof(__with_err.__repr_.__union_.__val_), std::move(__with_val.__repr_.__union_.__val_));
         __trans.__complete();
-        std::destroy_at(std::addressof(__with_val.__union_.__val_));
-        std::construct_at(std::addressof(__with_val.__union_.__unex_), std::move(__tmp));
+        std::destroy_at(std::addressof(__with_val.__repr_.__union_.__val_));
+        std::construct_at(std::addressof(__with_val.__repr_.__union_.__unex_), std::move(__tmp));
       } else {
         static_assert(is_nothrow_move_constructible_v<_Tp>,
                       "To provide strong exception guarantee, Tp has to satisfy `is_nothrow_move_constructible_v` so "
                       "that it can be reverted to the previous state in case an exception is thrown during swap.");
-        _Tp __tmp(std::move(__with_val.__union_.__val_));
-        std::destroy_at(std::addressof(__with_val.__union_.__val_));
+        _Tp __tmp(std::move(__with_val.__repr_.__union_.__val_));
+        std::destroy_at(std::addressof(__with_val.__repr_.__union_.__val_));
         auto __trans = std::__make_exception_guard([&] {
-          std::construct_at(std::addressof(__with_val.__union_.__val_), std::move(__tmp));
+          std::construct_at(std::addressof(__with_val.__repr_.__union_.__val_), std::move(__tmp));
         });
-        std::construct_at(std::addressof(__with_val.__union_.__unex_), std::move(__with_err.__union_.__unex_));
+        std::construct_at(std::addressof(__with_val.__repr_.__union_.__unex_), std::move(__with_err.__repr_.__union_.__unex_));
         __trans.__complete();
-        std::destroy_at(std::addressof(__with_err.__union_.__unex_));
-        std::construct_at(std::addressof(__with_err.__union_.__val_), std::move(__tmp));
+        std::destroy_at(std::addressof(__with_err.__repr_.__union_.__unex_));
+        std::construct_at(std::addressof(__with_err.__repr_.__union_.__val_), std::move(__tmp));
       }
-      __with_val.__has_val_ = false;
-      __with_err.__has_val_ = true;
+      __with_val.__repr_.__has_val_ = false;
+      __with_err.__repr_.__has_val_ = true;
     };
 
-    if (__has_val_) {
-      if (__rhs.__has_val_) {
+    if (__repr_.__has_val_) {
+      if (__rhs.__repr_.__has_val_) {
         using std::swap;
-        swap(__union_.__val_, __rhs.__union_.__val_);
+        swap(__repr_.__union_.__val_, __rhs.__repr_.__union_.__val_);
       } else {
         __swap_val_unex_impl(*this, __rhs);
       }
     } else {
-      if (__rhs.__has_val_) {
+      if (__rhs.__repr_.__has_val_) {
         __swap_val_unex_impl(__rhs, *this);
       } else {
         using std::swap;
-        swap(__union_.__unex_, __rhs.__union_.__unex_);
+        swap(__repr_.__union_.__unex_, __rhs.__repr_.__union_.__unex_);
       }
     }
   }
@@ -488,105 +497,105 @@ class expected {
 
   // [expected.object.obs], observers
   _LIBCPP_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__has_val_, "expected::operator-> requires the expected to contain a value");
-    return std::addressof(__union_.__val_);
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator-> requires the expected to contain a value");
+    return std::addressof(__repr_.__union_.__val_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__has_val_, "expected::operator-> requires the expected to contain a value");
-    return std::addressof(__union_.__val_);
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator-> requires the expected to contain a value");
+    return std::addressof(__repr_.__union_.__val_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__has_val_, "expected::operator* requires the expected to contain a value");
-    return __union_.__val_;
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
+    return __repr_.__union_.__val_;
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__has_val_, "expected::operator* requires the expected to contain a value");
-    return __union_.__val_;
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
+    return __repr_.__union_.__val_;
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__has_val_, "expected::operator* requires the expected to contain a value");
-    return std::move(__union_.__val_);
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
+    return std::move(__repr_.__union_.__val_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__has_val_, "expected::operator* requires the expected to contain a value");
-    return std::move(__union_.__val_);
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
+    return std::move(__repr_.__union_.__val_);
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return __has_val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return __repr_.__has_val_; }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __has_val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __repr_.__has_val_; }
 
   _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& value() const& {
     static_assert(is_copy_constructible_v<_Err>, "error_type has to be copy constructible");
-    if (!__has_val_) {
+    if (!__repr_.__has_val_) {
       std::__throw_bad_expected_access<_Err>(std::as_const(error()));
     }
-    return __union_.__val_;
+    return __repr_.__union_.__val_;
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & {
     static_assert(is_copy_constructible_v<_Err>, "error_type has to be copy constructible");
-    if (!__has_val_) {
+    if (!__repr_.__has_val_) {
       std::__throw_bad_expected_access<_Err>(std::as_const(error()));
     }
-    return __union_.__val_;
+    return __repr_.__union_.__val_;
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& value() const&& {
     static_assert(is_copy_constructible_v<_Err> && is_constructible_v<_Err, decltype(std::move(error()))>,
                   "error_type has to be both copy constructible and constructible from decltype(std::move(error()))");
-    if (!__has_val_) {
+    if (!__repr_.__has_val_) {
       std::__throw_bad_expected_access<_Err>(std::move(error()));
     }
-    return std::move(__union_.__val_);
+    return std::move(__repr_.__union_.__val_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && {
     static_assert(is_copy_constructible_v<_Err> && is_constructible_v<_Err, decltype(std::move(error()))>,
                   "error_type has to be both copy constructible and constructible from decltype(std::move(error()))");
-    if (!__has_val_) {
+    if (!__repr_.__has_val_) {
       std::__throw_bad_expected_access<_Err>(std::move(error()));
     }
-    return std::move(__union_.__val_);
+    return std::move(__repr_.__union_.__val_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr const _Err& error() const& noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
-    return __union_.__unex_;
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+    return __repr_.__union_.__unex_;
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Err& error() & noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
-    return __union_.__unex_;
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+    return __repr_.__union_.__unex_;
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr const _Err&& error() const&& noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
-    return std::move(__union_.__unex_);
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+    return std::move(__repr_.__union_.__unex_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Err&& error() && noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
-    return std::move(__union_.__unex_);
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+    return std::move(__repr_.__union_.__unex_);
   }
 
   template <class _Up>
   _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 __has_val_ ? __union_.__val_ : static_cast<_Tp>(std::forward<_Up>(__v));
+    return __repr_.__has_val_ ? __repr_.__union_.__val_ : static_cast<_Tp>(std::forward<_Up>(__v));
   }
 
   template <class _Up>
   _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");
-    return __has_val_ ? std::move(__union_.__val_) : static_cast<_Tp>(std::forward<_Up>(__v));
+    return __repr_.__has_val_ ? std::move(__repr_.__union_.__val_) : static_cast<_Tp>(std::forward<_Up>(__v));
   }
 
   template <class _Up = _Err>
@@ -616,7 +625,7 @@ class expected {
     static_assert(is_same_v<typename _Up::error_type, _Err>,
                   "The result of f(**this) must have the same error_type as this expected");
     if (has_value()) {
-      return std::invoke(std::forward<_Func>(__f), __union_.__val_);
+      return std::invoke(std::forward<_Func>(__f), __repr_.__union_.__val_);
     }
     return _Up(unexpect, error());
   }
@@ -629,7 +638,7 @@ class expected {
     static_assert(is_same_v<typename _Up::error_type, _Err>,
                   "The result of f(**this) must have the same error_type as this expected");
     if (has_value()) {
-      return std::invoke(std::forward<_Func>(__f), __union_.__val_);
+      return std::invoke(std::forward<_Func>(__f), __repr_.__union_.__val_);
     }
     return _Up(unexpect, error());
   }
@@ -643,7 +652,7 @@ class expected {
     static_assert(is_same_v<typename _Up::error_type, _Err>,
                   "The result of f(std::move(**this)) must have the same error_type as this expected");
     if (has_value()) {
-      return std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_));
+      return std::invoke(std::forward<_Func>(__f), std::move(__repr_.__union_.__val_));
     }
     return _Up(unexpect, std::move(error()));
   }
@@ -657,7 +666,7 @@ class expected {
     static_assert(is_same_v<typename _Up::error_type, _Err>,
                   "The result of f(std::move(**this)) must have the same error_type as this expected");
     if (has_value()) {
-      return std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_));
+      return std::invoke(std::forward<_Func>(__f), std::move(__repr_.__union_.__val_));
     }
     return _Up(unexpect, std::move(error()));
   }
@@ -670,7 +679,7 @@ class expected {
     static_assert(is_same_v<typename _Gp::value_type, _Tp>,
                   "The result of f(error()) must have the same value_type as this expected");
     if (has_value()) {
-      return _Gp(in_place, __union_.__val_);
+      return _Gp(in_place, __repr_.__union_.__val_);
     }
     return std::invoke(std::forward<_Func>(__f), error());
   }
@@ -683,7 +692,7 @@ class expected {
     static_assert(is_same_v<typename _Gp::value_type, _Tp>,
                   "The result of f(error()) must have the same value_type as this expected");
     if (has_value()) {
-      return _Gp(in_place, __union_.__val_);
+      return _Gp(in_place, __repr_.__union_.__val_);
     }
     return std::invoke(std::forward<_Func>(__f), error());
   }
@@ -697,7 +706,7 @@ class expected {
     static_assert(is_same_v<typename _Gp::value_type, _Tp>,
                   "The result of f(std::move(error())) must have the same value_type as this expected");
     if (has_value()) {
-      return _Gp(in_place, std::move(__union_.__val_));
+      return _Gp(in_place, std::move(__repr_.__union_.__val_));
     }
     return std::invoke(std::forward<_Func>(__f), std::move(error()));
   }
@@ -711,7 +720,7 @@ class expected {
     static_assert(is_same_v<typename _Gp::value_type, _Tp>,
                   "The result of f(std::move(error())) must have the same value_type as this expected");
     if (has_value()) {
-      return _Gp(in_place, std::move(__union_.__val_));
+      return _Gp(in_place, std::move(__repr_.__union_.__val_));
     }
     return std::invoke(std::forward<_Func>(__f), std::move(error()));
   }
@@ -724,9 +733,9 @@ class expected {
       return expected<_Up, _Err>(unexpect, error());
     }
     if constexpr (!is_void_v<_Up>) {
-      return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), __union_.__val_);
+      return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), __repr_.__union_.__val_);
     } else {
-      std::invoke(std::forward<_Func>(__f), __union_.__val_);
+      std::invoke(std::forward<_Func>(__f), __repr_.__union_.__val_);
       return expected<_Up, _Err>();
     }
   }
@@ -739,9 +748,9 @@ class expected {
       return expected<_Up, _Err>(unexpect, error());
     }
     if constexpr (!is_void_v<_Up>) {
-      return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), __union_.__val_);
+      return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), __repr_.__union_.__val_);
     } else {
-      std::invoke(std::forward<_Func>(__f), __union_.__val_);
+      std::invoke(std::forward<_Func>(__f), __repr_.__union_.__val_);
       return expected<_Up, _Err>();
     }
   }
@@ -755,9 +764,9 @@ class expected {
     }
     if constexpr (!is_void_v<_Up>) {
       return expected<_Up, _Err>(
-          __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(__union_.__val_));
+          __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(__repr_.__union_.__val_));
     } else {
-      std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_));
+      std::invoke(std::forward<_Func>(__f), std::move(__repr_.__union_.__val_));
       return expected<_Up, _Err>();
     }
   }
@@ -771,9 +780,9 @@ class expected {
     }
     if constexpr (!is_void_v<_Up>) {
       return expected<_Up, _Err>(
-          __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(__union_.__val_));
+          __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(__repr_.__union_.__val_));
     } else {
-      std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_));
+      std::invoke(std::forward<_Func>(__f), std::move(__repr_.__union_.__val_));
       return expected<_Up, _Err>();
     }
   }
@@ -785,7 +794,7 @@ class expected {
     static_assert(__valid_std_unexpected<_Gp>::value,
                   "The result of f(error()) must be a valid template argument for unexpected");
     if (has_value()) {
-      return expected<_Tp, _Gp>(in_place, __union_.__val_);
+      return expected<_Tp, _Gp>(in_place, __repr_.__union_.__val_);
     }
     return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error());
   }
@@ -797,7 +806,7 @@ class expected {
     static_assert(__valid_std_unexpected<_Gp>::value,
                   "The result of f(error()) must be a valid template argument for unexpected");
     if (has_value()) {
-      return expected<_Tp, _Gp>(in_place, __union_.__val_);
+      return expected<_Tp, _Gp>(in_place, __repr_.__union_.__val_);
     }
     return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error());
   }
@@ -809,7 +818,7 @@ class expected {
     static_assert(__valid_std_unexpected<_Gp>::value,
                   "The result of f(std::move(error())) must be a valid template argument for unexpected");
     if (has_value()) {
-      return expected<_Tp, _Gp>(in_place, std::move(__union_.__val_));
+      return expected<_Tp, _Gp>(in_place, std::move(__repr_.__union_.__val_));
     }
     return expected<_Tp, _Gp>(
         __expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error()));
@@ -822,7 +831,7 @@ class expected {
     static_assert(__valid_std_unexpected<_Gp>::value,
                   "The result of f(std::move(error())) must be a valid template argument for unexpected");
     if (has_value()) {
-      return expected<_Tp, _Gp>(in_place, std::move(__union_.__val_));
+      return expected<_Tp, _Gp>(in_place, std::move(__repr_.__union_.__val_));
     }
     return expected<_Tp, _Gp>(
         __expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error()));
@@ -832,25 +841,25 @@ class expected {
   template <class _T2, class _E2>
     requires(!is_void_v<_T2>)
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) {
-    if (__x.__has_val_ != __y.__has_val_) {
+    if (__x.__repr_.__has_val_ != __y.__repr_.__has_val_) {
       return false;
     } else {
-      if (__x.__has_val_) {
-        return __x.__union_.__val_ == __y.__union_.__val_;
+      if (__x.__repr_.__has_val_) {
+        return __x.__repr_.__union_.__val_ == __y.__repr_.__union_.__val_;
       } else {
-        return __x.__union_.__unex_ == __y.__union_.__unex_;
+        return __x.__repr_.__union_.__unex_ == __y.__repr_.__union_.__unex_;
       }
     }
   }
 
   template <class _T2>
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v) {
-    return __x.__has_val_ && static_cast<bool>(__x.__union_.__val_ == __v);
+    return __x.__repr_.__has_val_ && static_cast<bool>(__x.__repr_.__union_.__val_ == __v);
   }
 
   template <class _E2>
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __e) {
-    return !__x.__has_val_ && static_cast<bool>(__x.__union_.__unex_ == __e.error());
+    return !__x.__repr_.__has_val_ && static_cast<bool>(__x.__repr_.__union_.__unex_ == __e.error());
   }
 
 private:
@@ -942,8 +951,7 @@ class expected {
     _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
   };
 
-  _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
-  bool __has_val_;
+  __expected_repr<__union_t<_Tp, _Err>> __repr_;
 };
 
 template <class _Tp, class _Err>
@@ -974,7 +982,7 @@ class expected<_Tp, _Err> {
   using rebind = expected<_Up, error_type>;
 
   // [expected.void.ctor], constructors
-  _LIBCPP_HIDE_FROM_ABI constexpr expected() noexcept : __has_val_(true) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr expected() noexcept : __repr_{.__union_{}, .__has_val_ = true} {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
 
@@ -985,7 +993,7 @@ class expected<_Tp, _Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr expected(const expected& __rhs)
     noexcept(is_nothrow_copy_constructible_v<_Err>) // strengthened
     requires(is_copy_constructible_v<_Err> && !is_trivially_copy_constructible_v<_Err>)
-      : __union_(__rhs.__has_val_, __rhs.__union_), __has_val_(__rhs.__has_val_) {}
+      : __repr_{.__union_{__rhs.__repr_.__has_val_, __rhs.__repr_.__union_}, .__has_val_ = __rhs.__repr_.__has_val_} {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
     requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
@@ -994,61 +1002,61 @@ class expected<_Tp, _Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr expected(expected&& __rhs)
     noexcept(is_nothrow_move_constructible_v<_Err>)
     requires(is_move_constructible_v<_Err> && !is_trivially_move_constructible_v<_Err>)
-      : __union_(__rhs.__has_val_, std::move(__rhs.__union_)), __has_val_(__rhs.__has_val_) {}
+      : __repr_{.__union_{__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)}, .__has_val_ = __rhs.__repr_.__has_val_} {}
 
   template <class _Up, class _OtherErr>
     requires __can_convert<_Up, _OtherErr, const _OtherErr&>::value
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
   expected(const expected<_Up, _OtherErr>& __rhs)
     noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __union_(__rhs.__has_val_, __rhs.__union_), __has_val_(__rhs.__has_val_) {}
+      : __repr_{.__union_{__rhs.__repr_.__has_val_, __rhs.__repr_.__union_}, .__has_val_ = __rhs.__repr_.__has_val_} {}
 
   template <class _Up, class _OtherErr>
     requires __can_convert<_Up, _OtherErr, _OtherErr>::value
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
   expected(expected<_Up, _OtherErr>&& __rhs)
     noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __union_(__rhs.__has_val_, std::move(__rhs.__union_)), __has_val_(__rhs.__has_val_) {}
+      : __repr_{.__union_{__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)}, .__has_val_ = __rhs.__repr_.__has_val_} {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, const _OtherErr&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
   expected(const unexpected<_OtherErr>& __unex)
     noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __union_(std::unexpect, __unex.error()), __has_val_(false) {}
+      : __repr_{.__union_{std::unexpect, __unex.error()}, .__has_val_ = false} {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
   expected(unexpected<_OtherErr>&& __unex)
     noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __union_(std::unexpect, std::move(__unex.error())), __has_val_(false) {}
+      : __repr_{.__union_{std::unexpect, std::move(__unex.error())}, .__has_val_ = false} {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __has_val_(true) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __repr_{.__union_{}, .__has_val_ = true} {}
 
   template <class... _Args>
     requires is_constructible_v<_Err, _Args...>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
-      : __union_(std::unexpect, std::forward<_Args>(__args)...), __has_val_(false) {}
+      : __repr_{.__union_{std::unexpect, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
 
   template <class _Up, class... _Args>
     requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
-      : __union_(std::unexpect, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
+      : __repr_{.__union_{std::unexpect, __il, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
 
 private:
   template <class _Func>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(__expected_construct_in_place_from_invoke_tag, _Func&& __f)
-      : __has_val_(true) {
+      : __repr_{.__union_{}, .__has_val_ = true} {
     std::invoke(std::forward<_Func>(__f));
   }
 
   template <class _Func, class... _Args>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
       __expected_construct_unexpected_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)
-      : __union_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...), __has_val_(false) {}
+      : __repr_{.__union_{__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...}, .__has_val_ = false} {}
 
 public:
   // [expected.void.dtor], destructor
@@ -1060,8 +1068,8 @@ class expected<_Tp, _Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr ~expected()
     requires(!is_trivially_destructible_v<_Err>)
   {
-    if (!__has_val_) {
-      std::destroy_at(std::addressof(__union_.__unex_));
+    if (!__repr_.__has_val_) {
+      std::destroy_at(std::addressof(__repr_.__union_.__unex_));
     }
   }
 
@@ -1073,17 +1081,17 @@ class expected<_Tp, _Err> {
     noexcept(is_nothrow_copy_assignable_v<_Err> && is_nothrow_copy_constructible_v<_Err>) // strengthened
     requires(is_copy_assignable_v<_Err> && is_copy_constructible_v<_Err>)
   {
-    if (__has_val_) {
-      if (!__rhs.__has_val_) {
-        std::construct_at(std::addressof(__union_.__unex_), __rhs.__union_.__unex_);
-        __has_val_ = false;
+    if (__repr_.__has_val_) {
+      if (!__rhs.__repr_.__has_val_) {
+        std::construct_at(std::addressof(__repr_.__union_.__unex_), __rhs.__repr_.__union_.__unex_);
+        __repr_.__has_val_ = false;
       }
     } else {
-      if (__rhs.__has_val_) {
-        std::destroy_at(std::addressof(__union_.__unex_));
-        __has_val_ = true;
+      if (__rhs.__repr_.__has_val_) {
+        std::destroy_at(std::addressof(__repr_.__union_.__unex_));
+        __repr_.__has_val_ = true;
       } else {
-        __union_.__unex_ = __rhs.__union_.__unex_;
+        __repr_.__union_.__unex_ = __rhs.__repr_.__union_.__unex_;
       }
     }
     return *this;
@@ -1097,17 +1105,17 @@ class expected<_Tp, _Err> {
     requires(is_move_assignable_v<_Err> &&
              is_move_constructible_v<_Err>)
   {
-    if (__has_val_) {
-      if (!__rhs.__has_val_) {
-        std::construct_at(std::addressof(__union_.__unex_), std::move(__rhs.__union_.__unex_));
-        __has_val_ = false;
+    if (__repr_.__has_val_) {
+      if (!__rhs.__repr_.__has_val_) {
+        std::construct_at(std::addressof(__repr_.__union_.__unex_), std::move(__rhs.__repr_.__union_.__unex_));
+        __repr_.__has_val_ = false;
       }
     } else {
-      if (__rhs.__has_val_) {
-        std::destroy_at(std::addressof(__union_.__unex_));
-        __has_val_ = true;
+      if (__rhs.__repr_.__has_val_) {
+        std::destroy_at(std::addressof(__repr_.__union_.__unex_));
+        __repr_.__has_val_ = true;
       } else {
-        __union_.__unex_ = std::move(__rhs.__union_.__unex_);
+        __repr_.__union_.__unex_ = std::move(__rhs.__repr_.__union_.__unex_);
       }
     }
     return *this;
@@ -1116,11 +1124,11 @@ class expected<_Tp, _Err> {
   template <class _OtherErr>
     requires(is_constructible_v<_Err, const _OtherErr&> && is_assignable_v<_Err&, const _OtherErr&>)
   _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const unexpected<_OtherErr>& __un) {
-    if (__has_val_) {
-      std::construct_at(std::addressof(__union_.__unex_), __un.error());
-      __has_val_ = false;
+    if (__repr_.__has_val_) {
+      std::construct_at(std::addressof(__repr_.__union_.__unex_), __un.error());
+      __repr_.__has_val_ = false;
     } else {
-      __union_.__unex_ = __un.error();
+      __repr_.__union_.__unex_ = __un.error();
     }
     return *this;
   }
@@ -1128,19 +1136,19 @@ class expected<_Tp, _Err> {
   template <class _OtherErr>
     requires(is_constructible_v<_Err, _OtherErr> && is_assignable_v<_Err&, _OtherErr>)
   _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(unexpected<_OtherErr>&& __un) {
-    if (__has_val_) {
-      std::construct_at(std::addressof(__union_.__unex_), std::move(__un.error()));
-      __has_val_ = false;
+    if (__repr_.__has_val_) {
+      std::construct_at(std::addressof(__repr_.__union_.__unex_), std::move(__un.error()));
+      __repr_.__has_val_ = false;
     } else {
-      __union_.__unex_ = std::move(__un.error());
+      __repr_.__union_.__unex_ = std::move(__un.error());
     }
     return *this;
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr void emplace() noexcept {
-    if (!__has_val_) {
-      std::destroy_at(std::addressof(__union_.__unex_));
-      __has_val_ = true;
+    if (!__repr_.__has_val_) {
+      std::destroy_at(std::addressof(__repr_.__union_.__unex_));
+      __repr_.__has_val_ = true;
     }
   }
 
@@ -1150,22 +1158,22 @@ class expected<_Tp, _Err> {
     requires(is_swappable_v<_Err> && is_move_constructible_v<_Err>)
   {
     auto __swap_val_unex_impl = [&](expected& __with_val, expected& __with_err) {
-      std::construct_at(std::addressof(__with_val.__union_.__unex_), std::move(__with_err.__union_.__unex_));
-      std::destroy_at(std::addressof(__with_err.__union_.__unex_));
-      __with_val.__has_val_ = false;
-      __with_err.__has_val_ = true;
+      std::construct_at(std::addressof(__with_val.__repr_.__union_.__unex_), std::move(__with_err.__repr_.__union_.__unex_));
+      std::destroy_at(std::addressof(__with_err.__repr_.__union_.__unex_));
+      __with_val.__repr_.__has_val_ = false;
+      __with_err.__repr_.__has_val_ = true;
     };
 
-    if (__has_val_) {
-      if (!__rhs.__has_val_) {
+    if (__repr_.__has_val_) {
+      if (!__rhs.__repr_.__has_val_) {
         __swap_val_unex_impl(*this, __rhs);
       }
     } else {
-      if (__rhs.__has_val_) {
+      if (__rhs.__repr_.__has_val_) {
         __swap_val_unex_impl(__rhs, *this);
       } else {
         using std::swap;
-        swap(__union_.__unex_, __rhs.__union_.__unex_);
+        swap(__repr_.__union_.__unex_, __rhs.__repr_.__union_.__unex_);
       }
     }
   }
@@ -1178,44 +1186,44 @@ class expected<_Tp, _Err> {
   }
 
   // [expected.void.obs], observers
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return __has_val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return __repr_.__has_val_; }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __has_val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __repr_.__has_val_; }
 
   _LIBCPP_HIDE_FROM_ABI constexpr void operator*() const noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__has_val_, "expected::operator* requires the expected to contain a value");
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr void value() const& {
-    if (!__has_val_) {
-      std::__throw_bad_expected_access<_Err>(__union_.__unex_);
+    if (!__repr_.__has_val_) {
+      std::__throw_bad_expected_access<_Err>(__repr_.__union_.__unex_);
     }
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr void value() && {
-    if (!__has_val_) {
-      std::__throw_bad_expected_access<_Err>(std::move(__union_.__unex_));
+    if (!__repr_.__has_val_) {
+      std::__throw_bad_expected_access<_Err>(std::move(__repr_.__union_.__unex_));
     }
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr const _Err& error() const& noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
-    return __union_.__unex_;
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+    return __repr_.__union_.__unex_;
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Err& error() & noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
-    return __union_.__unex_;
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+    return __repr_.__union_.__unex_;
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr const _Err&& error() const&& noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
-    return std::move(__union_.__unex_);
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+    return std::move(__repr_.__union_.__unex_);
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Err&& error() && noexcept {
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
-    return std::move(__union_.__unex_);
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+    return std::move(__repr_.__union_.__unex_);
   }
 
   template <class _Up = _Err>
@@ -1451,16 +1459,16 @@ class expected<_Tp, _Err> {
   template <class _T2, class _E2>
     requires is_void_v<_T2>
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) {
-    if (__x.__has_val_ != __y.__has_val_) {
+    if (__x.__repr_.__has_val_ != __y.__repr_.__has_val_) {
       return false;
     } else {
-      return __x.__has_val_ || static_cast<bool>(__x.__union_.__unex_ == __y.__union_.__unex_);
+      return __x.__repr_.__has_val_ || static_cast<bool>(__x.__repr_.__union_.__unex_ == __y.__repr_.__union_.__unex_);
     }
   }
 
   template <class _E2>
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y) {
-    return !__x.__has_val_ && static_cast<bool>(__x.__union_.__unex_ == __y.error());
+    return !__x.__repr_.__has_val_ && static_cast<bool>(__x.__repr_.__union_.__unex_ == __y.error());
   }
 
 private:
@@ -1539,8 +1547,7 @@ class expected<_Tp, _Err> {
     _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
   };
 
-  _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
-  bool __has_val_;
+  __expected_repr<__union_t<_Err>> __repr_;
 };
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
index c8c217054ae6c43..80209db9045b506 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
@@ -13,6 +13,7 @@
 // test [[no_unique_address]] is applied to the union
 
 #include <expected>
+#include <memory>
 
 struct Empty {};
 
@@ -26,6 +27,13 @@ struct B : public A {
   virtual ~B() = default;
 };
 
+struct BoolWithPadding {
+  explicit operator bool() { return b; }
+
+private:
+  alignas(1024) bool b = false;
+};
+
 static_assert(sizeof(std::expected<Empty, Empty>) == sizeof(bool));
 static_assert(sizeof(std::expected<Empty, A>) == 2 * sizeof(int) + alignof(std::expected<Empty, A>));
 static_assert(sizeof(std::expected<Empty, B>) == sizeof(B) + alignof(std::expected<Empty, B>));
@@ -33,3 +41,13 @@ static_assert(sizeof(std::expected<A, Empty>) == 2 * sizeof(int) + alignof(std::
 static_assert(sizeof(std::expected<A, A>) == 2 * sizeof(int) + alignof(std::expected<A, A>));
 static_assert(sizeof(std::expected<B, Empty>) == sizeof(B) + alignof(std::expected<B, Empty>));
 static_assert(sizeof(std::expected<B, B>) == sizeof(B) + alignof(std::expected<B, B>));
+
+// Check that `expected`'s datasize is large enough for the parameter type(s).
+static_assert(sizeof(std::expected<BoolWithPadding, Empty>) ==
+              std::__libcpp_datasizeof<std::expected<BoolWithPadding, Empty>>::value);
+static_assert(sizeof(std::expected<Empty, BoolWithPadding>) ==
+              std::__libcpp_datasizeof<std::expected<Empty, BoolWithPadding>>::value);
+
+// In this case, there should be tail padding in the `expected` because `A`
+// itself does _not_ have tail padding.
+static_assert(sizeof(std::expected<A, A>) > std::__libcpp_datasizeof<std::expected<A, A>>::value);
diff --git a/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
index dc59a6228386bcd..db2e4259eb8c797 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
@@ -13,6 +13,7 @@
 // test [[no_unique_address]] is applied to the union
 
 #include <expected>
+#include <memory>
 
 struct Empty {};
 
@@ -26,6 +27,21 @@ struct B : public A {
   virtual ~B() = default;
 };
 
+struct BoolWithPadding {
+  explicit operator bool() { return b; }
+
+private:
+  alignas(1024) bool b = false;
+};
+
 static_assert(sizeof(std::expected<void, Empty>) == sizeof(bool));
 static_assert(sizeof(std::expected<void, A>) == 2 * sizeof(int) + alignof(std::expected<void, A>));
 static_assert(sizeof(std::expected<void, B>) == sizeof(B) + alignof(std::expected<void, B>));
+
+// Check that `expected`'s datasize is large enough for the parameter type(s).
+static_assert(sizeof(std::expected<void, BoolWithPadding>) ==
+              std::__libcpp_datasizeof<std::expected<void, BoolWithPadding>>::value);
+
+// In this case, there should be tail padding in the `expected` because `A`
+// itself does _not_ have tail padding.
+static_assert(sizeof(std::expected<void, A>) > std::__libcpp_datasizeof<std::expected<void, A>>::value);

>From dd658fbec08f7a7a68fa8f51615d557804a9f947 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 10:52:00 +0200
Subject: [PATCH 22/24] transparently replace whole 'repr' struct on mutating
 operations

---
 libcxx/include/__expected/expected.h          | 208 ++++++++++--------
 .../no_unique_address.compile.pass.cpp        |   3 +-
 .../no_unique_address.compile.pass.cpp        |   3 +-
 3 files changed, 125 insertions(+), 89 deletions(-)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 9eaa20c30937d33..dff31ef3fccc1d8 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -89,12 +89,6 @@ _LIBCPP_HIDE_FROM_ABI void __throw_bad_expected_access(_Arg&& __arg) {
 #  endif
 }
 
-template <class _Union>
-struct __expected_repr {
-    [[no_unique_address]] _Union __union_;
-    [[no_unique_address]] bool __has_val_;
-};
-
 template <class _Tp, class _Err>
 class expected {
   static_assert(
@@ -126,7 +120,7 @@ class expected {
   _LIBCPP_HIDE_FROM_ABI constexpr expected()
     noexcept(is_nothrow_default_constructible_v<_Tp>) // strengthened
     requires is_default_constructible_v<_Tp>
-      : __repr_{.__union_{std::in_place}, .__has_val_ = true} {}
+      : __repr_(std::in_place) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
 
@@ -141,7 +135,7 @@ class expected {
     noexcept(is_nothrow_copy_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Err>) // strengthened
     requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err> &&
              !(is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Err>))
-      : __repr_{.__union_{__other.__repr_.__has_val_, __other.__repr_.__union_}, .__has_val_ = __other.__repr_.__has_val_} { }
+      : __repr_(__other.__repr_.__has_val_, __other.__repr_.__union_) { }
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
     requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err>
@@ -152,8 +146,7 @@ class expected {
     noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_move_constructible_v<_Err>)
     requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err> &&
              !(is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>))
-      : __repr_{.__union_{__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)},
-                .__has_val_ = __other.__repr_.__has_val_} { }
+      : __repr_(__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)) { }
 
 private:
   template <class _Up, class _OtherErr, class _UfQual, class _OtherErrQual>
@@ -178,12 +171,12 @@ class expected {
   template <class _Func, class... _Args>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
       std::__expected_construct_in_place_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)
-      : __repr_{.__union_{__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...}, .__has_val_ = true} {}
+      : __repr_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...) {}
 
   template <class _Func, class... _Args>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
       std::__expected_construct_unexpected_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)
-      : __repr_{.__union_{__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...}, .__has_val_ = false} {}
+      : __repr_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...) {}
 
 public:
   template <class _Up, class _OtherErr>
@@ -193,16 +186,14 @@ class expected {
   expected(const expected<_Up, _OtherErr>& __other)
     noexcept(is_nothrow_constructible_v<_Tp, const _Up&> &&
              is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __repr_{.__union_{__other.__repr_.__has_val_, __other.__repr_.__union_},
-                .__has_val_ = __other.__repr_.__has_val_} {}
+      : __repr_(__other.__repr_.__has_val_, __other.__repr_.__union_) {}
 
   template <class _Up, class _OtherErr>
     requires __can_convert<_Up, _OtherErr, _Up, _OtherErr>::value
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp> || !is_convertible_v<_OtherErr, _Err>)
   expected(expected<_Up, _OtherErr>&& __other)
     noexcept(is_nothrow_constructible_v<_Tp, _Up> && is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __repr_{.__union_{__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)},
-                .__has_val_ = __other.__repr_.__has_val_} {}
+      : __repr_(__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)) {}
 
   template <class _Up = _Tp>
     requires(!is_same_v<remove_cvref_t<_Up>, in_place_t> && !is_same_v<expected, remove_cvref_t<_Up>> &&
@@ -210,84 +201,72 @@ class expected {
              (!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_expected<remove_cvref_t<_Up>>::value))
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp>)
       expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
-      : __repr_{.__union_{std::in_place, std::forward<_Up>(__u)}, .__has_val_ = true} {}
+      : __repr_(std::in_place, std::forward<_Up>(__u)) {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, const _OtherErr&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
   expected(const unexpected<_OtherErr>& __unex)
     noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __repr_{.__union_{std::unexpect, __unex.error()}, .__has_val_ = false} {}
+      : __repr_(std::unexpect, __unex.error()) {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
   expected(unexpected<_OtherErr>&& __unex)
     noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __repr_{.__union_{std::unexpect, std::move(__unex.error())}, .__has_val_ = false} {}
+      : __repr_(std::unexpect, std::move(__unex.error())) {}
 
   template <class... _Args>
     requires is_constructible_v<_Tp, _Args...>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Tp, _Args...>) // strengthened
-      : __repr_{.__union_{std::in_place, std::forward<_Args>(__args)...}, .__has_val_ = true} {}
+      : __repr_(std::in_place, std::forward<_Args>(__args)...) {}
 
   template <class _Up, class... _Args>
     requires is_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
   _LIBCPP_HIDE_FROM_ABI constexpr explicit
   expected(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, _Args...>) // strengthened
-      : __repr_{.__union_{std::in_place, __il, std::forward<_Args>(__args)...}, .__has_val_ = true} {}
+      : __repr_(std::in_place, __il, std::forward<_Args>(__args)...) {}
 
   template <class... _Args>
     requires is_constructible_v<_Err, _Args...>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
-      : __repr_{.__union_{std::unexpect, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
+      : __repr_(std::unexpect, std::forward<_Args>(__args)...) {}
 
   template <class _Up, class... _Args>
     requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
   _LIBCPP_HIDE_FROM_ABI constexpr explicit
   expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
-      : __repr_{.__union_{std::unexpect, __il, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
+      : __repr_(std::unexpect, __il, std::forward<_Args>(__args)...) {}
 
   // [expected.object.dtor], destructor
 
-  _LIBCPP_HIDE_FROM_ABI constexpr ~expected()
-    requires(is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Err>)
-  = default;
-
-  _LIBCPP_HIDE_FROM_ABI constexpr ~expected()
-    requires(!is_trivially_destructible_v<_Tp> || !is_trivially_destructible_v<_Err>)
-  {
-    if (__repr_.__has_val_) {
-      std::destroy_at(std::addressof(__repr_.__union_.__val_));
-    } else {
-      std::destroy_at(std::addressof(__repr_.__union_.__unex_));
-    }
-  }
+  _LIBCPP_HIDE_FROM_ABI constexpr ~expected() = default;
 
 private:
-  template <class _T1, class _T2, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI static constexpr void __reinit_expected(_T1& __newval, _T2& __oldval, _Args&&... __args) {
+  template <class _Tag, class _OtherTag, class _T1, class _T2, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __reinit_expected(_T2& __oldval, _Args&&... __args) {
     if constexpr (is_nothrow_constructible_v<_T1, _Args...>) {
-      std::destroy_at(std::addressof(__oldval));
-      std::construct_at(std::addressof(__newval), std::forward<_Args>(__args)...);
+      std::destroy_at(&__repr_);
+      std::construct_at(&__repr_, _Tag{}, std::forward<_Args>(__args)...);
     } else if constexpr (is_nothrow_move_constructible_v<_T1>) {
       _T1 __tmp(std::forward<_Args>(__args)...);
-      std::destroy_at(std::addressof(__oldval));
-      std::construct_at(std::addressof(__newval), std::move(__tmp));
+      std::destroy_at(&__repr_);
+      std::construct_at(&__repr_, _Tag{}, std::move(__tmp));
     } else {
       static_assert(
           is_nothrow_move_constructible_v<_T2>,
           "To provide strong exception guarantee, T2 has to satisfy `is_nothrow_move_constructible_v` so that it can "
           "be reverted to the previous state in case an exception is thrown during the assignment.");
       _T2 __tmp(std::move(__oldval));
-      std::destroy_at(std::addressof(__oldval));
+      std::destroy_at(&__repr_);
       auto __trans =
-          std::__make_exception_guard([&] { std::construct_at(std::addressof(__oldval), std::move(__tmp)); });
-      std::construct_at(std::addressof(__newval), std::forward<_Args>(__args)...);
+          std::__make_exception_guard([&] { std::construct_at(&__repr_, _OtherTag{}, std::move(__tmp)); });
+      std::construct_at(&__repr_, _Tag{}, std::forward<_Args>(__args)...);
       __trans.__complete();
     }
   }
@@ -311,14 +290,14 @@ class expected {
     if (__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
       __repr_.__union_.__val_ = __rhs.__repr_.__union_.__val_;
     } else if (__repr_.__has_val_) {
-      __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, __rhs.__repr_.__union_.__unex_);
+      __reinit_expected<std::unexpect_t, std::in_place_t, _Err, _Tp>(
+          __repr_.__union_.__val_, __rhs.__repr_.__union_.__unex_);
     } else if (__rhs.__repr_.__has_val_) {
-      __reinit_expected(__repr_.__union_.__val_, __repr_.__union_.__unex_, __rhs.__repr_.__union_.__val_);
+      __reinit_expected<std::in_place_t, std::unexpect_t, _Tp, _Err>(
+          __repr_.__union_.__unex_, __rhs.__repr_.__union_.__val_);
     } else {
       __repr_.__union_.__unex_ = __rhs.__repr_.__union_.__unex_;
     }
-    // note: only reached if no exception+rollback was done inside __reinit_expected
-    __repr_.__has_val_ = __rhs.__repr_.__has_val_;
     return *this;
   }
 
@@ -337,14 +316,14 @@ class expected {
     if (__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
       __repr_.__union_.__val_ = std::move(__rhs.__repr_.__union_.__val_);
     } else if (__repr_.__has_val_) {
-      __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, std::move(__rhs.__repr_.__union_.__unex_));
+      __reinit_expected<std::unexpect_t, std::in_place_t, _Err, _Tp>(
+          __repr_.__union_.__val_, std::move(__rhs.__repr_.__union_.__unex_));
     } else if (__rhs.__repr_.__has_val_) {
-      __reinit_expected(__repr_.__union_.__val_, __repr_.__union_.__unex_, std::move(__rhs.__repr_.__union_.__val_));
+      __reinit_expected<std::in_place_t, std::unexpect_t, _Tp, _Err>(
+          __repr_.__union_.__unex_, std::move(__rhs.__repr_.__union_.__val_));
     } else {
       __repr_.__union_.__unex_ = std::move(__rhs.__repr_.__union_.__unex_);
     }
-    // note: only reached if no exception+rollback was done inside __reinit_expected
-    __repr_.__has_val_ = __rhs.__repr_.__has_val_;
     return *this;
   }
 
@@ -361,8 +340,8 @@ class expected {
     if (__repr_.__has_val_) {
       __repr_.__union_.__val_ = std::forward<_Up>(__v);
     } else {
-      __reinit_expected(__repr_.__union_.__val_, __repr_.__union_.__unex_, std::forward<_Up>(__v));
-      __repr_.__has_val_ = true;
+      __reinit_expected<std::in_place_t, std::unexpect_t, _Tp, _Err>(
+          __repr_.__union_.__unex_, std::forward<_Up>(__v));
     }
     return *this;
   }
@@ -382,8 +361,8 @@ class expected {
     requires(__can_assign_from_unexpected<const _OtherErr&>)
   _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const unexpected<_OtherErr>& __un) {
     if (__repr_.__has_val_) {
-      __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, __un.error());
-      __repr_.__has_val_ = false;
+      __reinit_expected<std::unexpect_t, std::in_place_t, _Err, _Tp>(
+          __repr_.__union_.__val_, __un.error());
     } else {
       __repr_.__union_.__unex_ = __un.error();
     }
@@ -394,8 +373,8 @@ class expected {
     requires(__can_assign_from_unexpected<_OtherErr>)
   _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(unexpected<_OtherErr>&& __un) {
     if (__repr_.__has_val_) {
-      __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, std::move(__un.error()));
-      __repr_.__has_val_ = false;
+      __reinit_expected<std::unexpect_t, std::in_place_t, _Err, _Tp>(
+          __repr_.__union_.__val_, std::move(__un.error()));
     } else {
       __repr_.__union_.__unex_ = std::move(__un.error());
     }
@@ -405,26 +384,16 @@ class expected {
   template <class... _Args>
     requires is_nothrow_constructible_v<_Tp, _Args...>
   _LIBCPP_HIDE_FROM_ABI constexpr _Tp& emplace(_Args&&... __args) noexcept {
-    if (__repr_.__has_val_) {
-      std::destroy_at(std::addressof(__repr_.__union_.__val_));
-    } else {
-      std::destroy_at(std::addressof(__repr_.__union_.__unex_));
-    }
-    std::construct_at(std::addressof(__repr_.__union_.__val_), std::forward<_Args>(__args)...);
-    __repr_.__has_val_ = true;
+    std::destroy_at(&__repr_);
+    std::construct_at(&__repr_, std::in_place, std::forward<_Args>(__args)...);
     return *std::addressof(__repr_.__union_.__val_);
   }
 
   template <class _Up, class... _Args>
     requires is_nothrow_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
   _LIBCPP_HIDE_FROM_ABI constexpr _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) noexcept {
-    if (__repr_.__has_val_) {
-      std::destroy_at(std::addressof(__repr_.__union_.__val_));
-    } else {
-      std::destroy_at(std::addressof(__repr_.__union_.__unex_));
-    }
-    std::construct_at(std::addressof(__repr_.__union_.__val_), __il, std::forward<_Args>(__args)...);
-    __repr_.__has_val_ = true;
+    std::destroy_at(&__repr_);
+    std::construct_at(&__repr_, std::in_place, __il, std::forward<_Args>(__args)...);
     return *std::addressof(__repr_.__union_.__val_);
   }
 
@@ -443,33 +412,31 @@ class expected {
              (is_nothrow_move_constructible_v<_Tp> ||
               is_nothrow_move_constructible_v<_Err>))
   {
-    auto __swap_val_unex_impl = [&](expected& __with_val, expected& __with_err) {
+    auto __swap_val_unex_impl = [](expected& __with_val, expected& __with_err) {
       if constexpr (is_nothrow_move_constructible_v<_Err>) {
         _Err __tmp(std::move(__with_err.__repr_.__union_.__unex_));
-        std::destroy_at(std::addressof(__with_err.__repr_.__union_.__unex_));
+        std::destroy_at(&__with_err.__repr_);
         auto __trans = std::__make_exception_guard([&] {
-          std::construct_at(std::addressof(__with_err.__repr_.__union_.__unex_), std::move(__tmp));
+          std::construct_at(&__with_err.__repr_, std::unexpect, std::move(__tmp));
         });
-        std::construct_at(std::addressof(__with_err.__repr_.__union_.__val_), std::move(__with_val.__repr_.__union_.__val_));
+        std::construct_at(&__with_err.__repr_, std::in_place, std::move(__with_val.__repr_.__union_.__val_));
         __trans.__complete();
-        std::destroy_at(std::addressof(__with_val.__repr_.__union_.__val_));
-        std::construct_at(std::addressof(__with_val.__repr_.__union_.__unex_), std::move(__tmp));
+        std::destroy_at(&__with_val.__repr_);
+        std::construct_at(&__with_val.__repr_, std::unexpect, std::move(__tmp));
       } else {
         static_assert(is_nothrow_move_constructible_v<_Tp>,
                       "To provide strong exception guarantee, Tp has to satisfy `is_nothrow_move_constructible_v` so "
                       "that it can be reverted to the previous state in case an exception is thrown during swap.");
         _Tp __tmp(std::move(__with_val.__repr_.__union_.__val_));
-        std::destroy_at(std::addressof(__with_val.__repr_.__union_.__val_));
+        std::destroy_at(&__with_val.__repr_);
         auto __trans = std::__make_exception_guard([&] {
-          std::construct_at(std::addressof(__with_val.__repr_.__union_.__val_), std::move(__tmp));
+          std::construct_at(&__with_val.__repr_, std::in_place, std::move(__tmp));
         });
-        std::construct_at(std::addressof(__with_val.__repr_.__union_.__unex_), std::move(__with_err.__repr_.__union_.__unex_));
+        std::construct_at(&__with_val.__repr_, std::unexpect, std::move(__with_err.__repr_.__union_.__unex_));
         __trans.__complete();
-        std::destroy_at(std::addressof(__with_err.__repr_.__union_.__unex_));
-        std::construct_at(std::addressof(__with_err.__repr_.__union_.__val_), std::move(__tmp));
+        std::destroy_at(&__with_err.__repr_);
+        std::construct_at(&__with_err.__repr_, std::in_place, std::move(__tmp));
       }
-      __with_val.__repr_.__has_val_ = false;
-      __with_err.__repr_.__has_val_ = true;
     };
 
     if (__repr_.__has_val_) {
@@ -952,7 +919,74 @@ class expected {
     _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
   };
 
-  __expected_repr<__union_t<_Tp, _Err>> __repr_;
+  struct __expected_repr {
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr() = delete;
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(std::in_place_t __tag, _Args&&... __args)
+        : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(true) {}
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(std::unexpect_t __tag, _Args&&... __args)
+        : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(
+        std::__expected_construct_in_place_from_invoke_tag __tag, _Args&&... __args)
+        : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(true) {}
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(
+        std::__expected_construct_unexpected_from_invoke_tag __tag, _Args&&... __args)
+        : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(bool __has_val, _Args&&... __args)
+        : __union_(__has_val, std::forward<_Args>(__args)...), __has_val_(__has_val) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&)
+      requires(is_copy_constructible_v<_Tp> &&
+               is_copy_constructible_v<_Err> &&
+               is_trivially_copy_constructible_v<_Tp> &&
+               is_trivially_copy_constructible_v<_Err>)
+    = default;
+    _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(__expected_repr&&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(__expected_repr&&)
+      requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err>
+                && is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>)
+    = default;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr& operator=(const __expected_repr&) = delete;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+      requires(is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Err>)
+    = default;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+      requires(!is_trivially_destructible_v<_Tp> || !is_trivially_destructible_v<_Err>)
+    {
+      if (__has_val_) {
+        std::destroy_at(std::addressof(__union_.__val_));
+      } else {
+        std::destroy_at(std::addressof(__union_.__unex_));
+      }
+    }
+
+  private:
+    template <class _Up, class _OtherErr> friend class expected;
+
+    [[no_unique_address]] __union_t<_Tp, _Err> __union_;
+    [[no_unique_address]] bool __has_val_;
+  };
+
+  __expected_repr __repr_;
+};
+
+template <class _Union>
+struct __expected_repr {
+  [[no_unique_address]] _Union __union_;
+  [[no_unique_address]] bool __has_val_;
 };
 
 template <class _Tp, class _Err>
diff --git a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
index 80209db9045b506..8eeec70ff0e4243 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
@@ -50,4 +50,5 @@ static_assert(sizeof(std::expected<Empty, BoolWithPadding>) ==
 
 // In this case, there should be tail padding in the `expected` because `A`
 // itself does _not_ have tail padding.
-static_assert(sizeof(std::expected<A, A>) > std::__libcpp_datasizeof<std::expected<A, A>>::value);
+// XXX
+// static_assert(sizeof(std::expected<A, A>) > std::__libcpp_datasizeof<std::expected<A, A>>::value);
diff --git a/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
index db2e4259eb8c797..2129aa7b2405c7e 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
@@ -44,4 +44,5 @@ static_assert(sizeof(std::expected<void, BoolWithPadding>) ==
 
 // In this case, there should be tail padding in the `expected` because `A`
 // itself does _not_ have tail padding.
-static_assert(sizeof(std::expected<void, A>) > std::__libcpp_datasizeof<std::expected<void, A>>::value);
+// XXX
+// static_assert(sizeof(std::expected<void, A>) > std::__libcpp_datasizeof<std::expected<void, A>>::value);

>From 3c6a8d278db691746ddb5bde840cd49a78de38dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 11:39:31 +0200
Subject: [PATCH 23/24] do the same for 'expected<void>'

---
 libcxx/include/__expected/expected.h | 150 +++++++++++++++++----------
 1 file changed, 97 insertions(+), 53 deletions(-)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index dff31ef3fccc1d8..5583eb85f4b437b 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -863,7 +863,7 @@ class expected {
       requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>)
     = default;
 
-    // the expected's destructor handles this
+    // the __expected_repr's destructor handles this
     _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() {}
 
     _ValueType __val_;
@@ -910,7 +910,7 @@ class expected {
       requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>)
     = default;
 
-    // the expected's destructor handles this
+    // the __expected_repr's destructor handles this
     _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
       requires(!is_trivially_destructible_v<_ValueType> || !is_trivially_destructible_v<_ErrorType>)
     {}
@@ -974,7 +974,8 @@ class expected {
     }
 
   private:
-    template <class _Up, class _OtherErr> friend class expected;
+    template <class, class>
+    friend class expected;
 
     [[no_unique_address]] __union_t<_Tp, _Err> __union_;
     [[no_unique_address]] bool __has_val_;
@@ -983,12 +984,6 @@ class expected {
   __expected_repr __repr_;
 };
 
-template <class _Union>
-struct __expected_repr {
-  [[no_unique_address]] _Union __union_;
-  [[no_unique_address]] bool __has_val_;
-};
-
 template <class _Tp, class _Err>
   requires is_void_v<_Tp>
 class expected<_Tp, _Err> {
@@ -1017,7 +1012,7 @@ class expected<_Tp, _Err> {
   using rebind = expected<_Up, error_type>;
 
   // [expected.void.ctor], constructors
-  _LIBCPP_HIDE_FROM_ABI constexpr expected() noexcept : __repr_{.__union_{}, .__has_val_ = true} {}
+  _LIBCPP_HIDE_FROM_ABI constexpr expected() noexcept : __repr_(std::in_place) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
 
@@ -1028,7 +1023,7 @@ class expected<_Tp, _Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr expected(const expected& __rhs)
     noexcept(is_nothrow_copy_constructible_v<_Err>) // strengthened
     requires(is_copy_constructible_v<_Err> && !is_trivially_copy_constructible_v<_Err>)
-      : __repr_{.__union_{__rhs.__repr_.__has_val_, __rhs.__repr_.__union_}, .__has_val_ = __rhs.__repr_.__has_val_} {}
+      : __repr_(__rhs.__repr_.__has_val_, __rhs.__repr_.__union_) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
     requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
@@ -1037,79 +1032,86 @@ class expected<_Tp, _Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr expected(expected&& __rhs)
     noexcept(is_nothrow_move_constructible_v<_Err>)
     requires(is_move_constructible_v<_Err> && !is_trivially_move_constructible_v<_Err>)
-      : __repr_{.__union_{__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)}, .__has_val_ = __rhs.__repr_.__has_val_} {}
+      : __repr_(__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)) {}
 
   template <class _Up, class _OtherErr>
     requires __can_convert<_Up, _OtherErr, const _OtherErr&>::value
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
   expected(const expected<_Up, _OtherErr>& __rhs)
     noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __repr_{.__union_{__rhs.__repr_.__has_val_, __rhs.__repr_.__union_}, .__has_val_ = __rhs.__repr_.__has_val_} {}
+      : __repr_(__rhs.__repr_.__has_val_, __rhs.__repr_.__union_) {}
 
   template <class _Up, class _OtherErr>
     requires __can_convert<_Up, _OtherErr, _OtherErr>::value
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
   expected(expected<_Up, _OtherErr>&& __rhs)
     noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __repr_{.__union_{__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)}, .__has_val_ = __rhs.__repr_.__has_val_} {}
+      : __repr_(__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)) {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, const _OtherErr&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
   expected(const unexpected<_OtherErr>& __unex)
     noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
-      : __repr_{.__union_{std::unexpect, __unex.error()}, .__has_val_ = false} {}
+      : __repr_(std::unexpect, __unex.error()) {}
 
   template <class _OtherErr>
     requires is_constructible_v<_Err, _OtherErr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
   expected(unexpected<_OtherErr>&& __unex)
     noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
-      : __repr_{.__union_{std::unexpect, std::move(__unex.error())}, .__has_val_ = false} {}
+      : __repr_(std::unexpect, std::move(__unex.error())) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __repr_{.__union_{}, .__has_val_ = true} {}
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __repr_(std::in_place) {}
 
   template <class... _Args>
     requires is_constructible_v<_Err, _Args...>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
-      : __repr_{.__union_{std::unexpect, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
+      : __repr_(std::unexpect, std::forward<_Args>(__args)...) {}
 
   template <class _Up, class... _Args>
     requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
     noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
-      : __repr_{.__union_{std::unexpect, __il, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
+      : __repr_(std::unexpect, __il, std::forward<_Args>(__args)...) {}
 
 private:
   template <class _Func>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(__expected_construct_in_place_from_invoke_tag, _Func&& __f)
-      : __repr_{.__union_{}, .__has_val_ = true} {
+      : __repr_(std::in_place) {
     std::invoke(std::forward<_Func>(__f));
   }
 
   template <class _Func, class... _Args>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
       __expected_construct_unexpected_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)
-      : __repr_{.__union_{__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...}, .__has_val_ = false} {}
+      : __repr_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...) {}
 
 public:
   // [expected.void.dtor], destructor
 
-  _LIBCPP_HIDE_FROM_ABI constexpr ~expected()
-    requires is_trivially_destructible_v<_Err>
-  = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr ~expected() = default;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr ~expected()
-    requires(!is_trivially_destructible_v<_Err>)
-  {
-    if (!__repr_.__has_val_) {
-      std::destroy_at(std::addressof(__repr_.__union_.__unex_));
-    }
+private:
+  // precondition: has_value()
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __reinit_expected(std::unexpect_t, _Args&&... __args) {
+    std::destroy_at(&__repr_);
+    auto __trans =
+        std::__make_exception_guard([&] { std::construct_at(&__repr_, std::in_place); });
+    std::construct_at(&__repr_, std::unexpect, std::forward<_Args>(__args)...);
+    __trans.__complete();
   }
 
-  // [expected.void.assign], assignment
+  // precondition: !has_value()
+  _LIBCPP_HIDE_FROM_ABI constexpr void __reinit_expected(std::in_place_t) {
+    std::destroy_at(&__repr_);
+    std::construct_at(&__repr_, std::in_place);
+  }
 
+public:
+  // [expected.void.assign], assignment
   _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const expected&) = delete;
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const expected& __rhs)
@@ -1118,13 +1120,11 @@ class expected<_Tp, _Err> {
   {
     if (__repr_.__has_val_) {
       if (!__rhs.__repr_.__has_val_) {
-        std::construct_at(std::addressof(__repr_.__union_.__unex_), __rhs.__repr_.__union_.__unex_);
-        __repr_.__has_val_ = false;
+        __reinit_expected(std::unexpect, __rhs.__repr_.__union_.__unex_);
       }
     } else {
       if (__rhs.__repr_.__has_val_) {
-        std::destroy_at(std::addressof(__repr_.__union_.__unex_));
-        __repr_.__has_val_ = true;
+        __reinit_expected(std::in_place);
       } else {
         __repr_.__union_.__unex_ = __rhs.__repr_.__union_.__unex_;
       }
@@ -1142,13 +1142,11 @@ class expected<_Tp, _Err> {
   {
     if (__repr_.__has_val_) {
       if (!__rhs.__repr_.__has_val_) {
-        std::construct_at(std::addressof(__repr_.__union_.__unex_), std::move(__rhs.__repr_.__union_.__unex_));
-        __repr_.__has_val_ = false;
+        __reinit_expected(std::unexpect, std::move(__rhs.__repr_.__union_.__unex_));
       }
     } else {
       if (__rhs.__repr_.__has_val_) {
-        std::destroy_at(std::addressof(__repr_.__union_.__unex_));
-        __repr_.__has_val_ = true;
+        __reinit_expected(std::in_place);
       } else {
         __repr_.__union_.__unex_ = std::move(__rhs.__repr_.__union_.__unex_);
       }
@@ -1160,8 +1158,7 @@ class expected<_Tp, _Err> {
     requires(is_constructible_v<_Err, const _OtherErr&> && is_assignable_v<_Err&, const _OtherErr&>)
   _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const unexpected<_OtherErr>& __un) {
     if (__repr_.__has_val_) {
-      std::construct_at(std::addressof(__repr_.__union_.__unex_), __un.error());
-      __repr_.__has_val_ = false;
+      __reinit_expected(std::unexpect, __un.error());
     } else {
       __repr_.__union_.__unex_ = __un.error();
     }
@@ -1172,8 +1169,7 @@ class expected<_Tp, _Err> {
     requires(is_constructible_v<_Err, _OtherErr> && is_assignable_v<_Err&, _OtherErr>)
   _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(unexpected<_OtherErr>&& __un) {
     if (__repr_.__has_val_) {
-      std::construct_at(std::addressof(__repr_.__union_.__unex_), std::move(__un.error()));
-      __repr_.__has_val_ = false;
+      __reinit_expected(std::unexpect, std::move(__un.error()));
     } else {
       __repr_.__union_.__unex_ = std::move(__un.error());
     }
@@ -1182,8 +1178,7 @@ class expected<_Tp, _Err> {
 
   _LIBCPP_HIDE_FROM_ABI constexpr void emplace() noexcept {
     if (!__repr_.__has_val_) {
-      std::destroy_at(std::addressof(__repr_.__union_.__unex_));
-      __repr_.__has_val_ = true;
+      __reinit_expected(std::in_place);
     }
   }
 
@@ -1192,11 +1187,11 @@ class expected<_Tp, _Err> {
     noexcept(is_nothrow_move_constructible_v<_Err> && is_nothrow_swappable_v<_Err>)
     requires(is_swappable_v<_Err> && is_move_constructible_v<_Err>)
   {
-    auto __swap_val_unex_impl = [&](expected& __with_val, expected& __with_err) {
-      std::construct_at(std::addressof(__with_val.__repr_.__union_.__unex_), std::move(__with_err.__repr_.__union_.__unex_));
-      std::destroy_at(std::addressof(__with_err.__repr_.__union_.__unex_));
-      __with_val.__repr_.__has_val_ = false;
-      __with_err.__repr_.__has_val_ = true;
+    auto __swap_val_unex_impl = [](expected& __with_val, expected& __with_err) {
+      // May throw, but will re-engage `__with_val` in that case.
+      __with_val.__reinit_expected(std::unexpect, std::move(__with_err.__repr_.__union_.__unex_));
+      // Will not throw.
+      __with_err.__reinit_expected(std::in_place);
     };
 
     if (__repr_.__has_val_) {
@@ -1534,7 +1529,7 @@ class expected<_Tp, _Err> {
       requires(is_trivially_destructible_v<_ErrorType>)
     = default;
 
-    // the expected's destructor handles this
+    // the __expected_repr's destructor handles this
     _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() {}
 
     __empty_t __empty_;
@@ -1573,7 +1568,7 @@ class expected<_Tp, _Err> {
       requires(is_trivially_destructible_v<_ErrorType>)
     = default;
 
-    // the expected's destructor handles this
+    // the __expected_repr's destructor handles this
     _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
       requires(!is_trivially_destructible_v<_ErrorType>)
     {}
@@ -1582,7 +1577,56 @@ class expected<_Tp, _Err> {
     _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
   };
 
-  __expected_repr<__union_t<_Err>> __repr_;
+  struct __expected_repr {
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr() = delete;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(std::in_place_t) : __union_(), __has_val_(true) {}
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(std::unexpect_t __tag, _Args&&... __args)
+        : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(
+        std::__expected_construct_unexpected_from_invoke_tag __tag, _Args&&... __args)
+        : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(bool __has_val, _Args&&... __args)
+        : __union_(__has_val, std::forward<_Args>(__args)...), __has_val_(__has_val) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&)
+      requires(is_copy_constructible_v<_Err> && is_trivially_copy_constructible_v<_Err>)
+    = default;
+    _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(__expected_repr&&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(__expected_repr&&)
+      requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
+    = default;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr& operator=(const __expected_repr&) = delete;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+      requires(is_trivially_destructible_v<_Err>)
+    = default;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+      requires(!is_trivially_destructible_v<_Err>)
+    {
+      if (!__has_val_) {
+        std::destroy_at(std::addressof(__union_.__unex_));
+      }
+    }
+
+  private:
+    template <class, class>
+    friend class expected;
+
+    [[no_unique_address]] __union_t<_Err> __union_;
+    [[no_unique_address]] bool __has_val_;
+  };
+
+  __expected_repr __repr_;
 };
 
 _LIBCPP_END_NAMESPACE_STD

>From 3caf86c2fa1aa31e9106421e921bf3f7c5feab61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 11:40:48 +0200
Subject: [PATCH 24/24] remove unused 'expected<void>' constructor

---
 libcxx/include/__expected/expected.h | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 5583eb85f4b437b..5ab10d9f3abf50b 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -1077,12 +1077,6 @@ class expected<_Tp, _Err> {
       : __repr_(std::unexpect, __il, std::forward<_Args>(__args)...) {}
 
 private:
-  template <class _Func>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(__expected_construct_in_place_from_invoke_tag, _Func&& __f)
-      : __repr_(std::in_place) {
-    std::invoke(std::forward<_Func>(__f));
-  }
-
   template <class _Func, class... _Args>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
       __expected_construct_unexpected_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)



More information about the libcxx-commits mailing list