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

via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jan 22 06:05:43 PST 2024


Author: Jan Kokemüller
Date: 2024-01-22T09:05:39-05:00
New Revision: 4f4690530e8b40cdf3a17c76a352b26c2fb0446c

URL: https://github.com/llvm/llvm-project/commit/4f4690530e8b40cdf3a17c76a352b26c2fb0446c
DIFF: https://github.com/llvm/llvm-project/commit/4f4690530e8b40cdf3a17c76a352b26c2fb0446c.diff

LOG: [libc++] Ensure that std::expected has no tail padding (#69673)

Currently std::expected can have some padding bytes in its tail due to
[[no_unique_address]]. Those padding bytes can be used by other objects.
For example, in the current implementation:

  sizeof(std::expected<std::optional<int>, bool>) == 
    sizeof(std::expected<std::expected<std::optional<int>, bool>, bool>)

As a result, the data layout of an
  std::expected<std::expected<std::optional<int>, bool>, bool> 
can look like this:

              +-- optional "has value" flag
              |        +--padding
  /---int---\ |        |
  00 00 00 00 01 00 00 00
                |  |
                |  +- "outer" expected "has value" flag
                |
                +- expected "has value" flag

This is problematic because `emplace()`ing the "inner" expected can not
only overwrite the "inner" expected "has value" flag (issue #68552) but
also the tail padding where other objects might live.

This patch fixes the problem by ensuring that std::expected has no tail
padding, which is achieved by conditional usage of [[no_unique_address]]
based on the tail padding that this would create.

This is an ABI breaking change because the following property changes:

  sizeof(std::expected<std::optional<int>, bool>) <
    sizeof(std::expected<std::expected<std::optional<int>, bool>, bool>)

Before the change, this relation didn't hold. After the change, the relation
does hold, which means that the size of std::expected in these cases increases
after this patch. The data layout will change in the following cases where
tail padding can be reused by other objects:

  class foo : std::expected<std::optional<int>, bool> {
    bool b;
  };

or using [[no_unique_address]]:

  struct foo {
    [[no_unique_address]] std::expected<std::optional<int>, bool> e;
    bool b;
  };

The vendor communication is handled in #70820.
Fixes: #70494

Co-authored-by: philnik777 <nikolasklauser at berlin.de>
Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>

Added: 
    

Modified: 
    libcxx/docs/ReleaseNotes/18.rst
    libcxx/include/__expected/expected.h
    libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
    libcxx/test/libcxx/utilities/expected/expected.expected/transform_error.mandates.verify.cpp
    libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
    libcxx/test/libcxx/utilities/expected/expected.void/transform_error.mandates.verify.cpp
    libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp
    libcxx/test/std/utilities/expected/expected.expected/assign/assign.copy.pass.cpp
    libcxx/test/std/utilities/expected/expected.expected/assign/assign.move.pass.cpp
    libcxx/test/std/utilities/expected/expected.expected/assign/emplace.pass.cpp
    libcxx/test/std/utilities/expected/expected.expected/monadic/transform.pass.cpp
    libcxx/test/std/utilities/expected/expected.expected/monadic/transform_error.pass.cpp
    libcxx/test/std/utilities/expected/expected.expected/swap/member.swap.pass.cpp
    libcxx/test/std/utilities/expected/expected.void/assign/assign.copy.pass.cpp
    libcxx/test/std/utilities/expected/expected.void/assign/assign.move.pass.cpp
    libcxx/test/std/utilities/expected/expected.void/assign/assign.unexpected.copy.pass.cpp
    libcxx/test/std/utilities/expected/expected.void/assign/assign.unexpected.move.pass.cpp
    libcxx/test/std/utilities/expected/expected.void/monadic/transform_error.pass.cpp
    libcxx/test/std/utilities/expected/expected.void/swap/member.swap.pass.cpp
    libcxx/test/std/utilities/expected/types.h

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index 7d6331a0258083..c619f94fd00357 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -284,6 +284,28 @@ ABI Affecting Changes
   against 
diff erent configurations of it being used in 
diff erent translation
   units.
 
+- The amount of padding bytes available for use at the end of certain ``std::expected`` instantiations has changed in this
+  release. This is an ABI break for any code that held a ``std::expected`` member with ``[[no_unique_address]]`` in an
+  ABI-facing type. In those cases, the layout of the enclosing type will change, breaking the ABI. However, the
+  ``std::expected<T, E>`` member requires a few characteristics in order to be affected by this change:
+
+  - A type equivalent to ``union {T ; E}`` needs to have more than one byte of padding available.
+  - The ``std::expected<T, E>`` member must have been in a situation where its padding bytes were previously reused by
+    another object, which can happen in a few cases (this is probably not exhaustive):
+
+    - It is a member with ``[[no_unique_address]]`` applied to it, and it is followed by another data member, or
+    - It is a member with ``[[no_unique_address]]`` applied to it, and it is the last member of the user-defined type,
+      and that user-defined type is used in ways that its padding bytes can be reused, or
+    - It is inherited from
+
+  We expect that this will not be a very frequent occurrence. However, there is unfortunately no technique we can use
+  in the library to catch such misuse. Indeed, even applying an ABI tag to ``std::expected`` would not help since ABI
+  tags are not propagated to containing types. As a result, if you notice very 
diff icult to explain bugs around the
+  usage of a ``std::expected``, you should consider checking whether you are hitting this ABI break. This change was
+  done to fix `#70494 <https://github.com/llvm/llvm-project/issues/70494>`_ and the vendor communication is handled
+  in `#70820 <https://github.com/llvm/llvm-project/issues/70820>`_.
+
+
 Build System Changes
 --------------------
 

diff  --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 9f36c41b78edfd..443d9257dc598d 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -88,8 +88,372 @@ _LIBCPP_HIDE_FROM_ABI void __throw_bad_expected_access(_Arg&& __arg) {
 #  endif
 }
 
+// If parameter type `_Tp` of `__conditional_no_unique_address` is neither
+// copyable nor movable, a constructor with this tag is provided. For that
+// constructor, the user has to provide a function and arguments. The function
+// must return an object of type `_Tp`. When the function is invoked by the
+// constructor, guaranteed copy elision kicks in and the `_Tp` is constructed
+// in place.
+struct __conditional_no_unique_address_invoke_tag {};
+
+// This class implements an object with `[[no_unique_address]]` conditionally applied to it,
+// based on the value of `_NoUnique`.
+//
+// A member of this class must always have `[[no_unique_address]]` applied to
+// it. Otherwise, the `[[no_unique_address]]` in the "`_NoUnique == true`" case
+// would not have any effect. In the `false` case, the `__v` is not
+// `[[no_unique_address]]`, so nullifies the effects of the "outer"
+// `[[no_unique_address]]` regarding data layout.
+//
+// If we had a language feature, this class would basically be replaced by `[[no_unique_address(condition)]]`.
+template <bool _NoUnique, class _Tp>
+struct __conditional_no_unique_address;
+
+template <class _Tp>
+struct __conditional_no_unique_address<true, _Tp> {
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __conditional_no_unique_address(in_place_t, _Args&&... __args)
+      : __v(std::forward<_Args>(__args)...) {}
+
+  template <class _Func, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __conditional_no_unique_address(
+      __conditional_no_unique_address_invoke_tag, _Func&& __f, _Args&&... __args)
+      : __v(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {}
+
+  _LIBCPP_NO_UNIQUE_ADDRESS _Tp __v;
+};
+
+template <class _Tp>
+struct __conditional_no_unique_address<false, _Tp> {
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __conditional_no_unique_address(in_place_t, _Args&&... __args)
+      : __v(std::forward<_Args>(__args)...) {}
+
+  template <class _Func, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __conditional_no_unique_address(
+      __conditional_no_unique_address_invoke_tag, _Func&& __f, _Args&&... __args)
+      : __v(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {}
+
+  _Tp __v;
+};
+
+// This function returns whether the type `_Second` can be stuffed into the tail padding
+// of the `_First` type if both of them are given `[[no_unique_address]]`.
+template <class _First, class _Second>
+inline constexpr bool __fits_in_tail_padding = []() {
+  struct __x {
+    _LIBCPP_NO_UNIQUE_ADDRESS _First __first;
+    _LIBCPP_NO_UNIQUE_ADDRESS _Second __second;
+  };
+  return sizeof(__x) == sizeof(_First);
+}();
+
+// This class implements the storage used by `std::expected`. We have a few
+// goals for this storage:
+// 1. Whenever the underlying {_Tp | _Unex} combination has free bytes in its
+//    tail padding, we should reuse it to store the bool discriminator of the
+//    expected, so as to save space.
+// 2. Whenever the `expected<_Tp, _Unex>` as a whole has free bytes in its tail
+//    padding, we should allow an object following the expected to be stored in
+//    its tail padding.
+// 3. However, we never want a user object (say `X`) that would follow an
+//    `expected<_Tp, _Unex>` to be stored in the padding bytes of the
+//    underlying {_Tp | _Unex} union, if any. That is because we use
+//    `construct_at` on that union, which would end up overwriting the `X`
+//    member if it is stored in the tail padding of the union.
+//
+// To achieve this, `__expected_base`'s logic is implemented in an inner
+// `__repr` class. `__expected_base` holds one `__repr` member which is
+// conditionally `[[no_unique_address]]`. The `__repr` class holds the
+// underlying {_Tp | _Unex} union and a boolean "has value" flag.
+//
+// Which one of the `__repr_`/`__union_` members is `[[no_unique_address]]`
+// depends on whether the "has value" boolean fits into the tail padding of
+// the underlying {_Tp | _Unex} union:
+//
+// - In case the "has value" bool fits into the tail padding of the union, the
+//   whole `__repr_` member is _not_ `[[no_unique_address]]` as it needs to be
+//   transparently replaced on `emplace()`/`swap()` etc.
+// - In case the "has value" bool does not fit into the tail padding of the
+//   union, only the union member must be transparently replaced (therefore is
+//   _not_ `[[no_unique_address]]`) and the "has value" flag must be adjusted
+//   manually.
+//
+// This way, the member that is transparently replaced on mutating operations
+// is never `[[no_unique_address]]`, satisfying the requirements from
+// "[basic.life]" in the standard.
+//
+// Stripped away of all superfluous elements, the layout of `__expected_base`
+// then looks like this:
+//
+//     template <class Tp, class Err>
+//     class expected_base {
+//       union union_t {
+//         [[no_unique_address]] Tp val;
+//         [[no_unique_address]] Err unex;
+//       };
+//
+//       static constexpr bool put_flag_in_tail                    = fits_in_tail_padding<union_t, bool>;
+//       static constexpr bool allow_reusing_expected_tail_padding = !put_flag_in_tail;
+//
+//       struct repr {
+//       private:
+//         // If "has value" fits into the tail, this should be
+//         // `[[no_unique_address]]`, otherwise not.
+//         [[no_unique_address]] conditional_no_unique_address<
+//             put_flag_in_tail,
+//             union_t>::type union_;
+//         [[no_unique_address]] bool has_val_;
+//       };
+//
+//     protected:
+//       // If "has value" fits into the tail, this must _not_ be
+//       // `[[no_unique_address]]` so that we fill out the
+//       // complete `expected` object.
+//       [[no_unique_address]] conditional_no_unique_address<
+//           allow_reusing_expected_tail_padding,
+//           repr>::type repr_;
+//     };
+//
 template <class _Tp, class _Err>
-class expected {
+class __expected_base {
+  // use named union because [[no_unique_address]] cannot be applied to an unnamed union,
+  // also guaranteed elision into a potentially-overlapping subobject is unsettled (and
+  // it's not clear that it's implementable, given that the function is allowed to clobber
+  // the tail padding) - see https://github.com/itanium-cxx-abi/cxx-abi/issues/107.
+  union __union_t {
+    _LIBCPP_HIDE_FROM_ABI constexpr __union_t(const __union_t&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __union_t(const __union_t&)
+      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 __union_t(__union_t&&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __union_t(__union_t&&)
+      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 __union_t& operator=(const __union_t&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(__union_t&&)      = delete;
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(in_place_t, _Args&&... __args)
+        : __val_(std::forward<_Args>(__args)...) {}
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(unexpect_t, _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)
+        : __val_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {}
+
+    template <class _Func, class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(
+        std::__expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args)
+        : __unex_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
+      requires(is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Err>)
+    = default;
+
+    // __repr's destructor handles this
+    _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() {}
+
+    _LIBCPP_NO_UNIQUE_ADDRESS _Tp __val_;
+    _LIBCPP_NO_UNIQUE_ADDRESS _Err __unex_;
+  };
+
+  static constexpr bool __put_flag_in_tail                    = __fits_in_tail_padding<__union_t, bool>;
+  static constexpr bool __allow_reusing_expected_tail_padding = !__put_flag_in_tail;
+
+  struct __repr {
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr() = delete;
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(in_place_t __tag, _Args&&... __args)
+        : __union_(in_place, __tag, std::forward<_Args>(__args)...), __has_val_(true) {}
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(unexpect_t __tag, _Args&&... __args)
+        : __union_(in_place, __tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(std::__expected_construct_in_place_from_invoke_tag __tag,
+                                                    _Args&&... __args)
+        : __union_(in_place, __tag, std::forward<_Args>(__args)...), __has_val_(true) {}
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(std::__expected_construct_unexpected_from_invoke_tag __tag,
+                                                    _Args&&... __args)
+        : __union_(in_place, __tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+    // The return value of `__make_union` must be constructed in place in the
+    // `__v` member of `__union_`, relying on guaranteed copy elision. To do
+    // this, the `__conditional_no_unique_address_invoke_tag` constructor is
+    // called with a lambda that is immediately called inside
+    // `__conditional_no_unique_address`'s constructor.
+    template <class _OtherUnion>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(bool __has_val, _OtherUnion&& __other)
+      requires(__allow_reusing_expected_tail_padding)
+        : __union_(__conditional_no_unique_address_invoke_tag{},
+                   [&] { return __make_union(__has_val, std::forward<_OtherUnion>(__other)); }),
+          __has_val_(__has_val) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __repr(const __repr&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __repr(const __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 __repr(__repr&&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __repr(__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 __repr& operator=(const __repr&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __repr& operator=(__repr&&)      = delete;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr ~__repr()
+      requires(is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Err>)
+    = default;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr ~__repr()
+      requires(!is_trivially_destructible_v<_Tp> || !is_trivially_destructible_v<_Err>)
+    {
+      __destroy_union_member();
+    }
+
+    _LIBCPP_HIDE_FROM_ABI constexpr void __destroy_union()
+      requires(__allow_reusing_expected_tail_padding &&
+               (is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Err>))
+    {
+      // Note: Since the destructor of the union is trivial, this does nothing
+      // except to end the lifetime of the union.
+      std::destroy_at(&__union_.__v);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI constexpr void __destroy_union()
+      requires(__allow_reusing_expected_tail_padding &&
+               (!is_trivially_destructible_v<_Tp> || !is_trivially_destructible_v<_Err>))
+    {
+      __destroy_union_member();
+      std::destroy_at(&__union_.__v);
+    }
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr void __construct_union(in_place_t, _Args&&... __args)
+      requires(__allow_reusing_expected_tail_padding)
+    {
+      std::construct_at(&__union_.__v, in_place, std::forward<_Args>(__args)...);
+      __has_val_ = true;
+    }
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr void __construct_union(unexpect_t, _Args&&... __args)
+      requires(__allow_reusing_expected_tail_padding)
+    {
+      std::construct_at(&__union_.__v, unexpect, std::forward<_Args>(__args)...);
+      __has_val_ = false;
+    }
+
+  private:
+    template <class, class>
+    friend class __expected_base;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr void __destroy_union_member()
+      requires(!is_trivially_destructible_v<_Tp> || !is_trivially_destructible_v<_Err>)
+    {
+      if (__has_val_) {
+        std::destroy_at(std::addressof(__union_.__v.__val_));
+      } else {
+        std::destroy_at(std::addressof(__union_.__v.__unex_));
+      }
+    }
+
+    template <class _OtherUnion>
+    _LIBCPP_HIDE_FROM_ABI static constexpr __union_t __make_union(bool __has_val, _OtherUnion&& __other)
+      requires(__allow_reusing_expected_tail_padding)
+    {
+      if (__has_val)
+        return __union_t(in_place, std::forward<_OtherUnion>(__other).__val_);
+      else
+        return __union_t(unexpect, std::forward<_OtherUnion>(__other).__unex_);
+    }
+
+    _LIBCPP_NO_UNIQUE_ADDRESS __conditional_no_unique_address<__put_flag_in_tail, __union_t> __union_;
+    _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
+  };
+
+  template <class _OtherUnion>
+  _LIBCPP_HIDE_FROM_ABI static constexpr __repr __make_repr(bool __has_val, _OtherUnion&& __other)
+    requires(__put_flag_in_tail)
+  {
+    if (__has_val)
+      return __repr(in_place, std::forward<_OtherUnion>(__other).__val_);
+    else
+      return __repr(unexpect, std::forward<_OtherUnion>(__other).__unex_);
+  }
+
+protected:
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_base(_Args&&... __args)
+      : __repr_(in_place, std::forward<_Args>(__args)...) {}
+
+  // In case we copy/move construct from another `expected` we need to create
+  // our `expected` so that it either has a value or not, depending on the "has
+  // value" flag of the other `expected`. To do this without falling back on
+  // `std::construct_at` we rely on guaranteed copy elision using two helper
+  // functions `__make_repr` and `__make_union`. There have to be two since
+  // there are two data layouts with 
diff erent members being
+  // `[[no_unique_address]]`. GCC (as of version 13) does not do guaranteed
+  // copy elision when initializing `[[no_unique_address]]` members. The two
+  // cases are:
+  //
+  // - `__make_repr`: This is used when the "has value" flag lives in the tail
+  //   of the union. In this case, the `__repr` member is _not_
+  //   `[[no_unique_address]]`.
+  // - `__make_union`: When the "has value" flag does _not_ fit in the tail of
+  //   the union, the `__repr` member is `[[no_unique_address]]` and the union
+  //   is not.
+  //
+  // This constructor "catches" the first case and leaves the second case to
+  // `__union_t`, its constructors and `__make_union`.
+  template <class _OtherUnion>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_base(bool __has_val, _OtherUnion&& __other)
+    requires(__put_flag_in_tail)
+      : __repr_(__conditional_no_unique_address_invoke_tag{},
+                [&] { return __make_repr(__has_val, std::forward<_OtherUnion>(__other)); }) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr void __destroy() {
+    if constexpr (__put_flag_in_tail)
+      std::destroy_at(&__repr_.__v);
+    else
+      __repr_.__v.__destroy_union();
+  }
+
+  template <class _Tag, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __construct(_Tag __tag, _Args&&... __args) {
+    if constexpr (__put_flag_in_tail)
+      std::construct_at(&__repr_.__v, __tag, std::forward<_Args>(__args)...);
+    else
+      __repr_.__v.__construct_union(__tag, std::forward<_Args>(__args)...);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool __has_val() const { return __repr_.__v.__has_val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr __union_t& __union() { return __repr_.__v.__union_.__v; }
+  _LIBCPP_HIDE_FROM_ABI constexpr const __union_t& __union() const { return __repr_.__v.__union_.__v; }
+  _LIBCPP_HIDE_FROM_ABI constexpr _Tp& __val() { return __repr_.__v.__union_.__v.__val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& __val() const { return __repr_.__v.__union_.__v.__val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr _Err& __unex() { return __repr_.__v.__union_.__v.__unex_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Err& __unex() const { return __repr_.__v.__union_.__v.__unex_; }
+
+private:
+  _LIBCPP_NO_UNIQUE_ADDRESS __conditional_no_unique_address<__allow_reusing_expected_tail_padding, __repr> __repr_;
+};
+
+template <class _Tp, class _Err>
+class expected : private __expected_base<_Tp, _Err> {
   static_assert(!is_reference_v<_Tp> && !is_function_v<_Tp> && !is_same_v<remove_cv_t<_Tp>, in_place_t> &&
                     !is_same_v<remove_cv_t<_Tp>, unexpect_t> && !__is_std_unexpected<remove_cv_t<_Tp>>::value &&
                     __valid_std_unexpected<_Err>::value,
@@ -102,6 +466,8 @@ class expected {
   template <class _Up, class _OtherErr>
   friend class expected;
 
+  using __base = __expected_base<_Tp, _Err>;
+
 public:
   using value_type      = _Tp;
   using error_type      = _Err;
@@ -113,7 +479,7 @@ class expected {
   // [expected.object.ctor], constructors
   _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) {}
+      : __base(in_place) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
 
@@ -126,7 +492,7 @@ class expected {
       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_) {}
+      : __base(__other.__has_val(), __other.__union()) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
     requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Tp> &&
@@ -137,7 +503,7 @@ class expected {
       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_) {}
+      : __base(__other.__has_val(), std::move(__other.__union())) {}
 
 private:
   template <class _Up, class _OtherErr, class _UfQual, class _OtherErrQual>
@@ -162,12 +528,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) {}
+      : __base(__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)
-      : __union_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...), __has_val_(false) {}
+      : __base(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...) {}
 
 public:
   template <class _Up, class _OtherErr>
@@ -177,14 +543,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_(__other.__has_val_, __other.__union_), __has_val_(__other.__has_val_) {}
+      : __base(__other.__has_val(), __other.__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
-      : __union_(__other.__has_val_, std::move(__other.__union_)), __has_val_(__other.__has_val_) {}
+      : __base(__other.__has_val(), std::move(__other.__union())) {}
 
   template <class _Up = _Tp>
     requires(!is_same_v<remove_cvref_t<_Up>, in_place_t> && !is_same_v<expected, remove_cvref_t<_Up>> &&
@@ -192,80 +558,67 @@ 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) {}
+      : __base(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
-      : __union_(std::unexpect, __unex.error()), __has_val_(false) {}
+      : __base(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
-      : __union_(std::unexpect, std::move(__unex.error())), __has_val_(false) {}
+      : __base(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
-      : __union_(std::in_place, std::forward<_Args>(__args)...), __has_val_(true) {}
+      : __base(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
-      : __union_(std::in_place, __il, std::forward<_Args>(__args)...), __has_val_(true) {}
+      : __base(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
-      : __union_(std::unexpect, std::forward<_Args>(__args)...), __has_val_(false) {}
+      : __base(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
-      : __union_(std::unexpect, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
+      : __base(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 (__has_val_) {
-      std::destroy_at(std::addressof(__union_.__val_));
-    } else {
-      std::destroy_at(std::addressof(__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)...);
+      this->__destroy();
+      this->__construct(_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));
+      this->__destroy();
+      this->__construct(_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));
-      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)...);
+      this->__destroy();
+      auto __trans = std::__make_exception_guard([&] { this->__construct(_OtherTag{}, std::move(__tmp)); });
+      this->__construct(_Tag{}, std::forward<_Args>(__args)...);
       __trans.__complete();
     }
   }
@@ -281,17 +634,15 @@ class expected {
              is_copy_constructible_v<_Err> &&
              (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 (this->__has_val() && __rhs.__has_val()) {
+      this->__val() = __rhs.__val();
+    } else if (this->__has_val()) {
+      __reinit_expected<unexpect_t, in_place_t, _Err, _Tp>(this->__val(), __rhs.__unex());
+    } else if (__rhs.__has_val()) {
+      __reinit_expected<in_place_t, unexpect_t, _Tp, _Err>(this->__unex(), __rhs.__val());
     } else {
-      __union_.__unex_ = __rhs.__union_.__unex_;
+      this->__unex() = __rhs.__unex();
     }
-    // note: only reached if no exception+rollback was done inside __reinit_expected
-    __has_val_ = __rhs.__has_val_;
     return *this;
   }
 
@@ -302,17 +653,15 @@ class expected {
              is_move_assignable_v<_Err> &&
              (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 (this->__has_val() && __rhs.__has_val()) {
+      this->__val() = std::move(__rhs.__val());
+    } else if (this->__has_val()) {
+      __reinit_expected<unexpect_t, in_place_t, _Err, _Tp>(this->__val(), std::move(__rhs.__unex()));
+    } else if (__rhs.__has_val()) {
+      __reinit_expected<in_place_t, unexpect_t, _Tp, _Err>(this->__unex(), std::move(__rhs.__val()));
     } else {
-      __union_.__unex_ = std::move(__rhs.__union_.__unex_);
+      this->__unex() = std::move(__rhs.__unex());
     }
-    // note: only reached if no exception+rollback was done inside __reinit_expected
-    __has_val_ = __rhs.__has_val_;
     return *this;
   }
 
@@ -323,11 +672,10 @@ class expected {
              (is_nothrow_constructible_v<_Tp, _Up> || is_nothrow_move_constructible_v<_Tp> ||
               is_nothrow_move_constructible_v<_Err>))
   {
-    if (__has_val_) {
-      __union_.__val_ = std::forward<_Up>(__v);
+    if (this->__has_val()) {
+      this->__val() = std::forward<_Up>(__v);
     } else {
-      __reinit_expected(__union_.__val_, __union_.__unex_, std::forward<_Up>(__v));
-      __has_val_ = true;
+      __reinit_expected<in_place_t, unexpect_t, _Tp, _Err>(this->__unex(), std::forward<_Up>(__v));
     }
     return *this;
   }
@@ -346,11 +694,10 @@ 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 (this->__has_val()) {
+      __reinit_expected<unexpect_t, in_place_t, _Err, _Tp>(this->__val(), __un.error());
     } else {
-      __union_.__unex_ = __un.error();
+      this->__unex() = __un.error();
     }
     return *this;
   }
@@ -358,11 +705,10 @@ 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 (this->__has_val()) {
+      __reinit_expected<unexpect_t, in_place_t, _Err, _Tp>(this->__val(), std::move(__un.error()));
     } else {
-      __union_.__unex_ = std::move(__un.error());
+      this->__unex() = std::move(__un.error());
     }
     return *this;
   }
@@ -370,27 +716,17 @@ 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_));
-    } else {
-      std::destroy_at(std::addressof(__union_.__unex_));
-    }
-    std::construct_at(std::addressof(__union_.__val_), std::forward<_Args>(__args)...);
-    __has_val_ = true;
-    return __union_.__val_;
+    this->__destroy();
+    this->__construct(in_place, std::forward<_Args>(__args)...);
+    return this->__val();
   }
 
   template <class _Up, class... _Args>
-    requires is_nothrow_constructible_v< _Tp, initializer_list<_Up>&, _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_));
-    } else {
-      std::destroy_at(std::addressof(__union_.__unex_));
-    }
-    std::construct_at(std::addressof(__union_.__val_), __il, std::forward<_Args>(__args)...);
-    __has_val_ = true;
-    return __union_.__val_;
+    this->__destroy();
+    this->__construct(in_place, __il, std::forward<_Args>(__args)...);
+    return this->__val();
   }
 
 public:
@@ -402,48 +738,42 @@ class expected {
              is_move_constructible_v<_Err> &&
              (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.__union_.__unex_));
-        std::destroy_at(std::addressof(__with_err.__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.__union_.__val_), std::move(__with_val.__union_.__val_));
+        _Err __tmp(std::move(__with_err.__unex()));
+        __with_err.__destroy();
+        auto __trans = std::__make_exception_guard([&] { __with_err.__construct(unexpect, std::move(__tmp)); });
+        __with_err.__construct(in_place, std::move(__with_val.__val()));
         __trans.__complete();
-        std::destroy_at(std::addressof(__with_val.__union_.__val_));
-        std::construct_at(std::addressof(__with_val.__union_.__unex_), std::move(__tmp));
+        __with_val.__destroy();
+        __with_val.__construct(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.__union_.__val_));
-        std::destroy_at(std::addressof(__with_val.__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.__union_.__unex_), std::move(__with_err.__union_.__unex_));
+        _Tp __tmp(std::move(__with_val.__val()));
+        __with_val.__destroy();
+        auto __trans = std::__make_exception_guard([&] { __with_val.__construct(in_place, std::move(__tmp)); });
+        __with_val.__construct(unexpect, std::move(__with_err.__unex()));
         __trans.__complete();
-        std::destroy_at(std::addressof(__with_err.__union_.__unex_));
-        std::construct_at(std::addressof(__with_err.__union_.__val_), std::move(__tmp));
+        __with_err.__destroy();
+        __with_err.__construct(in_place, std::move(__tmp));
       }
-      __with_val.__has_val_ = false;
-      __with_err.__has_val_ = true;
     };
 
-    if (__has_val_) {
-      if (__rhs.__has_val_) {
+    if (this->__has_val()) {
+      if (__rhs.__has_val()) {
         using std::swap;
-        swap(__union_.__val_, __rhs.__union_.__val_);
+        swap(this->__val(), __rhs.__val());
       } else {
         __swap_val_unex_impl(*this, __rhs);
       }
     } else {
-      if (__rhs.__has_val_) {
+      if (__rhs.__has_val()) {
         __swap_val_unex_impl(__rhs, *this);
       } else {
         using std::swap;
-        swap(__union_.__unex_, __rhs.__union_.__unex_);
+        swap(this->__unex(), __rhs.__unex());
       }
     }
   }
@@ -456,105 +786,115 @@ 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(
+        this->__has_val(), "expected::operator-> requires the expected to contain a value");
+    return std::addressof(this->__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(
+        this->__has_val(), "expected::operator-> requires the expected to contain a value");
+    return std::addressof(this->__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(
+        this->__has_val(), "expected::operator* requires the expected to contain a value");
+    return this->__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(
+        this->__has_val(), "expected::operator* requires the expected to contain a value");
+    return this->__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(
+        this->__has_val(), "expected::operator* requires the expected to contain a value");
+    return std::move(this->__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(
+        this->__has_val(), "expected::operator* requires the expected to contain a value");
+    return std::move(this->__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 this->__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 this->__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 (!this->__has_val()) {
       std::__throw_bad_expected_access<_Err>(std::as_const(error()));
     }
-    return __union_.__val_;
+    return this->__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 (!this->__has_val()) {
       std::__throw_bad_expected_access<_Err>(std::as_const(error()));
     }
-    return __union_.__val_;
+    return this->__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 (!this->__has_val()) {
       std::__throw_bad_expected_access<_Err>(std::move(error()));
     }
-    return std::move(__union_.__val_);
+    return std::move(this->__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 (!this->__has_val()) {
       std::__throw_bad_expected_access<_Err>(std::move(error()));
     }
-    return std::move(__union_.__val_);
+    return std::move(this->__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(
+        !this->__has_val(), "expected::error requires the expected to contain an error");
+    return this->__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(
+        !this->__has_val(), "expected::error requires the expected to contain an error");
+    return this->__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(
+        !this->__has_val(), "expected::error requires the expected to contain an error");
+    return std::move(this->__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(
+        !this->__has_val(), "expected::error requires the expected to contain an error");
+    return std::move(this->__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 this->__has_val() ? this->__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 this->__has_val() ? std::move(this->__val()) : static_cast<_Tp>(std::forward<_Up>(__v));
   }
 
   template <class _Up = _Err>
@@ -584,7 +924,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), this->__val());
     }
     return _Up(unexpect, error());
   }
@@ -597,7 +937,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), this->__val());
     }
     return _Up(unexpect, error());
   }
@@ -611,7 +951,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(this->__val()));
     }
     return _Up(unexpect, std::move(error()));
   }
@@ -625,7 +965,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(this->__val()));
     }
     return _Up(unexpect, std::move(error()));
   }
@@ -638,7 +978,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, this->__val());
     }
     return std::invoke(std::forward<_Func>(__f), error());
   }
@@ -651,7 +991,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, this->__val());
     }
     return std::invoke(std::forward<_Func>(__f), error());
   }
@@ -665,7 +1005,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(this->__val()));
     }
     return std::invoke(std::forward<_Func>(__f), std::move(error()));
   }
@@ -679,7 +1019,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(this->__val()));
     }
     return std::invoke(std::forward<_Func>(__f), std::move(error()));
   }
@@ -693,9 +1033,9 @@ class expected {
     }
     if constexpr (!is_void_v<_Up>) {
       return expected<_Up, _Err>(
-          __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), __union_.__val_);
+          __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), this->__val());
     } else {
-      std::invoke(std::forward<_Func>(__f), __union_.__val_);
+      std::invoke(std::forward<_Func>(__f), this->__val());
       return expected<_Up, _Err>();
     }
   }
@@ -709,9 +1049,9 @@ class expected {
     }
     if constexpr (!is_void_v<_Up>) {
       return expected<_Up, _Err>(
-          __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), __union_.__val_);
+          __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), this->__val());
     } else {
-      std::invoke(std::forward<_Func>(__f), __union_.__val_);
+      std::invoke(std::forward<_Func>(__f), this->__val());
       return expected<_Up, _Err>();
     }
   }
@@ -725,9 +1065,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(this->__val()));
     } else {
-      std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_));
+      std::invoke(std::forward<_Func>(__f), std::move(this->__val()));
       return expected<_Up, _Err>();
     }
   }
@@ -741,9 +1081,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(this->__val()));
     } else {
-      std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_));
+      std::invoke(std::forward<_Func>(__f), std::move(this->__val()));
       return expected<_Up, _Err>();
     }
   }
@@ -755,7 +1095,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, this->__val());
     }
     return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error());
   }
@@ -767,7 +1107,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, this->__val());
     }
     return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error());
   }
@@ -779,7 +1119,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(this->__val()));
     }
     return expected<_Tp, _Gp>(
         __expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error()));
@@ -792,7 +1132,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(this->__val()));
     }
     return expected<_Tp, _Gp>(
         __expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error()));
@@ -802,123 +1142,220 @@ 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.__has_val() != __y.__has_val()) {
       return false;
     } else {
-      if (__x.__has_val_) {
-        return __x.__union_.__val_ == __y.__union_.__val_;
+      if (__x.__has_val()) {
+        return __x.__val() == __y.__val();
       } else {
-        return __x.__union_.__unex_ == __y.__union_.__unex_;
+        return __x.__unex() == __y.__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.__has_val() && static_cast<bool>(__x.__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.__has_val() && static_cast<bool>(__x.__unex() == __e.error());
   }
+};
 
-private:
-  template <class _ValueType, class _ErrorType>
+template <class _Err>
+class __expected_void_base {
+  struct __empty_t {};
+  // use named union because [[no_unique_address]] cannot be applied to an unnamed union,
+  // also guaranteed elision into a potentially-overlapping subobject is unsettled (and
+  // it's not clear that it's implementable, given that the function is allowed to clobber
+  // the tail padding) - see https://github.com/itanium-cxx-abi/cxx-abi/issues/107.
   union __union_t {
-    template <class... _Args>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::in_place_t, _Args&&... __args)
-        : __val_(std::forward<_Args>(__args)...) {}
+    _LIBCPP_HIDE_FROM_ABI constexpr __union_t(const __union_t&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __union_t(const __union_t&)
+      requires(is_copy_constructible_v<_Err> && is_trivially_copy_constructible_v<_Err>)
+    = default;
+    _LIBCPP_HIDE_FROM_ABI constexpr __union_t(__union_t&&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __union_t(__union_t&&)
+      requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
+    = default;
+    _LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(const __union_t&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(__union_t&&)      = delete;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(in_place_t) : __empty_() {}
 
     template <class... _Args>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::unexpect_t, _Args&&... __args)
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(unexpect_t, _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)
-        : __val_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {}
-
-    template <class _Func, class... _Args>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(
-        std::__expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args)
+        __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(std::addressof(__val_), std::forward<_Union>(__other).__val_);
-      else
-        std::construct_at(std::addressof(__unex_), std::forward<_Union>(__other).__unex_);
-    }
-
     _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
-      requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>)
+      requires(is_trivially_destructible_v<_Err>)
     = default;
 
-    // the expected's destructor handles this
-    _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() {}
+    // __repr's destructor handles this
+    _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
+      requires(!is_trivially_destructible_v<_Err>)
+    {}
 
-    _ValueType __val_;
-    _ErrorType __unex_;
+    _LIBCPP_NO_UNIQUE_ADDRESS __empty_t __empty_;
+    _LIBCPP_NO_UNIQUE_ADDRESS _Err __unex_;
   };
 
-  // use named union because [[no_unique_address]] cannot be applied to an unnamed union,
-  // also guaranteed elision into a potentially-overlapping subobject is unsettled (and
-  // it's not clear that it's implementable, given that the function is allowed to clobber
-  // the tail padding) - see https://github.com/itanium-cxx-abi/cxx-abi/issues/107.
-  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(const __union_t&)            = default;
-    _LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(const __union_t&) = default;
+  static constexpr bool __put_flag_in_tail                    = __fits_in_tail_padding<__union_t, bool>;
+  static constexpr bool __allow_reusing_expected_tail_padding = !__put_flag_in_tail;
+
+  struct __repr {
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr() = delete;
 
     template <class... _Args>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::in_place_t, _Args&&... __args)
-        : __val_(std::forward<_Args>(__args)...) {}
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(in_place_t __tag) : __union_(in_place, __tag), __has_val_(true) {}
 
     template <class... _Args>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::unexpect_t, _Args&&... __args)
-        : __unex_(std::forward<_Args>(__args)...) {}
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(unexpect_t __tag, _Args&&... __args)
+        : __union_(in_place, __tag, std::forward<_Args>(__args)...), __has_val_(false) {}
 
-    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)
-        : __val_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {}
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(std::__expected_construct_unexpected_from_invoke_tag __tag,
+                                                    _Args&&... __args)
+        : __union_(in_place, __tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+    template <class _OtherUnion>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(bool __has_val, _OtherUnion&& __other)
+      requires(__allow_reusing_expected_tail_padding)
+        : __union_(__conditional_no_unique_address_invoke_tag{},
+                   [&] { return __make_union(__has_val, std::forward<_OtherUnion>(__other)); }),
+          __has_val_(__has_val) {}
+
+    _LIBCPP_HIDE_FROM_ABI constexpr __repr(const __repr&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __repr(const __repr&)
+      requires(is_copy_constructible_v<_Err> && is_trivially_copy_constructible_v<_Err>)
+    = default;
+    _LIBCPP_HIDE_FROM_ABI constexpr __repr(__repr&&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __repr(__repr&&)
+      requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
+    = default;
 
-    template <class _Func, class... _Args>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(
-        std::__expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args)
-        : __unex_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {}
+    _LIBCPP_HIDE_FROM_ABI constexpr __repr& operator=(const __repr&) = delete;
+    _LIBCPP_HIDE_FROM_ABI constexpr __repr& operator=(__repr&&)      = delete;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr ~__repr()
+      requires(is_trivially_destructible_v<_Err>)
+    = default;
 
-    template <class _Union>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
+    _LIBCPP_HIDE_FROM_ABI constexpr ~__repr()
+      requires(!is_trivially_destructible_v<_Err>)
+    {
+      __destroy_union_member();
+    }
+
+    _LIBCPP_HIDE_FROM_ABI constexpr void __destroy_union()
+      requires(__allow_reusing_expected_tail_padding && is_trivially_destructible_v<_Err>)
+    {
+      std::destroy_at(&__union_.__v);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI constexpr void __destroy_union()
+      requires(__allow_reusing_expected_tail_padding && !is_trivially_destructible_v<_Err>)
+    {
+      __destroy_union_member();
+      std::destroy_at(&__union_.__v);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI constexpr void __construct_union(in_place_t)
+      requires(__allow_reusing_expected_tail_padding)
+    {
+      std::construct_at(&__union_.__v, in_place);
+      __has_val_ = true;
+    }
+
+    template <class... _Args>
+    _LIBCPP_HIDE_FROM_ABI constexpr void __construct_union(unexpect_t, _Args&&... __args)
+      requires(__allow_reusing_expected_tail_padding)
+    {
+      std::construct_at(&__union_.__v, unexpect, std::forward<_Args>(__args)...);
+      __has_val_ = false;
+    }
+
+  private:
+    template <class>
+    friend class __expected_void_base;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr void __destroy_union_member()
+      requires(!is_trivially_destructible_v<_Err>)
+    {
+      if (!__has_val_)
+        std::destroy_at(std::addressof(__union_.__v.__unex_));
+    }
+
+    template <class _OtherUnion>
+    _LIBCPP_HIDE_FROM_ABI static constexpr __union_t __make_union(bool __has_val, _OtherUnion&& __other)
+      requires(__allow_reusing_expected_tail_padding)
+    {
       if (__has_val)
-        std::construct_at(std::addressof(__val_), std::forward<_Union>(__other).__val_);
+        return __union_t(in_place);
       else
-        std::construct_at(std::addressof(__unex_), std::forward<_Union>(__other).__unex_);
+        return __union_t(unexpect, std::forward<_OtherUnion>(__other).__unex_);
     }
 
-    _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
-      requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>)
-    = default;
+    _LIBCPP_NO_UNIQUE_ADDRESS __conditional_no_unique_address<__put_flag_in_tail, __union_t> __union_;
+    _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
+  };
 
-    // the expected's destructor handles this
-    _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
-      requires(!is_trivially_destructible_v<_ValueType> || !is_trivially_destructible_v<_ErrorType>)
-    {}
+  template <class _OtherUnion>
+  _LIBCPP_HIDE_FROM_ABI static constexpr __repr __make_repr(bool __has_val, _OtherUnion&& __other)
+    requires(__put_flag_in_tail)
+  {
+    if (__has_val)
+      return __repr(in_place);
+    else
+      return __repr(unexpect, std::forward<_OtherUnion>(__other).__unex_);
+  }
 
-    _LIBCPP_NO_UNIQUE_ADDRESS _ValueType __val_;
-    _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
-  };
+protected:
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_base(_Args&&... __args)
+      : __repr_(in_place, std::forward<_Args>(__args)...) {}
+
+  template <class _OtherUnion>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_base(bool __has_val, _OtherUnion&& __other)
+    requires(__put_flag_in_tail)
+      : __repr_(__conditional_no_unique_address_invoke_tag{},
+                [&] { return __make_repr(__has_val, std::forward<_OtherUnion>(__other)); }) {}
 
-  _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
-  bool __has_val_;
+  _LIBCPP_HIDE_FROM_ABI constexpr void __destroy() {
+    if constexpr (__put_flag_in_tail)
+      std::destroy_at(&__repr_.__v);
+    else
+      __repr_.__v.__destroy_union();
+  }
+
+  template <class _Tag, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __construct(_Tag __tag, _Args&&... __args) {
+    if constexpr (__put_flag_in_tail)
+      std::construct_at(&__repr_.__v, __tag, std::forward<_Args>(__args)...);
+    else
+      __repr_.__v.__construct_union(__tag, std::forward<_Args>(__args)...);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool __has_val() const { return __repr_.__v.__has_val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr __union_t& __union() { return __repr_.__v.__union_.__v; }
+  _LIBCPP_HIDE_FROM_ABI constexpr const __union_t& __union() const { return __repr_.__v.__union_.__v; }
+  _LIBCPP_HIDE_FROM_ABI constexpr _Err& __unex() { return __repr_.__v.__union_.__v.__unex_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Err& __unex() const { return __repr_.__v.__union_.__v.__unex_; }
+
+private:
+  _LIBCPP_NO_UNIQUE_ADDRESS __conditional_no_unique_address<__allow_reusing_expected_tail_padding, __repr> __repr_;
 };
 
 template <class _Tp, class _Err>
   requires is_void_v<_Tp>
-class expected<_Tp, _Err> {
+class expected<_Tp, _Err> : private __expected_void_base<_Err> {
   static_assert(__valid_std_unexpected<_Err>::value,
                 "[expected.void.general] A program that instantiates expected<T, E> with a E that is not a "
                 "valid argument for unexpected<E> is ill-formed");
@@ -935,6 +1372,8 @@ class expected<_Tp, _Err> {
             _Not<is_constructible<unexpected<_Err>, const expected<_Up, _OtherErr>&>>,
             _Not<is_constructible<unexpected<_Err>, const expected<_Up, _OtherErr>>>>;
 
+  using __base = __expected_void_base<_Err>;
+
 public:
   using value_type      = _Tp;
   using error_type      = _Err;
@@ -944,7 +1383,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 : __base(in_place) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
 
@@ -955,7 +1394,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_) {}
+      : __base(__rhs.__has_val(), __rhs.__union()) {}
 
   _LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
     requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
@@ -963,93 +1402,93 @@ 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_) {}
+      : __base(__rhs.__has_val(), std::move(__rhs.__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
-      : __union_(__rhs.__has_val_, __rhs.__union_), __has_val_(__rhs.__has_val_) {}
+      : __base(__rhs.__has_val(), __rhs.__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
-      : __union_(__rhs.__has_val_, std::move(__rhs.__union_)), __has_val_(__rhs.__has_val_) {}
+      : __base(__rhs.__has_val(), std::move(__rhs.__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
-      : __union_(std::unexpect, __unex.error()), __has_val_(false) {}
+      : __base(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
-      : __union_(std::unexpect, std::move(__unex.error())), __has_val_(false) {}
+      : __base(unexpect, std::move(__unex.error())) {}
 
-  _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 : __base(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
-      : __union_(std::unexpect, std::forward<_Args>(__args)...), __has_val_(false) {}
+      : __base(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
-      : __union_(std::unexpect, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
+      : __base(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)
-      : __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) {}
+      : __base(__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 (!__has_val_) {
-      std::destroy_at(std::addressof(__union_.__unex_));
-    }
+private:
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __reinit_expected(unexpect_t, _Args&&... __args) {
+    _LIBCPP_ASSERT_INTERNAL(this->__has_val(), "__reinit_expected(unexpect_t, ...) needs value to be set");
+
+    this->__destroy();
+    auto __trans = std::__make_exception_guard([&] { this->__construct(in_place); });
+    this->__construct(unexpect, std::forward<_Args>(__args)...);
+    __trans.__complete();
   }
 
-  // [expected.void.assign], assignment
+  _LIBCPP_HIDE_FROM_ABI constexpr void __reinit_expected(in_place_t) {
+    _LIBCPP_ASSERT_INTERNAL(!this->__has_val(), "__reinit_expected(in_place_t, ...) needs value to be unset");
 
+    this->__destroy();
+    this->__construct(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) 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 (this->__has_val()) {
+      if (!__rhs.__has_val()) {
+        __reinit_expected(unexpect, __rhs.__unex());
       }
     } else {
-      if (__rhs.__has_val_) {
-        std::destroy_at(std::addressof(__union_.__unex_));
-        __has_val_ = true;
+      if (__rhs.__has_val()) {
+        __reinit_expected(in_place);
       } else {
-        __union_.__unex_ = __rhs.__union_.__unex_;
+        this->__unex() = __rhs.__unex();
       }
     }
     return *this;
@@ -1061,17 +1500,15 @@ class expected<_Tp, _Err> {
   operator=(expected&& __rhs) noexcept(is_nothrow_move_assignable_v<_Err> && is_nothrow_move_constructible_v<_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 (this->__has_val()) {
+      if (!__rhs.__has_val()) {
+        __reinit_expected(unexpect, std::move(__rhs.__unex()));
       }
     } else {
-      if (__rhs.__has_val_) {
-        std::destroy_at(std::addressof(__union_.__unex_));
-        __has_val_ = true;
+      if (__rhs.__has_val()) {
+        __reinit_expected(in_place);
       } else {
-        __union_.__unex_ = std::move(__rhs.__union_.__unex_);
+        this->__unex() = std::move(__rhs.__unex());
       }
     }
     return *this;
@@ -1080,11 +1517,10 @@ 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 (this->__has_val()) {
+      __reinit_expected(unexpect, __un.error());
     } else {
-      __union_.__unex_ = __un.error();
+      this->__unex() = __un.error();
     }
     return *this;
   }
@@ -1092,19 +1528,17 @@ 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 (this->__has_val()) {
+      __reinit_expected(unexpect, std::move(__un.error()));
     } else {
-      __union_.__unex_ = std::move(__un.error());
+      this->__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 (!this->__has_val()) {
+      __reinit_expected(in_place);
     }
   }
 
@@ -1113,23 +1547,23 @@ class expected<_Tp, _Err> {
   swap(expected& __rhs) 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.__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;
+    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(unexpect, std::move(__with_err.__unex()));
+      // Will not throw.
+      __with_err.__reinit_expected(in_place);
     };
 
-    if (__has_val_) {
-      if (!__rhs.__has_val_) {
+    if (this->__has_val()) {
+      if (!__rhs.__has_val()) {
         __swap_val_unex_impl(*this, __rhs);
       }
     } else {
-      if (__rhs.__has_val_) {
+      if (__rhs.__has_val()) {
         __swap_val_unex_impl(__rhs, *this);
       } else {
         using std::swap;
-        swap(__union_.__unex_, __rhs.__union_.__unex_);
+        swap(this->__unex(), __rhs.__unex());
       }
     }
   }
@@ -1141,46 +1575,51 @@ 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 this->__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 this->__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(
+        this->__has_val(), "expected::operator* requires the expected to contain a value");
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr void value() const& {
     static_assert(is_copy_constructible_v<_Err>);
-    if (!__has_val_) {
-      std::__throw_bad_expected_access<_Err>(__union_.__unex_);
+    if (!this->__has_val()) {
+      std::__throw_bad_expected_access<_Err>(this->__unex());
     }
   }
 
   _LIBCPP_HIDE_FROM_ABI constexpr void value() && {
     static_assert(is_copy_constructible_v<_Err> && is_move_constructible_v<_Err>);
-    if (!__has_val_) {
-      std::__throw_bad_expected_access<_Err>(std::move(__union_.__unex_));
+    if (!this->__has_val()) {
+      std::__throw_bad_expected_access<_Err>(std::move(this->__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(
+        !this->__has_val(), "expected::error requires the expected to contain an error");
+    return this->__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(
+        !this->__has_val(), "expected::error requires the expected to contain an error");
+    return this->__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(
+        !this->__has_val(), "expected::error requires the expected to contain an error");
+    return std::move(this->__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(
+        !this->__has_val(), "expected::error requires the expected to contain an error");
+    return std::move(this->__unex());
   }
 
   template <class _Up = _Err>
@@ -1416,96 +1855,17 @@ 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.__has_val() != __y.__has_val()) {
       return false;
     } else {
-      return __x.__has_val_ || static_cast<bool>(__x.__union_.__unex_ == __y.__union_.__unex_);
+      return __x.__has_val() || static_cast<bool>(__x.__unex() == __y.__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.__has_val() && static_cast<bool>(__x.__unex() == __y.error());
   }
-
-private:
-  struct __empty_t {};
-
-  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(std::unexpect_t, _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)
-        : __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(std::addressof(__empty_));
-      else
-        std::construct_at(std::addressof(__unex_), std::forward<_Union>(__other).__unex_);
-    }
-
-    _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
-      requires(is_trivially_destructible_v<_ErrorType>)
-    = default;
-
-    // the expected's destructor handles this
-    _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() {}
-
-    __empty_t __empty_;
-    _ErrorType __unex_;
-  };
-
-  // use named union because [[no_unique_address]] cannot be applied to an unnamed union,
-  // also guaranteed elision into a potentially-overlapping subobject is unsettled (and
-  // it's not clear that it's implementable, given that the function is allowed to clobber
-  // the tail padding) - see https://github.com/itanium-cxx-abi/cxx-abi/issues/107.
-  template <class _ErrorType>
-    requires is_trivially_move_constructible_v<_ErrorType>
-  union __union_t<_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;
-
-    template <class... _Args>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::unexpect_t, _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)
-        : __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(std::addressof(__empty_));
-      else
-        std::construct_at(std::addressof(__unex_), std::forward<_Union>(__other).__unex_);
-    }
-
-    _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
-      requires(is_trivially_destructible_v<_ErrorType>)
-    = default;
-
-    // the expected's destructor handles this
-    _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
-      requires(!is_trivially_destructible_v<_ErrorType>)
-    {}
-
-    _LIBCPP_NO_UNIQUE_ADDRESS __empty_t __empty_;
-    _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
-  };
-
-  _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
-  bool __has_val_;
 };
 
 _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 c8c217054ae6c4..cf1909b9287323 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
@@ -12,7 +12,10 @@
 
 // test [[no_unique_address]] is applied to the union
 
+#include <__type_traits/datasizeof.h>
 #include <expected>
+#include <optional>
+#include <memory>
 
 struct Empty {};
 
@@ -23,13 +26,49 @@ struct A {
 
 struct B : public A {
   int z_;
+  short z2_;
   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>));
+static_assert(sizeof(std::expected<Empty, B>) == sizeof(B));
 static_assert(sizeof(std::expected<A, Empty>) == 2 * sizeof(int) + alignof(std::expected<A, Empty>));
 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>));
+static_assert(sizeof(std::expected<B, Empty>) == sizeof(B));
+static_assert(sizeof(std::expected<B, B>) == sizeof(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);
+
+// Test with some real types.
+static_assert(sizeof(std::expected<std::optional<int>, int>) == 8);
+static_assert(std::__libcpp_datasizeof<std::expected<std::optional<int>, int>>::value == 8);
+
+static_assert(sizeof(std::expected<int, std::optional<int>>) == 8);
+static_assert(std::__libcpp_datasizeof<std::expected<int, std::optional<int>>>::value == 8);
+
+static_assert(sizeof(std::expected<int, int>) == 8);
+static_assert(std::__libcpp_datasizeof<std::expected<int, int>>::value == 5);
+
+// clang-format off
+static_assert(std::__libcpp_datasizeof<int>::value == 4);
+static_assert(std::__libcpp_datasizeof<std::expected<int, int>>::value == 5);
+static_assert(std::__libcpp_datasizeof<std::expected<std::expected<int, int>, int>>::value == 8);
+static_assert(std::__libcpp_datasizeof<std::expected<std::expected<std::expected<int, int>, int>, int>>::value == 9);
+static_assert(std::__libcpp_datasizeof<std::expected<std::expected<std::expected<std::expected<int, int>, int>, int>, int>>::value == 12);
+// clang-format on

diff  --git a/libcxx/test/libcxx/utilities/expected/expected.expected/transform_error.mandates.verify.cpp b/libcxx/test/libcxx/utilities/expected/expected.expected/transform_error.mandates.verify.cpp
index 82024a068f00d1..5bf094bf37709d 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.expected/transform_error.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.expected/transform_error.mandates.verify.cpp
@@ -7,11 +7,15 @@
 //===----------------------------------------------------------------------===//
 
 // Clang-18 fixed some spurious clang diagnostics. Once clang-18 is the
-// minumum required version these obsolete tests can be removed.
+// minimum required version these obsolete tests can be removed.
 // TODO(LLVM-20) remove spurious clang diagnostic tests.
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
+// With clang-cl, some warnings have a 'which is a Microsoft extension' suffix
+// which break the tests.
+// XFAIL: msvc
+
 // Test the mandates
 
 // template<class F> constexpr auto transform_error(F&& f) &;
@@ -52,6 +56,7 @@ void test() {
     e.transform_error(return_unexpected<int&>); // expected-error-re@*:* {{static assertion failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}}
     // expected-error-re@*:* 0-1 {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}}
     // expected-error-re@*:* {{static assertion failed {{.*}}[expected.object.general] A program that instantiates the definition of template expected<T, E> for {{.*}} is ill-formed.}}
+    // expected-error-re@*:* {{union member {{.*}} has reference type {{.*}}}}
 
     e.transform_error(return_no_object<int&>); // expected-error-re@*:* {{static assertion failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}}
     // expected-error-re@*:* 0-1 {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}}

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 dc59a6228386bc..fdee8b71e5d983 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
@@ -11,8 +11,13 @@
 // XFAIL: msvc
 
 // test [[no_unique_address]] is applied to the union
+// In particular, we ensure that we reuse tail padding in the T
+// to store the discriminator whenever we can.
 
+#include <__type_traits/datasizeof.h>
 #include <expected>
+#include <optional>
+#include <memory>
 
 struct Empty {};
 
@@ -23,9 +28,40 @@ struct A {
 
 struct B : public A {
   int z_;
+  short z2_;
   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>));
+static_assert(sizeof(std::expected<void, B>) == sizeof(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);
+
+// Test with some real types.
+static_assert(sizeof(std::expected<void, std::optional<int>>) == 8);
+static_assert(std::__libcpp_datasizeof<std::expected<void, std::optional<int>>>::value == 8);
+
+static_assert(sizeof(std::expected<void, int>) == 8);
+static_assert(std::__libcpp_datasizeof<std::expected<void, int>>::value == 5);
+
+// clang-format off
+static_assert(std::__libcpp_datasizeof<int>::value == 4);
+static_assert(std::__libcpp_datasizeof<std::expected<void, int>>::value == 5);
+static_assert(std::__libcpp_datasizeof<std::expected<void, std::expected<void, int>>>::value == 8);
+static_assert(std::__libcpp_datasizeof<std::expected<void, std::expected<void, std::expected<void, int>>>>::value == 9);
+static_assert(std::__libcpp_datasizeof<std::expected<void, std::expected<void, std::expected<void, std::expected<void, int>>>>>::value == 12);
+// clang-format on

diff  --git a/libcxx/test/libcxx/utilities/expected/expected.void/transform_error.mandates.verify.cpp b/libcxx/test/libcxx/utilities/expected/expected.void/transform_error.mandates.verify.cpp
index 0f2fb581ccd705..4f4f5839361e2a 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.void/transform_error.mandates.verify.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.void/transform_error.mandates.verify.cpp
@@ -12,6 +12,10 @@
 
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
+// With clang-cl, some warnings have a 'which is a Microsoft extension' suffix
+// which break the tests.
+// XFAIL: msvc
+
 // Test the mandates
 
 // template<class F> constexpr auto transform_error(F&& f) &;
@@ -52,6 +56,8 @@ void test() {
     e.transform_error(return_unexpected<int&>); // expected-error-re@*:* {{static assertion failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}}
     // expected-error-re@*:* 0-1 {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}}
     // expected-error-re@*:* {{static assertion failed {{.*}}A program that instantiates expected<T, E> with a E that is not a valid argument for unexpected<E> is ill-formed}}
+    // expected-error-re@*:* {{call to deleted constructor of {{.*}}}}
+    // expected-error-re@*:* {{union member {{.*}} has reference type {{.*}}}}
 
     e.transform_error(return_no_object<int&>); // expected-error-re@*:* {{static assertion failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}}
     // expected-error-re@*:* 0-1 {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}}

diff  --git a/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp
index 1c8750b7acd100..2d3b03689b1d91 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp
@@ -310,6 +310,20 @@ constexpr bool test() {
     assert(e.value().j == 8);
   }
 
+  // CheckForInvalidWrites
+  {
+    {
+      CheckForInvalidWrites<true> e1(std::unexpect);
+      e1 = 42;
+      assert(e1.check());
+    }
+    {
+      CheckForInvalidWrites<false> e1(std::unexpect);
+      e1 = true;
+      assert(e1.check());
+    }
+  }
+
   return true;
 }
 

diff  --git a/libcxx/test/std/utilities/expected/expected.expected/assign/assign.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/assign/assign.copy.pass.cpp
index 03fe888b0a5e7f..2f5291332355ca 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/assign/assign.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/assign/assign.copy.pass.cpp
@@ -240,6 +240,29 @@ constexpr bool test() {
     assert(e1.error().data_ == 10);
     assert(oldState.copyAssignCalled);
   }
+
+  // CheckForInvalidWrites
+  {
+    {
+      CheckForInvalidWrites<true> e1(std::unexpect);
+      CheckForInvalidWrites<true> e2;
+
+      e1 = e2;
+
+      assert(e1.check());
+      assert(e2.check());
+    }
+    {
+      CheckForInvalidWrites<false> e1(std::unexpect);
+      CheckForInvalidWrites<false> e2;
+
+      e1 = e2;
+
+      assert(e1.check());
+      assert(e2.check());
+    }
+  }
+
   return true;
 }
 

diff  --git a/libcxx/test/std/utilities/expected/expected.expected/assign/assign.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/assign/assign.move.pass.cpp
index 8c419afd10729f..065827ae652c98 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/assign/assign.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/assign/assign.move.pass.cpp
@@ -258,6 +258,29 @@ constexpr bool test() {
     assert(e1.error().data_ == 10);
     assert(oldState.moveAssignCalled);
   }
+
+  // CheckForInvalidWrites
+  {
+    {
+      CheckForInvalidWrites<true> e1(std::unexpect);
+      CheckForInvalidWrites<true> e2;
+
+      e1 = std::move(e2);
+
+      assert(e1.check());
+      assert(e2.check());
+    }
+    {
+      CheckForInvalidWrites<false> e1(std::unexpect);
+      CheckForInvalidWrites<false> e2;
+
+      e1 = std::move(e2);
+
+      assert(e1.check());
+      assert(e2.check());
+    }
+  }
+
   return true;
 }
 

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 491de2dff03312..7e37f8bdf4abc0 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
@@ -80,6 +80,20 @@ constexpr bool test() {
     assert(e.has_value());
   }
 
+  // CheckForInvalidWrites
+  {
+    {
+      CheckForInvalidWrites<true> e;
+      e.emplace();
+      assert(e.check());
+    }
+    {
+      CheckForInvalidWrites<false> e;
+      e.emplace();
+      assert(e.check());
+    }
+  }
+
   return true;
 }
 

diff  --git a/libcxx/test/std/utilities/expected/expected.expected/monadic/transform.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/monadic/transform.pass.cpp
index f443613fe7f343..d38a46f0452485 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/monadic/transform.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/monadic/transform.pass.cpp
@@ -9,8 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 // GCC has a issue for `Guaranteed copy elision for potentially-overlapping non-static data members`,
-// please refer to: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108333, but we have a workaround to
-// avoid this issue.
+// please refer to: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108333
+// XFAIL: gcc-13
 
 // <expected>
 

diff  --git a/libcxx/test/std/utilities/expected/expected.expected/monadic/transform_error.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/monadic/transform_error.pass.cpp
index 84b57aea3cd231..ec55f637f02020 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/monadic/transform_error.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/monadic/transform_error.pass.cpp
@@ -9,8 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 // GCC has a issue for `Guaranteed copy elision for potentially-overlapping non-static data members`,
-// please refer to: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108333, but we have a workaround to
-// avoid this issue.
+// please refer to: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108333.
+// XFAIL: gcc-13
 
 // <expected>
 

diff  --git a/libcxx/test/std/utilities/expected/expected.expected/swap/member.swap.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/swap/member.swap.pass.cpp
index 34782303909e2a..f19599d43b32df 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/swap/member.swap.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/swap/member.swap.pass.cpp
@@ -227,6 +227,28 @@ constexpr bool test() {
     }
   }
 
+  // CheckForInvalidWrites
+  {
+    {
+      CheckForInvalidWrites<true> x(std::unexpect);
+      CheckForInvalidWrites<true> y;
+
+      x.swap(y);
+
+      assert(x.check());
+      assert(y.check());
+    }
+    {
+      CheckForInvalidWrites<false> x(std::unexpect);
+      CheckForInvalidWrites<false> y;
+
+      x.swap(y);
+
+      assert(x.check());
+      assert(y.check());
+    }
+  }
+
   return true;
 }
 

diff  --git a/libcxx/test/std/utilities/expected/expected.void/assign/assign.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/assign/assign.copy.pass.cpp
index b1968bcc242403..a51916f874eeab 100644
--- a/libcxx/test/std/utilities/expected/expected.void/assign/assign.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/assign/assign.copy.pass.cpp
@@ -99,6 +99,28 @@ constexpr bool test() {
     assert(state.copyAssignCalled);
   }
 
+  // CheckForInvalidWrites
+  {
+    {
+      CheckForInvalidWrites<true, true> e1;
+      CheckForInvalidWrites<true, true> e2(std::unexpect);
+
+      e1 = e2;
+
+      assert(e1.check());
+      assert(e2.check());
+    }
+    {
+      CheckForInvalidWrites<false, true> e1;
+      CheckForInvalidWrites<false, true> e2(std::unexpect);
+
+      e1 = e2;
+
+      assert(e1.check());
+      assert(e2.check());
+    }
+  }
+
   return true;
 }
 

diff  --git a/libcxx/test/std/utilities/expected/expected.void/assign/assign.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/assign/assign.move.pass.cpp
index e6a29cd62583be..60ae0346f7af1e 100644
--- a/libcxx/test/std/utilities/expected/expected.void/assign/assign.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/assign/assign.move.pass.cpp
@@ -125,6 +125,28 @@ constexpr bool test() {
     assert(state.moveAssignCalled);
   }
 
+  // CheckForInvalidWrites
+  {
+    {
+      CheckForInvalidWrites<true, true> e1;
+      CheckForInvalidWrites<true, true> e2(std::unexpect);
+
+      e1 = std::move(e2);
+
+      assert(e1.check());
+      assert(e2.check());
+    }
+    {
+      CheckForInvalidWrites<false, true> e1;
+      CheckForInvalidWrites<false, true> e2(std::unexpect);
+
+      e1 = std::move(e2);
+
+      assert(e1.check());
+      assert(e2.check());
+    }
+  }
+
   return true;
 }
 

diff  --git a/libcxx/test/std/utilities/expected/expected.void/assign/assign.unexpected.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/assign/assign.unexpected.copy.pass.cpp
index 1ae96530e528e8..699597d243c19b 100644
--- a/libcxx/test/std/utilities/expected/expected.void/assign/assign.unexpected.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/assign/assign.unexpected.copy.pass.cpp
@@ -91,6 +91,22 @@ constexpr bool test() {
     assert(state1.copyAssignCalled);
   }
 
+  // CheckForInvalidWrites
+  {
+    {
+      CheckForInvalidWrites<true, true> e;
+      std::unexpected<int> un(std::in_place, 42);
+      e = un;
+      assert(e.check());
+    }
+    {
+      CheckForInvalidWrites<false, true> e;
+      std::unexpected<bool> un(std::in_place, true);
+      e = un;
+      assert(e.check());
+    }
+  }
+
   return true;
 }
 

diff  --git a/libcxx/test/std/utilities/expected/expected.void/assign/assign.unexpected.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/assign/assign.unexpected.move.pass.cpp
index ea947715af28f1..641eb4927a724e 100644
--- a/libcxx/test/std/utilities/expected/expected.void/assign/assign.unexpected.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/assign/assign.unexpected.move.pass.cpp
@@ -172,6 +172,23 @@ constexpr bool test() {
     assert(e1.error().data_ == 10);
     assert(oldState.moveAssignCalled);
   }
+
+  // CheckForInvalidWrites
+  {
+    {
+      CheckForInvalidWrites<true, true> e;
+      std::unexpected<int> un(std::in_place, 42);
+      e = std::move(un);
+      assert(e.check());
+    }
+    {
+      CheckForInvalidWrites<false, true> e;
+      std::unexpected<bool> un(std::in_place, true);
+      e = std::move(un);
+      assert(e.check());
+    }
+  }
+
   return true;
 }
 

diff  --git a/libcxx/test/std/utilities/expected/expected.void/monadic/transform_error.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/monadic/transform_error.pass.cpp
index f0e19ac3c39827..cd6e5a5038d28d 100644
--- a/libcxx/test/std/utilities/expected/expected.void/monadic/transform_error.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/monadic/transform_error.pass.cpp
@@ -9,8 +9,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 // GCC has a issue for `Guaranteed copy elision for potentially-overlapping non-static data members`,
-// please refer to: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108333, but we have a workaround to
-// avoid this issue.
+// please refer to: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108333
+// XFAIL: gcc-13
 
 // <expected>
 

diff  --git a/libcxx/test/std/utilities/expected/expected.void/swap/member.swap.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/swap/member.swap.pass.cpp
index 07980dee13c1e9..25601af39a3162 100644
--- a/libcxx/test/std/utilities/expected/expected.void/swap/member.swap.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/swap/member.swap.pass.cpp
@@ -139,6 +139,28 @@ constexpr bool test() {
     assert(y.has_value());
   }
 
+  // CheckForInvalidWrites
+  {
+    {
+      CheckForInvalidWrites<true, true> x(std::unexpect);
+      CheckForInvalidWrites<true, true> y;
+
+      x.swap(y);
+
+      assert(x.check());
+      assert(y.check());
+    }
+    {
+      CheckForInvalidWrites<false, true> x(std::unexpect);
+      CheckForInvalidWrites<false, true> y;
+
+      x.swap(y);
+
+      assert(x.check());
+      assert(y.check());
+    }
+  }
+
   return true;
 }
 

diff  --git a/libcxx/test/std/utilities/expected/types.h b/libcxx/test/std/utilities/expected/types.h
index 90768ed8e7520b..2b6983fb399c67 100644
--- a/libcxx/test/std/utilities/expected/types.h
+++ b/libcxx/test/std/utilities/expected/types.h
@@ -199,4 +199,141 @@ static_assert(!std::is_trivially_move_constructible_v<TailClobbererNonTrivialMov
 static_assert(std::is_nothrow_move_constructible_v<TailClobbererNonTrivialMove<0, true>>);
 static_assert(!std::is_nothrow_move_constructible_v<TailClobbererNonTrivialMove<0, false>>);
 
+// The `CheckForInvalidWrites` class recreates situations where other objects
+// may be placed into a `std::expected`'s tail padding (see
+// https://github.com/llvm/llvm-project/issues/70494). With a template
+// parameter `WithPaddedExpected` two cases can be tested:
+//
+// 1. The `std::expected<T, E>` itself has padding, because `T`/`E` _don't_
+//    have tail padding. This is modelled by `CheckForInvalidWrites<true>`
+//    which has a (potential) data layout like this:
+//
+//                +- `expected`'s "has value" flag
+//                |
+//                |             +- `please_dont_overwrite_me`
+//                |             |
+//    /---int---\ |  /----------^-------\                                    //
+//    00 00 00 00 01 01 01 01 01 01 01 01
+//                   \--v---/
+//                      |
+//                      |
+//                      +- `expected`'s tail padding which
+//                         gets repurposed by `please_dont_overwrite_me`
+//
+// 2. There is tail padding in the union of `T` and `E` which means the
+//    "has value" flag can be put into this tail padding. In this case, the
+//    `std::expected` itself _must not_ have any tail padding as it may get
+//    overwritten on mutating operations such as `emplace()`. This case is
+//    modelled by `CheckForInvalidWrites<false>` with a (potential) data
+//    layout like this:
+//
+//    +- bool
+//    |                                +- please_dont_overwrite_me
+//    |  +- "has value" flag           |
+//    |  |                    /--------^---------\                           //
+//    00 00 00 00 00 00 00 00 01 01 01 01 01 01 01 00
+//          \---padding-----/                      |
+//                                                 +- `CheckForInvalidWrites`
+//                                                    padding
+//
+// Note that other implementation strategies are viable, including one that
+// doesn't make use of `[[no_unique_address]]`. But if an implementation uses
+// the strategy above, it must make sure that those tail padding bytes are not
+// overwritten improperly on operations such as `emplace()`.
+
+struct BoolWithPadding {
+  constexpr explicit BoolWithPadding() noexcept : BoolWithPadding(false) {}
+  constexpr BoolWithPadding(bool val) noexcept {
+    if (!std::is_constant_evaluated()) {
+      std::memset(this, 0, sizeof(*this));
+    }
+    val_ = val;
+  }
+  constexpr BoolWithPadding(const BoolWithPadding& other) noexcept : BoolWithPadding(other.val_) {}
+  constexpr BoolWithPadding& operator=(const BoolWithPadding& other) noexcept {
+    val_ = other.val_;
+    return *this;
+  }
+  // The previous data layout of libc++'s `expected` required `T` to be
+  // trivially move constructible to employ the `[[no_unique_address]]`
+  // optimization. To trigger bugs with the old implementation, make
+  // `BoolWithPadding` trivially move constructible.
+  constexpr BoolWithPadding(BoolWithPadding&&) = default;
+
+private:
+  alignas(8) bool val_;
+};
+
+struct IntWithoutPadding {
+  constexpr explicit IntWithoutPadding() noexcept : IntWithoutPadding(0) {}
+  constexpr IntWithoutPadding(int val) noexcept {
+    if (!std::is_constant_evaluated()) {
+      std::memset(this, 0, sizeof(*this));
+    }
+    val_ = val;
+  }
+  constexpr IntWithoutPadding(const IntWithoutPadding& other) noexcept : IntWithoutPadding(other.val_) {}
+  constexpr IntWithoutPadding& operator=(const IntWithoutPadding& other) noexcept {
+    val_ = other.val_;
+    return *this;
+  }
+  // See comment on `BoolWithPadding`.
+  constexpr IntWithoutPadding(IntWithoutPadding&&) = default;
+
+private:
+  int val_;
+};
+
+template <bool WithPaddedExpected, bool ExpectedVoid>
+struct CheckForInvalidWritesBaseImpl;
+template <>
+struct CheckForInvalidWritesBaseImpl<true, false> {
+  using type = std::expected<IntWithoutPadding, bool>;
+};
+template <>
+struct CheckForInvalidWritesBaseImpl<false, false> {
+  using type = std::expected<BoolWithPadding, bool>;
+};
+template <>
+struct CheckForInvalidWritesBaseImpl<true, true> {
+  using type = std::expected<void, IntWithoutPadding>;
+};
+template <>
+struct CheckForInvalidWritesBaseImpl<false, true> {
+  using type = std::expected<void, BoolWithPadding>;
+};
+
+template <bool WithPaddedExpected, bool ExpectedVoid>
+using CheckForInvalidWritesBase = typename CheckForInvalidWritesBaseImpl<WithPaddedExpected, ExpectedVoid>::type;
+
+template <bool WithPaddedExpected, bool ExpectedVoid = false>
+struct CheckForInvalidWrites : public CheckForInvalidWritesBase<WithPaddedExpected, ExpectedVoid> {
+  constexpr CheckForInvalidWrites() = default;
+  constexpr CheckForInvalidWrites(std::unexpect_t)
+      : CheckForInvalidWritesBase<WithPaddedExpected, ExpectedVoid>(std::unexpect) {}
+
+  constexpr CheckForInvalidWrites& operator=(const CheckForInvalidWrites& other) {
+    CheckForInvalidWritesBase<WithPaddedExpected, ExpectedVoid>::operator=(other);
+    return *this;
+  }
+
+  constexpr CheckForInvalidWrites& operator=(CheckForInvalidWrites&& other) {
+    CheckForInvalidWritesBase<WithPaddedExpected, ExpectedVoid>::operator=(std::move(other));
+    return *this;
+  }
+
+  using CheckForInvalidWritesBase<WithPaddedExpected, ExpectedVoid>::operator=;
+
+  const bool please_dont_overwrite_me[7] = {true, true, true, true, true, true, true};
+
+  constexpr bool check() {
+    for (bool i : please_dont_overwrite_me) {
+      if (!i) {
+        return false;
+      }
+    }
+    return true;
+  }
+};
+
 #endif // TEST_STD_UTILITIES_EXPECTED_TYPES_H


        


More information about the libcxx-commits mailing list