[libcxx-commits] [libcxx] [libc++] Ensure that `std::expected` has no tail padding (PR #69673)
Jan Kokemüller via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Oct 24 09:21:42 PDT 2023
https://github.com/jiixyj updated https://github.com/llvm/llvm-project/pull/69673
>From b74ea37d1867af42dfd698874df7bcfc82fbc02c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 10 Oct 2023 20:24:06 +0200
Subject: [PATCH 01/38] [libc++] Fix UB in <expected> related to "has value"
flag (#68552)
The calls to `std::construct_at` might overwrite the previously set
`__has_value_` flag, so reverse the order everywhere. Where possible,
avoid calling `std::construct_at` and construct the value/error
directly into the union.
---
libcxx/include/__expected/expected.h | 168 ++++++++----------
.../observers/has_value.pass.cpp | 39 ++++
2 files changed, 117 insertions(+), 90 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 045370a486fae6b..08f35b1111f6bf9 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -119,9 +119,7 @@ class expected {
_LIBCPP_HIDE_FROM_ABI constexpr expected()
noexcept(is_nothrow_default_constructible_v<_Tp>) // strengthened
requires is_default_constructible_v<_Tp>
- : __has_val_(true) {
- std::construct_at(std::addressof(__union_.__val_));
- }
+ : __union_(__construct_in_place_tag{}), __has_val_(true) {}
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
@@ -136,14 +134,7 @@ class expected {
noexcept(is_nothrow_copy_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Err>) // strengthened
requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err> &&
!(is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Err>))
- : __has_val_(__other.__has_val_) {
- if (__has_val_) {
- std::construct_at(std::addressof(__union_.__val_), __other.__union_.__val_);
- } else {
- std::construct_at(std::addressof(__union_.__unex_), __other.__union_.__unex_);
- }
- }
-
+ : __union_(__union_from_expected(__other)), __has_val_(__other.__has_val_) { }
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err>
@@ -154,13 +145,7 @@ class expected {
noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_move_constructible_v<_Err>)
requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err> &&
!(is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>))
- : __has_val_(__other.__has_val_) {
- if (__has_val_) {
- std::construct_at(std::addressof(__union_.__val_), std::move(__other.__union_.__val_));
- } else {
- std::construct_at(std::addressof(__union_.__unex_), std::move(__other.__union_.__unex_));
- }
- }
+ : __union_(__union_from_expected(std::move(__other))), __has_val_(__other.__has_val_) { }
private:
template <class _Up, class _OtherErr, class _UfQual, class _OtherErrQual>
@@ -200,26 +185,14 @@ class expected {
expected(const expected<_Up, _OtherErr>& __other)
noexcept(is_nothrow_constructible_v<_Tp, const _Up&> &&
is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __has_val_(__other.__has_val_) {
- if (__has_val_) {
- std::construct_at(std::addressof(__union_.__val_), __other.__union_.__val_);
- } else {
- std::construct_at(std::addressof(__union_.__unex_), __other.__union_.__unex_);
- }
- }
+ : __union_(__union_from_expected(__other)), __has_val_(__other.__has_val_) {}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, _Up, _OtherErr>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp> || !is_convertible_v<_OtherErr, _Err>)
expected(expected<_Up, _OtherErr>&& __other)
noexcept(is_nothrow_constructible_v<_Tp, _Up> && is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __has_val_(__other.__has_val_) {
- if (__has_val_) {
- std::construct_at(std::addressof(__union_.__val_), std::move(__other.__union_.__val_));
- } else {
- std::construct_at(std::addressof(__union_.__unex_), std::move(__other.__union_.__unex_));
- }
- }
+ : __union_(__union_from_expected(std::move(__other))), __has_val_(__other.__has_val_) {}
template <class _Up = _Tp>
requires(!is_same_v<remove_cvref_t<_Up>, in_place_t> && !is_same_v<expected, remove_cvref_t<_Up>> &&
@@ -227,61 +200,47 @@ class expected {
(!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_expected<remove_cvref_t<_Up>>::value))
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp>)
expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
- : __has_val_(true) {
- std::construct_at(std::addressof(__union_.__val_), std::forward<_Up>(__u));
- }
+ : __union_(__construct_in_place_tag{}, std::forward<_Up>(__u)), __has_val_(true) {}
template <class _OtherErr>
requires is_constructible_v<_Err, const _OtherErr&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const unexpected<_OtherErr>& __unex)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __has_val_(false) {
- std::construct_at(std::addressof(__union_.__unex_), __unex.error());
- }
+ : __union_(__construct_unexpected_tag{}, __unex.error()), __has_val_(false) {}
template <class _OtherErr>
requires is_constructible_v<_Err, _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(unexpected<_OtherErr>&& __unex)
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __has_val_(false) {
- std::construct_at(std::addressof(__union_.__unex_), std::move(__unex.error()));
- }
+ : __union_(__construct_unexpected_tag{}, std::move(__unex.error())), __has_val_(false) {}
template <class... _Args>
requires is_constructible_v<_Tp, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Tp, _Args...>) // strengthened
- : __has_val_(true) {
- std::construct_at(std::addressof(__union_.__val_), std::forward<_Args>(__args)...);
- }
+ : __union_(__construct_in_place_tag{}, std::forward<_Args>(__args)...), __has_val_(true) {}
template <class _Up, class... _Args>
requires is_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit
expected(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, _Args...>) // strengthened
- : __has_val_(true) {
- std::construct_at(std::addressof(__union_.__val_), __il, std::forward<_Args>(__args)...);
- }
+ : __union_(__construct_in_place_tag{}, __il, std::forward<_Args>(__args)...), __has_val_(true) {}
template <class... _Args>
requires is_constructible_v<_Err, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
- noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
- : __has_val_(false) {
- std::construct_at(std::addressof(__union_.__unex_), std::forward<_Args>(__args)...);
- }
+ noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
+ : __union_(__construct_unexpected_tag{}, std::forward<_Args>(__args)...), __has_val_(false) {}
template <class _Up, class... _Args>
requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit
expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
- : __has_val_(false) {
- std::construct_at(std::addressof(__union_.__unex_), __il, std::forward<_Args>(__args)...);
- }
+ : __union_(__construct_unexpected_tag{}, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
// [expected.object.dtor], destructor
@@ -440,9 +399,10 @@ class expected {
std::destroy_at(std::addressof(__union_.__val_));
} else {
std::destroy_at(std::addressof(__union_.__unex_));
- __has_val_ = true;
}
- return *std::construct_at(std::addressof(__union_.__val_), std::forward<_Args>(__args)...);
+ std::construct_at(std::addressof(__union_.__val_), std::forward<_Args>(__args)...);
+ __has_val_ = true;
+ return *std::addressof(__union_.__val_);
}
template <class _Up, class... _Args>
@@ -452,9 +412,10 @@ class expected {
std::destroy_at(std::addressof(__union_.__val_));
} else {
std::destroy_at(std::addressof(__union_.__unex_));
- __has_val_ = true;
}
- return *std::construct_at(std::addressof(__union_.__val_), __il, std::forward<_Args>(__args)...);
+ std::construct_at(std::addressof(__union_.__val_), __il, std::forward<_Args>(__args)...);
+ __has_val_ = true;
+ return *std::addressof(__union_.__val_);
}
@@ -894,11 +855,21 @@ class expected {
private:
struct __empty_t {};
+ struct __construct_in_place_tag {};
+ struct __construct_unexpected_tag {};
template <class _ValueType, class _ErrorType>
union __union_t {
_LIBCPP_HIDE_FROM_ABI constexpr __union_t() {}
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_in_place_tag, _Args&&... __args)
+ : __val_(std::forward<_Args>(__args)...) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+ : __unex_(std::forward<_Args>(__args)...) {}
+
template <class _Func, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(
std::__expected_construct_in_place_from_invoke_tag, _Func&& __f, _Args&&... __args)
@@ -931,6 +902,14 @@ class expected {
_LIBCPP_HIDE_FROM_ABI constexpr __union_t(const __union_t&) = default;
_LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(const __union_t&) = default;
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_in_place_tag, _Args&&... __args)
+ : __val_(std::forward<_Args>(__args)...) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+ : __unex_(std::forward<_Args>(__args)...) {}
+
template <class _Func, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(
std::__expected_construct_in_place_from_invoke_tag, _Func&& __f, _Args&&... __args)
@@ -955,6 +934,18 @@ class expected {
_LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
};
+ template <class _Up, class _OtherErr>
+ _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
+ return __other.__has_val_ ? __union_t<_Tp, _Err>(__construct_in_place_tag{}, __other.__union_.__val_)
+ : __union_t<_Tp, _Err>(__construct_unexpected_tag{}, __other.__union_.__unex_);
+ }
+
+ template <class _Up, class _OtherErr>
+ _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
+ return __other.__has_val_ ? __union_t<_Tp, _Err>(__construct_in_place_tag{}, std::move(__other.__union_.__val_))
+ : __union_t<_Tp, _Err>(__construct_unexpected_tag{}, std::move(__other.__union_.__unex_));
+ }
+
_LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
bool __has_val_;
};
@@ -998,11 +989,7 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected& __rhs)
noexcept(is_nothrow_copy_constructible_v<_Err>) // strengthened
requires(is_copy_constructible_v<_Err> && !is_trivially_copy_constructible_v<_Err>)
- : __has_val_(__rhs.__has_val_) {
- if (!__rhs.__has_val_) {
- std::construct_at(std::addressof(__union_.__unex_), __rhs.__union_.__unex_);
- }
- }
+ : __union_(__union_from_expected(__rhs)), __has_val_(__rhs.__has_val_) {}
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
@@ -1011,51 +998,35 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&& __rhs)
noexcept(is_nothrow_move_constructible_v<_Err>)
requires(is_move_constructible_v<_Err> && !is_trivially_move_constructible_v<_Err>)
- : __has_val_(__rhs.__has_val_) {
- if (!__rhs.__has_val_) {
- std::construct_at(std::addressof(__union_.__unex_), std::move(__rhs.__union_.__unex_));
- }
- }
+ : __union_(__union_from_expected(std::move(__rhs))), __has_val_(__rhs.__has_val_) {}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, const _OtherErr&>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const expected<_Up, _OtherErr>& __rhs)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __has_val_(__rhs.__has_val_) {
- if (!__rhs.__has_val_) {
- std::construct_at(std::addressof(__union_.__unex_), __rhs.__union_.__unex_);
- }
- }
+ : __union_(__union_from_expected(__rhs)), __has_val_(__rhs.__has_val_) {}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, _OtherErr>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(expected<_Up, _OtherErr>&& __rhs)
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __has_val_(__rhs.__has_val_) {
- if (!__rhs.__has_val_) {
- std::construct_at(std::addressof(__union_.__unex_), std::move(__rhs.__union_.__unex_));
- }
- }
+ : __union_(__union_from_expected(std::move(__rhs))), __has_val_(__rhs.__has_val_) {}
template <class _OtherErr>
requires is_constructible_v<_Err, const _OtherErr&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const unexpected<_OtherErr>& __unex)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __has_val_(false) {
- std::construct_at(std::addressof(__union_.__unex_), __unex.error());
- }
+ : __union_(__construct_unexpected_tag{}, __unex.error()), __has_val_(false) {}
template <class _OtherErr>
requires is_constructible_v<_Err, _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(unexpected<_OtherErr>&& __unex)
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __has_val_(false) {
- std::construct_at(std::addressof(__union_.__unex_), std::move(__unex.error()));
- }
+ : __union_(__construct_unexpected_tag{}, std::move(__unex.error())), __has_val_(false) {}
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __has_val_(true) {}
@@ -1063,17 +1034,13 @@ class expected<_Tp, _Err> {
requires is_constructible_v<_Err, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
- : __has_val_(false) {
- std::construct_at(std::addressof(__union_.__unex_), std::forward<_Args>(__args)...);
- }
+ : __union_(__construct_unexpected_tag{}, std::forward<_Args>(__args)...), __has_val_(false) {}
template <class _Up, class... _Args>
requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
- : __has_val_(false) {
- std::construct_at(std::addressof(__union_.__unex_), __il, std::forward<_Args>(__args)...);
- }
+ : __union_(__construct_unexpected_tag{}, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
private:
template <class _Func>
@@ -1502,11 +1469,16 @@ class expected<_Tp, _Err> {
private:
struct __empty_t {};
+ struct __construct_unexpected_tag {};
template <class _ErrorType>
union __union_t {
_LIBCPP_HIDE_FROM_ABI constexpr __union_t() : __empty_() {}
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+ : __unex_(std::forward<_Args>(__args)...) {}
+
template <class _Func, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(
__expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args)
@@ -1534,6 +1506,10 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr __union_t(const __union_t&) = default;
_LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(const __union_t&) = default;
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+ : __unex_(std::forward<_Args>(__args)...) {}
+
template <class _Func, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(
__expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args)
@@ -1552,6 +1528,18 @@ class expected<_Tp, _Err> {
_LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
};
+ template <class _Up, class _OtherErr>
+ _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
+ return __other.__has_val_ ? __union_t<_Err>()
+ : __union_t<_Err>(__construct_unexpected_tag{}, __other.__union_.__unex_);
+ }
+
+ template <class _Up, class _OtherErr>
+ _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
+ return __other.__has_val_ ? __union_t<_Err>()
+ : __union_t<_Err>(__construct_unexpected_tag{}, std::move(__other.__union_.__unex_));
+ }
+
_LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
bool __has_val_;
};
diff --git a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
index 27d657a065699ea..8979e0f45d44f50 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
@@ -12,6 +12,7 @@
#include <cassert>
#include <concepts>
#include <expected>
+#include <optional>
#include <type_traits>
#include <utility>
@@ -30,6 +31,22 @@ static_assert(!HasValueNoexcept<Foo>);
static_assert(HasValueNoexcept<std::expected<int, int>>);
static_assert(HasValueNoexcept<const std::expected<int, int>>);
+// This type has one byte of tail padding where `std::expected` will put its
+// "has value" flag. The constructor will clobber all bytes including the
+// tail padding. With this type we can check that `std::expected` will set
+// its "has value" flag _after_ the value/error object is constructed.
+template <int c>
+struct tail_clobberer {
+ constexpr tail_clobberer() {
+ if (!std::is_constant_evaluated()) {
+ // This `memset` might actually be UB (?) but suffices to reproduce bugs
+ // related to the "has value" flag.
+ std::memset(this, c, sizeof(*this));
+ }
+ }
+ alignas(2) bool b;
+};
+
constexpr bool test() {
// has_value
{
@@ -43,6 +60,28 @@ constexpr bool test() {
assert(!e.has_value());
}
+ // See https://github.com/llvm/llvm-project/issues/68552
+ {
+ static constexpr auto f1 = [] -> std::expected<std::optional<int>, long> { return 0; };
+
+ static constexpr auto f2 = [] -> std::expected<std::optional<int>, int> {
+ return f1().transform_error([](auto) { return 0; });
+ };
+
+ auto e = f2();
+ assert(e.has_value());
+ }
+ {
+ const std::expected<tail_clobberer<0>, bool> e = {};
+ static_assert(sizeof(tail_clobberer<0>) == sizeof(e));
+ assert(e.has_value());
+ }
+ {
+ const std::expected<void, tail_clobberer<1>> e(std::unexpect);
+ static_assert(sizeof(tail_clobberer<1>) == sizeof(e));
+ assert(!e.has_value());
+ }
+
return true;
}
>From a606feef7d5ed267e995250b6c69fd85cfd1248b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sun, 15 Oct 2023 13:32:14 +0200
Subject: [PATCH 02/38] replace home-grown constructor tags with standard ones
---
libcxx/include/__expected/expected.h | 51 +++++++++++++---------------
1 file changed, 24 insertions(+), 27 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 08f35b1111f6bf9..6440b31e7183201 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -119,7 +119,7 @@ class expected {
_LIBCPP_HIDE_FROM_ABI constexpr expected()
noexcept(is_nothrow_default_constructible_v<_Tp>) // strengthened
requires is_default_constructible_v<_Tp>
- : __union_(__construct_in_place_tag{}), __has_val_(true) {}
+ : __union_(std::in_place), __has_val_(true) {}
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
@@ -200,47 +200,47 @@ class expected {
(!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_expected<remove_cvref_t<_Up>>::value))
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp>)
expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
- : __union_(__construct_in_place_tag{}, std::forward<_Up>(__u)), __has_val_(true) {}
+ : __union_(std::in_place, std::forward<_Up>(__u)), __has_val_(true) {}
template <class _OtherErr>
requires is_constructible_v<_Err, const _OtherErr&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const unexpected<_OtherErr>& __unex)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __union_(__construct_unexpected_tag{}, __unex.error()), __has_val_(false) {}
+ : __union_(std::unexpect, __unex.error()), __has_val_(false) {}
template <class _OtherErr>
requires is_constructible_v<_Err, _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(unexpected<_OtherErr>&& __unex)
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __union_(__construct_unexpected_tag{}, std::move(__unex.error())), __has_val_(false) {}
+ : __union_(std::unexpect, std::move(__unex.error())), __has_val_(false) {}
template <class... _Args>
requires is_constructible_v<_Tp, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Tp, _Args...>) // strengthened
- : __union_(__construct_in_place_tag{}, std::forward<_Args>(__args)...), __has_val_(true) {}
+ : __union_(std::in_place, std::forward<_Args>(__args)...), __has_val_(true) {}
template <class _Up, class... _Args>
requires is_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit
expected(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, _Args...>) // strengthened
- : __union_(__construct_in_place_tag{}, __il, std::forward<_Args>(__args)...), __has_val_(true) {}
+ : __union_(std::in_place, __il, std::forward<_Args>(__args)...), __has_val_(true) {}
template <class... _Args>
requires is_constructible_v<_Err, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
- : __union_(__construct_unexpected_tag{}, std::forward<_Args>(__args)...), __has_val_(false) {}
+ : __union_(std::unexpect, std::forward<_Args>(__args)...), __has_val_(false) {}
template <class _Up, class... _Args>
requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit
expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
- : __union_(__construct_unexpected_tag{}, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
+ : __union_(std::unexpect, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
// [expected.object.dtor], destructor
@@ -855,19 +855,17 @@ class expected {
private:
struct __empty_t {};
- struct __construct_in_place_tag {};
- struct __construct_unexpected_tag {};
template <class _ValueType, class _ErrorType>
union __union_t {
_LIBCPP_HIDE_FROM_ABI constexpr __union_t() {}
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_in_place_tag, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::in_place_t, _Args&&... __args)
: __val_(std::forward<_Args>(__args)...) {}
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::unexpect_t, _Args&&... __args)
: __unex_(std::forward<_Args>(__args)...) {}
template <class _Func, class... _Args>
@@ -903,11 +901,11 @@ class expected {
_LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(const __union_t&) = default;
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_in_place_tag, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::in_place_t, _Args&&... __args)
: __val_(std::forward<_Args>(__args)...) {}
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::unexpect_t, _Args&&... __args)
: __unex_(std::forward<_Args>(__args)...) {}
template <class _Func, class... _Args>
@@ -936,14 +934,14 @@ class expected {
template <class _Up, class _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
- return __other.__has_val_ ? __union_t<_Tp, _Err>(__construct_in_place_tag{}, __other.__union_.__val_)
- : __union_t<_Tp, _Err>(__construct_unexpected_tag{}, __other.__union_.__unex_);
+ return __other.__has_val_ ? __union_t<_Tp, _Err>(std::in_place, __other.__union_.__val_)
+ : __union_t<_Tp, _Err>(std::unexpect, __other.__union_.__unex_);
}
template <class _Up, class _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
- return __other.__has_val_ ? __union_t<_Tp, _Err>(__construct_in_place_tag{}, std::move(__other.__union_.__val_))
- : __union_t<_Tp, _Err>(__construct_unexpected_tag{}, std::move(__other.__union_.__unex_));
+ return __other.__has_val_ ? __union_t<_Tp, _Err>(std::in_place, std::move(__other.__union_.__val_))
+ : __union_t<_Tp, _Err>(std::unexpect, std::move(__other.__union_.__unex_));
}
_LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
@@ -1019,14 +1017,14 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const unexpected<_OtherErr>& __unex)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __union_(__construct_unexpected_tag{}, __unex.error()), __has_val_(false) {}
+ : __union_(std::unexpect, __unex.error()), __has_val_(false) {}
template <class _OtherErr>
requires is_constructible_v<_Err, _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(unexpected<_OtherErr>&& __unex)
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __union_(__construct_unexpected_tag{}, std::move(__unex.error())), __has_val_(false) {}
+ : __union_(std::unexpect, std::move(__unex.error())), __has_val_(false) {}
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __has_val_(true) {}
@@ -1034,13 +1032,13 @@ class expected<_Tp, _Err> {
requires is_constructible_v<_Err, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
- : __union_(__construct_unexpected_tag{}, std::forward<_Args>(__args)...), __has_val_(false) {}
+ : __union_(std::unexpect, std::forward<_Args>(__args)...), __has_val_(false) {}
template <class _Up, class... _Args>
requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
- : __union_(__construct_unexpected_tag{}, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
+ : __union_(std::unexpect, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
private:
template <class _Func>
@@ -1469,14 +1467,13 @@ class expected<_Tp, _Err> {
private:
struct __empty_t {};
- struct __construct_unexpected_tag {};
template <class _ErrorType>
union __union_t {
_LIBCPP_HIDE_FROM_ABI constexpr __union_t() : __empty_() {}
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::unexpect_t, _Args&&... __args)
: __unex_(std::forward<_Args>(__args)...) {}
template <class _Func, class... _Args>
@@ -1507,7 +1504,7 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(const __union_t&) = default;
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(__construct_unexpected_tag, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::unexpect_t, _Args&&... __args)
: __unex_(std::forward<_Args>(__args)...) {}
template <class _Func, class... _Args>
@@ -1531,13 +1528,13 @@ class expected<_Tp, _Err> {
template <class _Up, class _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
return __other.__has_val_ ? __union_t<_Err>()
- : __union_t<_Err>(__construct_unexpected_tag{}, __other.__union_.__unex_);
+ : __union_t<_Err>(std::unexpect, __other.__union_.__unex_);
}
template <class _Up, class _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
return __other.__has_val_ ? __union_t<_Err>()
- : __union_t<_Err>(__construct_unexpected_tag{}, std::move(__other.__union_.__unex_));
+ : __union_t<_Err>(std::unexpect, std::move(__other.__union_.__unex_));
}
_LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
>From 9fda352b29441cf189d331ae11bea9773702d77c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sun, 15 Oct 2023 13:37:48 +0200
Subject: [PATCH 03/38] replace use of ternary operator by explicit if/else
---
libcxx/include/__expected/expected.h | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 6440b31e7183201..55036d3f55c2432 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -934,14 +934,18 @@ class expected {
template <class _Up, class _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
- return __other.__has_val_ ? __union_t<_Tp, _Err>(std::in_place, __other.__union_.__val_)
- : __union_t<_Tp, _Err>(std::unexpect, __other.__union_.__unex_);
+ if (__other.__has_val_)
+ return __union_t<_Tp, _Err>(std::in_place, __other.__union_.__val_);
+ else
+ return __union_t<_Tp, _Err>(std::unexpect, __other.__union_.__unex_);
}
template <class _Up, class _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
- return __other.__has_val_ ? __union_t<_Tp, _Err>(std::in_place, std::move(__other.__union_.__val_))
- : __union_t<_Tp, _Err>(std::unexpect, std::move(__other.__union_.__unex_));
+ if (__other.__has_val_)
+ return __union_t<_Tp, _Err>(std::in_place, std::move(__other.__union_.__val_));
+ else
+ return __union_t<_Tp, _Err>(std::unexpect, std::move(__other.__union_.__unex_));
}
_LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
@@ -1527,14 +1531,18 @@ class expected<_Tp, _Err> {
template <class _Up, class _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
- return __other.__has_val_ ? __union_t<_Err>()
- : __union_t<_Err>(std::unexpect, __other.__union_.__unex_);
+ if (__other.__has_val_)
+ return __union_t<_Err>();
+ else
+ return __union_t<_Err>(std::unexpect, __other.__union_.__unex_);
}
template <class _Up, class _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
- return __other.__has_val_ ? __union_t<_Err>()
- : __union_t<_Err>(std::unexpect, std::move(__other.__union_.__unex_));
+ if (__other.__has_val_)
+ return __union_t<_Err>();
+ else
+ return __union_t<_Err>(std::unexpect, std::move(__other.__union_.__unex_));
}
_LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
>From d6aa05e8483eccb39799077bcc75f9ea107e02d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sun, 15 Oct 2023 13:47:06 +0200
Subject: [PATCH 04/38] add a short description to the tests
---
.../expected.expected/observers/has_value.pass.cpp | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
index 8979e0f45d44f50..b203253e897795e 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
@@ -60,7 +60,17 @@ constexpr bool test() {
assert(!e.has_value());
}
- // See https://github.com/llvm/llvm-project/issues/68552
+ // The following tests check that the "has_value" flag is not overwritten
+ // by the constructor of the value. This could happen because the flag is
+ // stored in the tail padding of the value.
+ //
+ // The first test is a simplified version of the real code where this was
+ // first observed.
+ //
+ // The other tests use a synthetic struct that clobbers its tail padding
+ // on construction, making the issue easier to reproduce.
+ //
+ // See https://github.com/llvm/llvm-project/issues/68552 and the linked PR.
{
static constexpr auto f1 = [] -> std::expected<std::optional<int>, long> { return 0; };
>From 7b470130e3a8b2310fe5052127006a98766df4fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sun, 15 Oct 2023 13:50:57 +0200
Subject: [PATCH 05/38] remove comment about memset being UB here
---
.../expected/expected.expected/observers/has_value.pass.cpp | 2 --
1 file changed, 2 deletions(-)
diff --git a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
index b203253e897795e..d7591718ae784b6 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
@@ -39,8 +39,6 @@ template <int c>
struct tail_clobberer {
constexpr tail_clobberer() {
if (!std::is_constant_evaluated()) {
- // This `memset` might actually be UB (?) but suffices to reproduce bugs
- // related to the "has value" flag.
std::memset(this, c, sizeof(*this));
}
}
>From 67a2a38d229ec18e195b402618e9c6960a028538 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 17 Oct 2023 15:59:37 +0200
Subject: [PATCH 06/38] add constructors to __union_t from expected parts and
refactor
GCC 13 didn't like the previous approach.
---
libcxx/include/__expected/expected.h | 80 ++++++++++++++--------------
1 file changed, 40 insertions(+), 40 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 55036d3f55c2432..06c9a26c8e3a88f 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -134,7 +134,7 @@ class expected {
noexcept(is_nothrow_copy_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Err>) // strengthened
requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err> &&
!(is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Err>))
- : __union_(__union_from_expected(__other)), __has_val_(__other.__has_val_) { }
+ : __union_(__other.__has_val_, __other.__union_), __has_val_(__other.__has_val_) { }
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err>
@@ -145,7 +145,7 @@ class expected {
noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_move_constructible_v<_Err>)
requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err> &&
!(is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>))
- : __union_(__union_from_expected(std::move(__other))), __has_val_(__other.__has_val_) { }
+ : __union_(__other.__has_val_, std::move(__other.__union_)), __has_val_(__other.__has_val_) { }
private:
template <class _Up, class _OtherErr, class _UfQual, class _OtherErrQual>
@@ -185,14 +185,14 @@ class expected {
expected(const expected<_Up, _OtherErr>& __other)
noexcept(is_nothrow_constructible_v<_Tp, const _Up&> &&
is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __union_(__union_from_expected(__other)), __has_val_(__other.__has_val_) {}
+ : __union_(__other.__has_val_, __other.__union_), __has_val_(__other.__has_val_) {}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, _Up, _OtherErr>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp> || !is_convertible_v<_OtherErr, _Err>)
expected(expected<_Up, _OtherErr>&& __other)
noexcept(is_nothrow_constructible_v<_Tp, _Up> && is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __union_(__union_from_expected(std::move(__other))), __has_val_(__other.__has_val_) {}
+ : __union_(__other.__has_val_, std::move(__other.__union_)), __has_val_(__other.__has_val_) {}
template <class _Up = _Tp>
requires(!is_same_v<remove_cvref_t<_Up>, in_place_t> && !is_same_v<expected, remove_cvref_t<_Up>> &&
@@ -878,6 +878,14 @@ class expected {
std::__expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args)
: __unex_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {}
+ template <class _Union>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
+ if (__has_val)
+ std::construct_at(this, std::in_place, std::forward<_Union>(__other).__val_);
+ else
+ std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+ }
+
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>)
= default;
@@ -918,6 +926,14 @@ class expected {
std::__expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args)
: __unex_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {}
+ template <class _Union>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
+ if (__has_val)
+ std::construct_at(this, std::in_place, std::forward<_Union>(__other).__val_);
+ else
+ std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+ }
+
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>)
= default;
@@ -932,22 +948,6 @@ class expected {
_LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
};
- template <class _Up, class _OtherErr>
- _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
- if (__other.__has_val_)
- return __union_t<_Tp, _Err>(std::in_place, __other.__union_.__val_);
- else
- return __union_t<_Tp, _Err>(std::unexpect, __other.__union_.__unex_);
- }
-
- template <class _Up, class _OtherErr>
- _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Tp, _Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
- if (__other.__has_val_)
- return __union_t<_Tp, _Err>(std::in_place, std::move(__other.__union_.__val_));
- else
- return __union_t<_Tp, _Err>(std::unexpect, std::move(__other.__union_.__unex_));
- }
-
_LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
bool __has_val_;
};
@@ -991,7 +991,7 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected& __rhs)
noexcept(is_nothrow_copy_constructible_v<_Err>) // strengthened
requires(is_copy_constructible_v<_Err> && !is_trivially_copy_constructible_v<_Err>)
- : __union_(__union_from_expected(__rhs)), __has_val_(__rhs.__has_val_) {}
+ : __union_(__rhs.__has_val_, __rhs.__union_), __has_val_(__rhs.__has_val_) {}
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
@@ -1000,21 +1000,21 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&& __rhs)
noexcept(is_nothrow_move_constructible_v<_Err>)
requires(is_move_constructible_v<_Err> && !is_trivially_move_constructible_v<_Err>)
- : __union_(__union_from_expected(std::move(__rhs))), __has_val_(__rhs.__has_val_) {}
+ : __union_(__rhs.__has_val_, std::move(__rhs.__union_)), __has_val_(__rhs.__has_val_) {}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, const _OtherErr&>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const expected<_Up, _OtherErr>& __rhs)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __union_(__union_from_expected(__rhs)), __has_val_(__rhs.__has_val_) {}
+ : __union_(__rhs.__has_val_, __rhs.__union_), __has_val_(__rhs.__has_val_) {}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, _OtherErr>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(expected<_Up, _OtherErr>&& __rhs)
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __union_(__union_from_expected(std::move(__rhs))), __has_val_(__rhs.__has_val_) {}
+ : __union_(__rhs.__has_val_, std::move(__rhs.__union_)), __has_val_(__rhs.__has_val_) {}
template <class _OtherErr>
requires is_constructible_v<_Err, const _OtherErr&>
@@ -1485,6 +1485,14 @@ class expected<_Tp, _Err> {
__expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args)
: __unex_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {}
+ template <class _Union>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
+ if (__has_val)
+ std::construct_at(this);
+ else
+ std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+ }
+
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
requires(is_trivially_destructible_v<_ErrorType>)
= default;
@@ -1516,6 +1524,14 @@ class expected<_Tp, _Err> {
__expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args)
: __unex_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {}
+ template <class _Union>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
+ if (__has_val)
+ std::construct_at(this);
+ else
+ std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+ }
+
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
requires(is_trivially_destructible_v<_ErrorType>)
= default;
@@ -1529,22 +1545,6 @@ class expected<_Tp, _Err> {
_LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
};
- template <class _Up, class _OtherErr>
- _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(const expected<_Up, _OtherErr>& __other) {
- if (__other.__has_val_)
- return __union_t<_Err>();
- else
- return __union_t<_Err>(std::unexpect, __other.__union_.__unex_);
- }
-
- template <class _Up, class _OtherErr>
- _LIBCPP_HIDE_FROM_ABI constexpr __union_t<_Err> __union_from_expected(expected<_Up, _OtherErr>&& __other) {
- if (__other.__has_val_)
- return __union_t<_Err>();
- else
- return __union_t<_Err>(std::unexpect, std::move(__other.__union_.__unex_));
- }
-
_LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
bool __has_val_;
};
>From a6763038108f50b203d6cdf2b5bfd8ef310b5bea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 17 Oct 2023 16:06:19 +0200
Subject: [PATCH 07/38] put new expected<void> test into appropriate folder
---
.../observers/has_value.pass.cpp | 24 +++----------------
.../observers/has_value.pass.cpp | 8 +++++++
libcxx/test/std/utilities/expected/types.h | 16 +++++++++++++
3 files changed, 27 insertions(+), 21 deletions(-)
diff --git a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
index d7591718ae784b6..74569cfa2083e92 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
@@ -17,6 +17,7 @@
#include <utility>
#include "test_macros.h"
+#include "../../types.h"
// Test noexcept
template <class T>
@@ -31,20 +32,6 @@ static_assert(!HasValueNoexcept<Foo>);
static_assert(HasValueNoexcept<std::expected<int, int>>);
static_assert(HasValueNoexcept<const std::expected<int, int>>);
-// This type has one byte of tail padding where `std::expected` will put its
-// "has value" flag. The constructor will clobber all bytes including the
-// tail padding. With this type we can check that `std::expected` will set
-// its "has value" flag _after_ the value/error object is constructed.
-template <int c>
-struct tail_clobberer {
- constexpr tail_clobberer() {
- if (!std::is_constant_evaluated()) {
- std::memset(this, c, sizeof(*this));
- }
- }
- alignas(2) bool b;
-};
-
constexpr bool test() {
// has_value
{
@@ -80,15 +67,10 @@ constexpr bool test() {
assert(e.has_value());
}
{
- const std::expected<tail_clobberer<0>, bool> e = {};
- static_assert(sizeof(tail_clobberer<0>) == sizeof(e));
+ const std::expected<TailClobberer<0>, bool> e = {};
+ static_assert(sizeof(TailClobberer<0>) == sizeof(e));
assert(e.has_value());
}
- {
- const std::expected<void, tail_clobberer<1>> e(std::unexpect);
- static_assert(sizeof(tail_clobberer<1>) == sizeof(e));
- assert(!e.has_value());
- }
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
index 42a173d60c898c6..89c79541f995526 100644
--- a/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
@@ -16,6 +16,7 @@
#include <utility>
#include "test_macros.h"
+#include "../../types.h"
// Test noexcept
template <class T>
@@ -43,6 +44,13 @@ constexpr bool test() {
assert(!e.has_value());
}
+ // See comments of the corresponding test in "expected.expected".
+ {
+ const std::expected<void, TailClobberer<1>> e(std::unexpect);
+ static_assert(sizeof(TailClobberer<1>) == sizeof(e));
+ assert(!e.has_value());
+ }
+
return true;
}
diff --git a/libcxx/test/std/utilities/expected/types.h b/libcxx/test/std/utilities/expected/types.h
index 7c7e517785b4f78..874b72b64e8ae11 100644
--- a/libcxx/test/std/utilities/expected/types.h
+++ b/libcxx/test/std/utilities/expected/types.h
@@ -9,7 +9,9 @@
#ifndef TEST_STD_UTILITIES_EXPECTED_TYPES_H
#define TEST_STD_UTILITIES_EXPECTED_TYPES_H
+#include <cstring>
#include <utility>
+#include <type_traits>
#include "test_macros.h"
template <bool copyMoveNoexcept, bool convertNoexcept = true>
@@ -102,6 +104,20 @@ struct TrackedMove {
}
};
+// This type has one byte of tail padding where `std::expected` will put its
+// "has value" flag. The constructor will clobber all bytes including the
+// tail padding. With this type we can check that `std::expected` will set
+// its "has value" flag _after_ the value/error object is constructed.
+template <int c>
+struct TailClobberer {
+ constexpr TailClobberer() {
+ if (!std::is_constant_evaluated()) {
+ std::memset(this, c, sizeof(*this));
+ }
+ }
+ alignas(2) bool b;
+};
+
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Except {};
>From c31e2e6ec81c81ea22c959a07258cd4b69e82919 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 17 Oct 2023 18:21:47 +0200
Subject: [PATCH 08/38] disable new tests on compilers that cannot build them
---
.../expected/expected.expected/observers/has_value.pass.cpp | 5 +++++
.../expected/expected.void/observers/has_value.pass.cpp | 3 +++
2 files changed, 8 insertions(+)
diff --git a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
index 74569cfa2083e92..3cb46bcf79abee3 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
@@ -57,6 +57,7 @@ constexpr bool test() {
//
// See https://github.com/llvm/llvm-project/issues/68552 and the linked PR.
{
+#if !defined(TEST_COMPILER_CLANG) || TEST_CLANG_VER >= 1600
static constexpr auto f1 = [] -> std::expected<std::optional<int>, long> { return 0; };
static constexpr auto f2 = [] -> std::expected<std::optional<int>, int> {
@@ -65,10 +66,14 @@ constexpr bool test() {
auto e = f2();
assert(e.has_value());
+#endif
}
{
const std::expected<TailClobberer<0>, bool> e = {};
+ // clang-cl does not support [[no_unique_address]] yet.
+#if !(defined(TEST_COMPILER_CLANG) && defined(_MSC_VER))
static_assert(sizeof(TailClobberer<0>) == sizeof(e));
+#endif
assert(e.has_value());
}
diff --git a/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
index 89c79541f995526..50098d10977b702 100644
--- a/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
@@ -47,7 +47,10 @@ constexpr bool test() {
// See comments of the corresponding test in "expected.expected".
{
const std::expected<void, TailClobberer<1>> e(std::unexpect);
+ // clang-cl does not support [[no_unique_address]] yet.
+#if !(defined(TEST_COMPILER_CLANG) && defined(_MSC_VER))
static_assert(sizeof(TailClobberer<1>) == sizeof(e));
+#endif
assert(!e.has_value());
}
>From 32327460d4fc7a802de110a966d315866d8b9c91 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 17 Oct 2023 19:57:07 +0200
Subject: [PATCH 09/38] make comment in
libcxx/test/std/utilities/expected/types.h more general
Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
libcxx/test/std/utilities/expected/types.h | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/libcxx/test/std/utilities/expected/types.h b/libcxx/test/std/utilities/expected/types.h
index 874b72b64e8ae11..e6fd5ac479266ad 100644
--- a/libcxx/test/std/utilities/expected/types.h
+++ b/libcxx/test/std/utilities/expected/types.h
@@ -104,10 +104,12 @@ struct TrackedMove {
}
};
-// This type has one byte of tail padding where `std::expected` will put its
+// This type has one byte of tail padding where `std::expected` may put its
// "has value" flag. The constructor will clobber all bytes including the
-// tail padding. With this type we can check that `std::expected` will set
-// its "has value" flag _after_ the value/error object is constructed.
+// tail padding. With this type we can check that `std::expected` handles
+// the case where the "has value" flag is an overlapping subobject correctly.
+//
+// See https://github.com/llvm/llvm-project/issues/68552 for details.
template <int c>
struct TailClobberer {
constexpr TailClobberer() {
>From f7d46a205645691981a5a8af18c592b2fcf2d6e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 17 Oct 2023 20:12:00 +0200
Subject: [PATCH 10/38] spell out full path to corresponding test file
---
.../expected/expected.void/observers/has_value.pass.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
index 50098d10977b702..06a45ed43018041 100644
--- a/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/observers/has_value.pass.cpp
@@ -44,7 +44,8 @@ constexpr bool test() {
assert(!e.has_value());
}
- // See comments of the corresponding test in "expected.expected".
+ // See comments of the corresponding test in
+ // "expected.expected/observers/has_value.pass.cpp".
{
const std::expected<void, TailClobberer<1>> e(std::unexpect);
// clang-cl does not support [[no_unique_address]] yet.
>From 7a95df883869ba6d8b3cc04a3cf1fff797e6c86d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 17 Oct 2023 20:14:28 +0200
Subject: [PATCH 11/38] directly construct into union member instead of trying
to delegate to another constructor
---
libcxx/include/__expected/expected.h | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 06c9a26c8e3a88f..9d09485d22ae4de 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -881,9 +881,9 @@ class expected {
template <class _Union>
_LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
if (__has_val)
- std::construct_at(this, std::in_place, std::forward<_Union>(__other).__val_);
+ std::construct_at(std::addressof(__val_), std::forward<_Union>(__other).__val_);
else
- std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+ std::construct_at(std::addressof(__unex_), std::forward<_Union>(__other).__unex_);
}
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
@@ -929,9 +929,9 @@ class expected {
template <class _Union>
_LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
if (__has_val)
- std::construct_at(this, std::in_place, std::forward<_Union>(__other).__val_);
+ std::construct_at(std::addressof(__val_), std::forward<_Union>(__other).__val_);
else
- std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+ std::construct_at(std::addressof(__unex_), std::forward<_Union>(__other).__unex_);
}
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
@@ -1488,9 +1488,9 @@ class expected<_Tp, _Err> {
template <class _Union>
_LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
if (__has_val)
- std::construct_at(this);
+ std::construct_at(std::addressof(__empty_));
else
- std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+ std::construct_at(std::addressof(__unex_), std::forward<_Union>(__other).__unex_);
}
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
@@ -1527,9 +1527,9 @@ class expected<_Tp, _Err> {
template <class _Union>
_LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(bool __has_val, _Union&& __other) {
if (__has_val)
- std::construct_at(this);
+ std::construct_at(std::addressof(__empty_));
else
- std::construct_at(this, std::unexpect, std::forward<_Union>(__other).__unex_);
+ std::construct_at(std::addressof(__unex_), std::forward<_Union>(__other).__unex_);
}
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
>From 5af066aa260e3a2a18ab09e1584da6392486c050 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 17 Oct 2023 20:24:03 +0200
Subject: [PATCH 12/38] remove unneeded '__empty_t' in union of non-void
expected
---
libcxx/include/__expected/expected.h | 6 ------
1 file changed, 6 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 9d09485d22ae4de..f874a3a126a58f3 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -854,12 +854,8 @@ class expected {
}
private:
- struct __empty_t {};
-
template <class _ValueType, class _ErrorType>
union __union_t {
- _LIBCPP_HIDE_FROM_ABI constexpr __union_t() {}
-
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::in_place_t, _Args&&... __args)
: __val_(std::forward<_Args>(__args)...) {}
@@ -904,7 +900,6 @@ class expected {
template <class _ValueType, class _ErrorType>
requires(is_trivially_move_constructible_v<_ValueType> && is_trivially_move_constructible_v<_ErrorType>)
union __union_t<_ValueType, _ErrorType> {
- _LIBCPP_HIDE_FROM_ABI constexpr __union_t() : __empty_() {}
_LIBCPP_HIDE_FROM_ABI constexpr __union_t(const __union_t&) = default;
_LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(const __union_t&) = default;
@@ -943,7 +938,6 @@ class expected {
requires(!is_trivially_destructible_v<_ValueType> || !is_trivially_destructible_v<_ErrorType>)
{}
- _LIBCPP_NO_UNIQUE_ADDRESS __empty_t __empty_;
_LIBCPP_NO_UNIQUE_ADDRESS _ValueType __val_;
_LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
};
>From 71312c52c3f4b7e8d0b77d2836c5eac8fc812820 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Wed, 18 Oct 2023 03:22:39 +0200
Subject: [PATCH 13/38] make lambdas automatic variables
---
.../expected/expected.expected/observers/has_value.pass.cpp | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
index 3cb46bcf79abee3..0e19da065594644 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/observers/has_value.pass.cpp
@@ -57,16 +57,14 @@ constexpr bool test() {
//
// See https://github.com/llvm/llvm-project/issues/68552 and the linked PR.
{
-#if !defined(TEST_COMPILER_CLANG) || TEST_CLANG_VER >= 1600
- static constexpr auto f1 = [] -> std::expected<std::optional<int>, long> { return 0; };
+ auto f1 = [] -> std::expected<std::optional<int>, long> { return 0; };
- static constexpr auto f2 = [] -> std::expected<std::optional<int>, int> {
+ auto f2 = [&f1] -> std::expected<std::optional<int>, int> {
return f1().transform_error([](auto) { return 0; });
};
auto e = f2();
assert(e.has_value());
-#endif
}
{
const std::expected<TailClobberer<0>, bool> e = {};
>From 519bd84d16be1be5dd58305f5a82b0c1f47731f7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Fri, 20 Oct 2023 18:28:34 +0200
Subject: [PATCH 14/38] rename template parameter from "c" to "constant"
Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
libcxx/test/std/utilities/expected/types.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/test/std/utilities/expected/types.h b/libcxx/test/std/utilities/expected/types.h
index e6fd5ac479266ad..697a410cea5fe48 100644
--- a/libcxx/test/std/utilities/expected/types.h
+++ b/libcxx/test/std/utilities/expected/types.h
@@ -110,7 +110,7 @@ struct TrackedMove {
// the case where the "has value" flag is an overlapping subobject correctly.
//
// See https://github.com/llvm/llvm-project/issues/68552 for details.
-template <int c>
+template <int constant>
struct TailClobberer {
constexpr TailClobberer() {
if (!std::is_constant_evaluated()) {
>From e2085e1127fecda28d367eedc0af8a4eba42e3b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Fri, 20 Oct 2023 20:55:20 +0200
Subject: [PATCH 15/38] fix build
---
libcxx/test/std/utilities/expected/types.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/test/std/utilities/expected/types.h b/libcxx/test/std/utilities/expected/types.h
index 697a410cea5fe48..6a7990383ada8b5 100644
--- a/libcxx/test/std/utilities/expected/types.h
+++ b/libcxx/test/std/utilities/expected/types.h
@@ -114,7 +114,7 @@ template <int constant>
struct TailClobberer {
constexpr TailClobberer() {
if (!std::is_constant_evaluated()) {
- std::memset(this, c, sizeof(*this));
+ std::memset(this, constant, sizeof(*this));
}
}
alignas(2) bool b;
>From 0716154195876130ecf06069a103bf418187d4b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 18:23:27 +0200
Subject: [PATCH 16/38] add TailClobberer tests for expected.expected
---
.../assign/emplace.intializer_list.pass.cpp | 8 +++++++
.../expected.expected/assign/emplace.pass.cpp | 7 ++++++
.../ctor/ctor.convert.copy.pass.cpp | 9 +++++++
.../ctor/ctor.convert.move.pass.cpp | 9 +++++++
.../expected.expected/ctor/ctor.copy.pass.cpp | 16 +++++++++++++
.../ctor/ctor.default.pass.cpp | 3 +++
.../ctor/ctor.inplace.pass.cpp | 16 ++++++++-----
.../ctor/ctor.inplace_init_list.pass.cpp | 7 ++++++
.../expected.expected/ctor/ctor.move.pass.cpp | 16 +++++++++++++
.../expected.expected/ctor/ctor.u.pass.cpp | 16 ++++++++-----
.../ctor/ctor.unexpect.pass.cpp | 16 ++++++++-----
.../ctor/ctor.unexpect_init_list.pass.cpp | 7 ++++++
.../ctor/ctor.unexpected.copy.pass.cpp | 6 +++--
.../ctor/ctor.unexpected.move.pass.cpp | 6 +++--
libcxx/test/std/utilities/expected/types.h | 24 ++++++++++++++++++-
15 files changed, 143 insertions(+), 23 deletions(-)
diff --git a/libcxx/test/std/utilities/expected/expected.expected/assign/emplace.intializer_list.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/assign/emplace.intializer_list.pass.cpp
index 3cdfcde3f4d6949..922200a8c0263d5 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/assign/emplace.intializer_list.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/assign/emplace.intializer_list.pass.cpp
@@ -81,6 +81,14 @@ constexpr bool test() {
assert(e.value().i == 10);
}
+ // TailClobberer
+ {
+ std::expected<TailClobberer<0>, bool> e(std::unexpect);
+ auto list = {4, 5, 6};
+ e.emplace(list);
+ assert(e.has_value());
+ }
+
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 c62e6289350201a..491de2dff03312d 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/assign/emplace.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/assign/emplace.pass.cpp
@@ -73,6 +73,13 @@ constexpr bool test() {
assert(e.value() == 10);
}
+ // TailClobberer
+ {
+ std::expected<TailClobberer<0>, bool> e(std::unexpect);
+ e.emplace();
+ assert(e.has_value());
+ }
+
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.copy.pass.cpp
index 9274b9a2c030e5d..2cc952358e821ce 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.copy.pass.cpp
@@ -45,6 +45,7 @@
#include <utility>
#include "test_macros.h"
+#include "../../types.h"
// Test Constraints:
template <class T1, class Err1, class T2, class Err2>
@@ -161,6 +162,14 @@ constexpr bool test() {
assert(e1.error() == 5);
}
+ // convert TailClobberer
+ {
+ const std::expected<TailClobbererNonTrivialMove<0>, char> e1;
+ std::expected<TailClobberer<0>, char> e2 = e1;
+ assert(e2.has_value());
+ assert(e1.has_value());
+ }
+
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.move.pass.cpp
index 71979311bfd1030..31819cd33ef6c33 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.move.pass.cpp
@@ -46,6 +46,7 @@
#include "MoveOnly.h"
#include "test_macros.h"
+#include "../../types.h"
// Test Constraints:
template <class T1, class Err1, class T2, class Err2>
@@ -160,6 +161,14 @@ constexpr bool test() {
assert(e1.error().get() == 0);
}
+ // convert TailClobberer
+ {
+ std::expected<TailClobbererNonTrivialMove<0>, char> e1;
+ std::expected<TailClobberer<0>, char> e2 = std::move(e1);
+ assert(e2.has_value());
+ assert(e1.has_value());
+ }
+
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.copy.pass.cpp
index 77d73485025ab64..1b1c4a59606920a 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.copy.pass.cpp
@@ -30,6 +30,7 @@
#include <utility>
#include "test_macros.h"
+#include "../../types.h"
struct NonCopyable {
NonCopyable(const NonCopyable&) = delete;
@@ -93,6 +94,21 @@ constexpr bool test() {
assert(!e2.has_value());
assert(e2.error() == 5);
}
+
+ // copy TailClobberer as value
+ {
+ const std::expected<TailClobberer<0>, bool> e1;
+ auto e2 = e1;
+ assert(e2.has_value());
+ }
+
+ // copy TailClobberer as error
+ {
+ const std::expected<bool, TailClobberer<1>> e1(std::unexpect);
+ auto e2 = e1;
+ assert(!e2.has_value());
+ }
+
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.default.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.default.pass.cpp
index 431e604e8b692f7..ea23d8aa735c84c 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.default.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.default.pass.cpp
@@ -22,6 +22,7 @@
#include <type_traits>
#include "test_macros.h"
+#include "../../types.h"
struct NoDedefaultCtor {
NoDedefaultCtor() = delete;
@@ -45,6 +46,7 @@ constexpr void testDefaultCtor() {
template <class T>
constexpr void testTypes() {
+ testDefaultCtor<T, bool>();
testDefaultCtor<T, int>();
testDefaultCtor<T, NoDedefaultCtor>();
}
@@ -52,6 +54,7 @@ constexpr void testTypes() {
constexpr bool test() {
testTypes<int>();
testTypes<MyInt>();
+ testTypes<TailClobberer<0>>();
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace.pass.cpp
index 92952551711e043..0b41eb44883e43c 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace.pass.cpp
@@ -26,6 +26,7 @@
#include "MoveOnly.h"
#include "test_macros.h"
+#include "../../types.h"
// Test Constraints:
static_assert(std::is_constructible_v<std::expected<int, int>, std::in_place_t>);
@@ -54,24 +55,24 @@ struct CopyOnly {
friend constexpr bool operator==(const CopyOnly& mi, int ii) { return mi.i == ii; }
};
-template <class T>
+template <class T, class E = int>
constexpr void testInt() {
- std::expected<T, int> e(std::in_place, 5);
+ std::expected<T, E> e(std::in_place, 5);
assert(e.has_value());
assert(e.value() == 5);
}
-template <class T>
+template <class T, class E = int>
constexpr void testLValue() {
T t(5);
- std::expected<T, int> e(std::in_place, t);
+ std::expected<T, E> e(std::in_place, t);
assert(e.has_value());
assert(e.value() == 5);
}
-template <class T>
+template <class T, class E = int>
constexpr void testRValue() {
- std::expected<T, int> e(std::in_place, T(5));
+ std::expected<T, E> e(std::in_place, T(5));
assert(e.has_value());
assert(e.value() == 5);
}
@@ -80,10 +81,13 @@ constexpr bool test() {
testInt<int>();
testInt<CopyOnly>();
testInt<MoveOnly>();
+ testInt<TailClobberer<0>, bool>();
testLValue<int>();
testLValue<CopyOnly>();
+ testLValue<TailClobberer<0>, bool>();
testRValue<int>();
testRValue<MoveOnly>();
+ testRValue<TailClobberer<0>, bool>();
// no arg
{
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace_init_list.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace_init_list.pass.cpp
index b4cad54b860e920..143f9d7dab11398 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace_init_list.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace_init_list.pass.cpp
@@ -28,6 +28,7 @@
#include "MoveOnly.h"
#include "test_macros.h"
+#include "../../types.h"
// Test Constraints:
static_assert(
@@ -90,6 +91,12 @@ constexpr bool test() {
assert(m.get() == 0);
}
+ // TailClobberer
+ {
+ std::expected<TailClobberer<0>, bool> e(std::in_place, {1, 2, 3});
+ assert(e.has_value());
+ }
+
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.move.pass.cpp
index 5e6749e50c16c97..f542778b3d5ff90 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.move.pass.cpp
@@ -32,6 +32,7 @@
#include <utility>
#include "test_macros.h"
+#include "../../types.h"
struct NonMovable {
NonMovable(NonMovable&&) = delete;
@@ -112,6 +113,21 @@ constexpr bool test() {
assert(e2.error() == 5);
assert(!e1.has_value());
}
+
+ // move TailClobbererNonTrivialMove as value
+ {
+ std::expected<TailClobbererNonTrivialMove<0>, bool> e1;
+ auto e2 = std::move(e1);
+ assert(e2.has_value());
+ }
+
+ // move TailClobbererNonTrivialMove as error
+ {
+ std::expected<bool, TailClobbererNonTrivialMove<1>> e1(std::unexpect);
+ auto e2 = std::move(e1);
+ assert(!e2.has_value());
+ }
+
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp
index 9e82943f9f314d8..16dd531a410c2a8 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp
@@ -29,6 +29,7 @@
#include "MoveOnly.h"
#include "test_macros.h"
+#include "../../types.h"
// Test Constraints:
static_assert(std::is_constructible_v<std::expected<int, int>, int>);
@@ -70,24 +71,24 @@ struct CopyOnly {
struct BaseError {};
struct DerivedError : BaseError {};
-template <class T>
+template <class T, class E = int>
constexpr void testInt() {
- std::expected<T, int> e(5);
+ std::expected<T, E> e(5);
assert(e.has_value());
assert(e.value() == 5);
}
-template <class T>
+template <class T, class E = int>
constexpr void testLValue() {
T t(5);
- std::expected<T, int> e(t);
+ std::expected<T, E> e(t);
assert(e.has_value());
assert(e.value() == 5);
}
-template <class T>
+template <class T, class E = int>
constexpr void testRValue() {
- std::expected<T, int> e(T(5));
+ std::expected<T, E> e(T(5));
assert(e.has_value());
assert(e.value() == 5);
}
@@ -96,10 +97,13 @@ constexpr bool test() {
testInt<int>();
testInt<CopyOnly>();
testInt<MoveOnly>();
+ testInt<TailClobberer<0>, bool>();
testLValue<int>();
testLValue<CopyOnly>();
+ testLValue<TailClobberer<0>, bool>();
testRValue<int>();
testRValue<MoveOnly>();
+ testRValue<TailClobberer<0>, bool>();
// Test default template argument.
// Without it, the template parameter cannot be deduced from an initializer list
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect.pass.cpp
index 5a78e41dfcae259..a0b334a25b0cb81 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect.pass.cpp
@@ -26,6 +26,7 @@
#include "MoveOnly.h"
#include "test_macros.h"
+#include "../../types.h"
// Test Constraints:
static_assert(std::is_constructible_v<std::expected<int, int>, std::unexpect_t>);
@@ -54,24 +55,24 @@ struct CopyOnly {
friend constexpr bool operator==(const CopyOnly& mi, int ii) { return mi.i == ii; }
};
-template <class T>
+template <class T, class V = int>
constexpr void testInt() {
- std::expected<int, T> e(std::unexpect, 5);
+ std::expected<V, T> e(std::unexpect, 5);
assert(!e.has_value());
assert(e.error() == 5);
}
-template <class T>
+template <class T, class V = int>
constexpr void testLValue() {
T t(5);
- std::expected<int, T> e(std::unexpect, t);
+ std::expected<V, T> e(std::unexpect, t);
assert(!e.has_value());
assert(e.error() == 5);
}
-template <class T>
+template <class T, class V = int>
constexpr void testRValue() {
- std::expected<int, T> e(std::unexpect, T(5));
+ std::expected<V, T> e(std::unexpect, T(5));
assert(!e.has_value());
assert(e.error() == 5);
}
@@ -80,10 +81,13 @@ constexpr bool test() {
testInt<int>();
testInt<CopyOnly>();
testInt<MoveOnly>();
+ testInt<TailClobberer<1>, bool>();
testLValue<int>();
testLValue<CopyOnly>();
+ testLValue<TailClobberer<1>, bool>();
testRValue<int>();
testRValue<MoveOnly>();
+ testRValue<TailClobberer<1>, bool>();
// no arg
{
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect_init_list.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect_init_list.pass.cpp
index 7cc36b51e415342..4c280fc32a4e952 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect_init_list.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect_init_list.pass.cpp
@@ -28,6 +28,7 @@
#include "MoveOnly.h"
#include "test_macros.h"
+#include "../../types.h"
// Test Constraints:
static_assert(
@@ -90,6 +91,12 @@ constexpr bool test() {
assert(m.get() == 0);
}
+ // TailClobberer
+ {
+ std::expected<bool, TailClobberer<1>> e(std::unexpect, {1, 2, 3});
+ assert(!e.has_value());
+ }
+
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.copy.pass.cpp
index 09ac91182b3b892..b3bb71cf36837f4 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.copy.pass.cpp
@@ -27,6 +27,7 @@
#include "MoveOnly.h"
#include "test_macros.h"
+#include "../../types.h"
// Test Constraints
static_assert(std::is_constructible_v<std::expected<int, int>, const std::unexpected<int>&>);
@@ -49,10 +50,10 @@ struct MyInt {
friend constexpr bool operator==(const MyInt&, const MyInt&) = default;
};
-template <class T>
+template <class T, class V = int>
constexpr void testUnexpected() {
const std::unexpected<int> u(5);
- std::expected<int, T> e(u);
+ std::expected<V, T> e(u);
assert(!e.has_value());
assert(e.error() == 5);
}
@@ -60,6 +61,7 @@ constexpr void testUnexpected() {
constexpr bool test() {
testUnexpected<int>();
testUnexpected<MyInt>();
+ testUnexpected<TailClobberer<1>, bool>();
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.move.pass.cpp
index 9aaaa3fe1a448dd..419c789c86d516c 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.move.pass.cpp
@@ -27,6 +27,7 @@
#include "MoveOnly.h"
#include "test_macros.h"
+#include "../../types.h"
// Test Constraints
static_assert(std::is_constructible_v<std::expected<int, int>, std::unexpected<int>>);
@@ -49,10 +50,10 @@ struct MyInt {
friend constexpr bool operator==(const MyInt&, const MyInt&) = default;
};
-template <class Err>
+template <class Err, class V = int>
constexpr void testInt() {
std::unexpected<int> u(5);
- std::expected<int, Err> e(std::move(u));
+ std::expected<V, Err> e(std::move(u));
assert(!e.has_value());
assert(e.error() == 5);
}
@@ -69,6 +70,7 @@ constexpr bool test() {
testInt<int>();
testInt<MyInt>();
testInt<MoveOnly>();
+ testInt<TailClobberer<1>, bool>();
testMoveOnly();
return true;
}
diff --git a/libcxx/test/std/utilities/expected/types.h b/libcxx/test/std/utilities/expected/types.h
index 6a7990383ada8b5..58c2df504bfb265 100644
--- a/libcxx/test/std/utilities/expected/types.h
+++ b/libcxx/test/std/utilities/expected/types.h
@@ -112,13 +112,35 @@ struct TrackedMove {
// See https://github.com/llvm/llvm-project/issues/68552 for details.
template <int constant>
struct TailClobberer {
- constexpr TailClobberer() {
+ constexpr TailClobberer() noexcept {
if (!std::is_constant_evaluated()) {
std::memset(this, constant, sizeof(*this));
}
+ // Always set `b` itself to `false` so that the comparison works.
+ b = false;
}
+ constexpr TailClobberer(const TailClobberer&) : TailClobberer() {}
+ constexpr TailClobberer(TailClobberer&&) = default;
+ // Converts from `int`/`std::initializer_list<int>, used in some tests.
+ constexpr TailClobberer(int) : TailClobberer() {}
+ constexpr TailClobberer(std::initializer_list<int>) noexcept : TailClobberer() {}
+
+ friend constexpr bool operator==(const TailClobberer&, const TailClobberer&) = default;
+
+private:
alignas(2) bool b;
};
+static_assert(!std::is_trivially_copy_constructible_v<TailClobberer<0>>);
+static_assert(std::is_trivially_move_constructible_v<TailClobberer<0>>);
+
+template <int constant>
+struct TailClobbererNonTrivialMove : TailClobberer<constant> {
+ using TailClobberer<constant>::TailClobberer;
+ constexpr TailClobbererNonTrivialMove(TailClobbererNonTrivialMove&&) noexcept : TailClobberer<constant>() {}
+};
+static_assert(!std::is_trivially_copy_constructible_v<TailClobbererNonTrivialMove<0>>);
+static_assert(std::is_move_constructible_v<TailClobbererNonTrivialMove<0>>);
+static_assert(!std::is_trivially_move_constructible_v<TailClobbererNonTrivialMove<0>>);
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Except {};
>From 8e0800e03060dfbf492b0968eb9b410e1145e98a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 18:37:48 +0200
Subject: [PATCH 17/38] add some missing checks
---
.../expected/expected.expected/ctor/ctor.move.pass.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.move.pass.cpp
index f542778b3d5ff90..aaf357fbc518afc 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.move.pass.cpp
@@ -119,6 +119,7 @@ constexpr bool test() {
std::expected<TailClobbererNonTrivialMove<0>, bool> e1;
auto e2 = std::move(e1);
assert(e2.has_value());
+ assert(e1.has_value());
}
// move TailClobbererNonTrivialMove as error
@@ -126,6 +127,7 @@ constexpr bool test() {
std::expected<bool, TailClobbererNonTrivialMove<1>> e1(std::unexpect);
auto e2 = std::move(e1);
assert(!e2.has_value());
+ assert(!e1.has_value());
}
return true;
>From 15b39e71e5c8902a91553d6905de2256e6f9dc3a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 18:38:04 +0200
Subject: [PATCH 18/38] add tests for expected<void>
---
.../expected.void/ctor/ctor.convert.copy.pass.cpp | 9 +++++++++
.../expected.void/ctor/ctor.convert.move.pass.cpp | 9 +++++++++
.../expected/expected.void/ctor/ctor.copy.pass.cpp | 8 ++++++++
.../expected/expected.void/ctor/ctor.move.pass.cpp | 9 +++++++++
.../expected/expected.void/ctor/ctor.unexpect.pass.cpp | 4 ++++
.../expected.void/ctor/ctor.unexpect_init_list.pass.cpp | 7 +++++++
.../expected.void/ctor/ctor.unexpected.copy.pass.cpp | 2 ++
.../expected.void/ctor/ctor.unexpected.move.pass.cpp | 2 ++
8 files changed, 50 insertions(+)
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.copy.pass.cpp
index 40f8efa5f94bf9d..118e4274e9ef893 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.copy.pass.cpp
@@ -33,6 +33,7 @@
#include <utility>
#include "test_macros.h"
+#include "../../types.h"
// Test Constraints:
template <class T1, class Err1, class T2, class Err2>
@@ -97,6 +98,14 @@ constexpr bool test() {
assert(e1.error() == 5);
}
+ // convert TailClobberer
+ {
+ const std::expected<void, TailClobbererNonTrivialMove<1>> e1;
+ std::expected<void, TailClobberer<1>> e2 = e1;
+ assert(!e2.has_value());
+ assert(!e1.has_value());
+ }
+
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.move.pass.cpp
index b28fc7a03bb3445..32f6055bc5c6181 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.move.pass.cpp
@@ -34,6 +34,7 @@
#include "MoveOnly.h"
#include "test_macros.h"
+#include "../../types.h"
// Test Constraints:
template <class T1, class Err1, class T2, class Err2>
@@ -98,6 +99,14 @@ constexpr bool test() {
assert(e1.error().get() == 0);
}
+ // convert TailClobberer
+ {
+ std::expected<void, TailClobbererNonTrivialMove<1>> e1;
+ std::expected<void, TailClobberer<1>> e2 = std::move(e1);
+ assert(!e2.has_value());
+ assert(!e1.has_value());
+ }
+
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.copy.pass.cpp
index 689f152a3ac5547..f46826a4e328965 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.copy.pass.cpp
@@ -62,6 +62,14 @@ constexpr bool test() {
assert(!e2.has_value());
assert(e2.error() == 5);
}
+
+ // copy TailClobberer as error
+ {
+ const std::expected<void, TailClobberer<1>> e1(std::unexpect);
+ auto e2 = e1;
+ assert(!e2.has_value());
+ }
+
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.move.pass.cpp
index 61bce2be4897f0a..38affdcadde3f33 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.move.pass.cpp
@@ -76,6 +76,15 @@ constexpr bool test() {
assert(e2.error() == 5);
assert(!e1.has_value());
}
+
+ // move TailClobbererNonTrivialMove as error
+ {
+ std::expected<void, TailClobbererNonTrivialMove<1>> e1(std::unexpect);
+ auto e2 = std::move(e1);
+ assert(!e2.has_value());
+ assert(!e1.has_value());
+ }
+
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect.pass.cpp
index 0a857c77d9c7a68..62c3817a8a66f61 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect.pass.cpp
@@ -26,6 +26,7 @@
#include "MoveOnly.h"
#include "test_macros.h"
+#include "../../types.h"
// Test Constraints:
static_assert(std::is_constructible_v<std::expected<void, int>, std::unexpect_t>);
@@ -80,10 +81,13 @@ constexpr bool test() {
testInt<int>();
testInt<CopyOnly>();
testInt<MoveOnly>();
+ testInt<TailClobberer<1>>();
testLValue<int>();
testLValue<CopyOnly>();
+ testLValue<TailClobberer<1>>();
testRValue<int>();
testRValue<MoveOnly>();
+ testRValue<TailClobberer<1>>();
// no arg
{
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect_init_list.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect_init_list.pass.cpp
index a73921225f1fae4..28b4165bc27dc16 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect_init_list.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect_init_list.pass.cpp
@@ -28,6 +28,7 @@
#include "MoveOnly.h"
#include "test_macros.h"
+#include "../../types.h"
// Test Constraints:
static_assert(
@@ -89,6 +90,12 @@ constexpr bool test() {
assert(m.get() == 0);
}
+ // TailClobberer
+ {
+ std::expected<void, TailClobberer<1>> e(std::unexpect, {1, 2, 3});
+ assert(!e.has_value());
+ }
+
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.copy.pass.cpp
index 89e1c9275e3e062..17063a97cb133ad 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.copy.pass.cpp
@@ -27,6 +27,7 @@
#include "MoveOnly.h"
#include "test_macros.h"
+#include "../../types.h"
// Test Constraints
static_assert(std::is_constructible_v<std::expected<void, int>, const std::unexpected<int>&>);
@@ -60,6 +61,7 @@ constexpr void testUnexpected() {
constexpr bool test() {
testUnexpected<int>();
testUnexpected<MyInt>();
+ testUnexpected<TailClobberer<1>>();
return true;
}
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.move.pass.cpp
index 2ddcb63c085f052..792ef341879eac0 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.move.pass.cpp
@@ -27,6 +27,7 @@
#include "MoveOnly.h"
#include "test_macros.h"
+#include "../../types.h"
// Test Constraints
static_assert(std::is_constructible_v<std::expected<void, int>, std::unexpected<int>>);
@@ -69,6 +70,7 @@ constexpr bool test() {
testInt<int>();
testInt<MyInt>();
testInt<MoveOnly>();
+ testInt<TailClobberer<1>>();
testMoveOnly();
return true;
}
>From e01258232d4905323b2180b0c82a9bcefe7067f1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sun, 22 Oct 2023 08:24:40 +0200
Subject: [PATCH 19/38] fix compile errors
---
.../expected/expected.void/ctor/ctor.convert.copy.pass.cpp | 2 +-
.../expected/expected.void/ctor/ctor.convert.move.pass.cpp | 2 +-
.../utilities/expected/expected.void/ctor/ctor.copy.pass.cpp | 1 +
.../utilities/expected/expected.void/ctor/ctor.move.pass.cpp | 1 +
4 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.copy.pass.cpp
index 118e4274e9ef893..30533206e65a306 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.copy.pass.cpp
@@ -100,7 +100,7 @@ constexpr bool test() {
// convert TailClobberer
{
- const std::expected<void, TailClobbererNonTrivialMove<1>> e1;
+ const std::expected<void, TailClobbererNonTrivialMove<1>> e1(std::unexpect);
std::expected<void, TailClobberer<1>> e2 = e1;
assert(!e2.has_value());
assert(!e1.has_value());
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.move.pass.cpp
index 32f6055bc5c6181..d9db49c27383155 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.move.pass.cpp
@@ -101,7 +101,7 @@ constexpr bool test() {
// convert TailClobberer
{
- std::expected<void, TailClobbererNonTrivialMove<1>> e1;
+ std::expected<void, TailClobbererNonTrivialMove<1>> e1(std::unexpect);
std::expected<void, TailClobberer<1>> e2 = std::move(e1);
assert(!e2.has_value());
assert(!e1.has_value());
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.copy.pass.cpp
index f46826a4e328965..83e049857a93d4b 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.copy.pass.cpp
@@ -25,6 +25,7 @@
#include <utility>
#include "test_macros.h"
+#include "../../types.h"
struct NonCopyable {
NonCopyable(const NonCopyable&) = delete;
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.move.pass.cpp
index 38affdcadde3f33..777b412f65f64f1 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.move.pass.cpp
@@ -25,6 +25,7 @@
#include <utility>
#include "test_macros.h"
+#include "../../types.h"
struct NonMovable {
NonMovable(NonMovable&&) = delete;
>From cc5f1768f4fbe7d014c0370af9f8cfd230e55c11 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sun, 22 Oct 2023 08:28:14 +0200
Subject: [PATCH 20/38] remove declaration of local Except struct
---
.../expected/expected.expected/ctor/ctor.convert.copy.pass.cpp | 2 --
.../expected/expected.expected/ctor/ctor.convert.move.pass.cpp | 2 --
.../expected/expected.expected/ctor/ctor.copy.pass.cpp | 2 --
.../expected/expected.expected/ctor/ctor.default.pass.cpp | 2 --
.../expected/expected.expected/ctor/ctor.inplace.pass.cpp | 2 --
.../expected/expected.expected/ctor/ctor.move.pass.cpp | 2 --
.../expected/expected.void/ctor/ctor.convert.copy.pass.cpp | 2 --
.../expected/expected.void/ctor/ctor.convert.move.pass.cpp | 2 --
.../utilities/expected/expected.void/ctor/ctor.copy.pass.cpp | 2 --
.../utilities/expected/expected.void/ctor/ctor.move.pass.cpp | 2 --
.../expected/expected.void/ctor/ctor.unexpect.pass.cpp | 2 --
.../expected.void/ctor/ctor.unexpect_init_list.pass.cpp | 2 --
.../expected/expected.void/ctor/ctor.unexpected.copy.pass.cpp | 2 --
.../expected/expected.void/ctor/ctor.unexpected.move.pass.cpp | 2 --
14 files changed, 28 deletions(-)
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.copy.pass.cpp
index 2cc952358e821ce..16de28d97039617 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.copy.pass.cpp
@@ -175,8 +175,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct ThrowingInt {
ThrowingInt(int) { throw Except{}; }
};
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.move.pass.cpp
index 31819cd33ef6c33..0e30ea2c7fe0b45 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.move.pass.cpp
@@ -174,8 +174,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct ThrowingInt {
ThrowingInt(int) { throw Except{}; }
};
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.copy.pass.cpp
index 1b1c4a59606920a..581df51207da29f 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.copy.pass.cpp
@@ -114,8 +114,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing() = default;
Throwing(const Throwing&) { throw Except{}; }
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.default.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.default.pass.cpp
index ea23d8aa735c84c..dcd046bdd9d8936 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.default.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.default.pass.cpp
@@ -60,8 +60,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing() { throw Except{}; };
};
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace.pass.cpp
index 0b41eb44883e43c..88ec41939439afa 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace.pass.cpp
@@ -115,8 +115,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing(int) { throw Except{}; };
};
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.move.pass.cpp
index aaf357fbc518afc..cd89e2445860a2a 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.move.pass.cpp
@@ -135,8 +135,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing() = default;
Throwing(Throwing&&) { throw Except{}; }
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.copy.pass.cpp
index 30533206e65a306..05f556e25eac14c 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.copy.pass.cpp
@@ -111,8 +111,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct ThrowingInt {
ThrowingInt(int) { throw Except{}; }
};
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.move.pass.cpp
index d9db49c27383155..a48888be53ee07a 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.convert.move.pass.cpp
@@ -112,8 +112,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct ThrowingInt {
ThrowingInt(int) { throw Except{}; }
};
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.copy.pass.cpp
index 83e049857a93d4b..7c04a5fa9d04404 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.copy.pass.cpp
@@ -76,8 +76,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing() = default;
Throwing(const Throwing&) { throw Except{}; }
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.move.pass.cpp
index 777b412f65f64f1..bfb5028c9264d02 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.move.pass.cpp
@@ -91,8 +91,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing() = default;
Throwing(Throwing&&) { throw Except{}; }
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect.pass.cpp
index 62c3817a8a66f61..85bc98d7f462de1 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect.pass.cpp
@@ -115,8 +115,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing(int) { throw Except{}; };
};
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect_init_list.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect_init_list.pass.cpp
index 28b4165bc27dc16..4128668a6b07bcd 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect_init_list.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpect_init_list.pass.cpp
@@ -101,8 +101,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing(std::initializer_list<int>, int) { throw Except{}; };
};
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.copy.pass.cpp
index 17063a97cb133ad..ba738a3e339de78 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.copy.pass.cpp
@@ -67,8 +67,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing(int) { throw Except{}; }
};
diff --git a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.move.pass.cpp
index 792ef341879eac0..33a5e7293df2143 100644
--- a/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/ctor/ctor.unexpected.move.pass.cpp
@@ -77,8 +77,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing(int) { throw Except{}; }
};
>From 8c27ea80ecc8e75ec2bfa18865f933ca72247bdf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sun, 22 Oct 2023 09:23:43 +0200
Subject: [PATCH 21/38] remove more declarations of local Except structs
---
.../expected.expected/ctor/ctor.inplace_init_list.pass.cpp | 2 --
.../utilities/expected/expected.expected/ctor/ctor.u.pass.cpp | 2 --
.../expected/expected.expected/ctor/ctor.unexpect.pass.cpp | 2 --
.../expected.expected/ctor/ctor.unexpect_init_list.pass.cpp | 2 --
.../expected.expected/ctor/ctor.unexpected.copy.pass.cpp | 2 --
.../expected.expected/ctor/ctor.unexpected.move.pass.cpp | 2 --
6 files changed, 12 deletions(-)
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace_init_list.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace_init_list.pass.cpp
index 143f9d7dab11398..a97086fcc9195be 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace_init_list.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace_init_list.pass.cpp
@@ -102,8 +102,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing(std::initializer_list<int>, int) { throw Except{}; };
};
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp
index 16dd531a410c2a8..1cf3d9cc2ef49c9 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp
@@ -157,8 +157,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing(int) { throw Except{}; };
};
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect.pass.cpp
index a0b334a25b0cb81..27ce97737d288ba 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect.pass.cpp
@@ -115,8 +115,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing(int) { throw Except{}; };
};
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect_init_list.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect_init_list.pass.cpp
index 4c280fc32a4e952..4f5d3d1492d37ae 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect_init_list.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpect_init_list.pass.cpp
@@ -102,8 +102,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing(std::initializer_list<int>, int) { throw Except{}; };
};
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.copy.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.copy.pass.cpp
index b3bb71cf36837f4..bbfd3048533c793 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.copy.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.copy.pass.cpp
@@ -67,8 +67,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing(int) { throw Except{}; }
};
diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.move.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.move.pass.cpp
index 419c789c86d516c..800d47bda69587b 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.move.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.unexpected.move.pass.cpp
@@ -77,8 +77,6 @@ constexpr bool test() {
void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
- struct Except {};
-
struct Throwing {
Throwing(int) { throw Except{}; }
};
>From 7be45f2497a7a7abc1bcf100fb68584d32a734f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sun, 15 Oct 2023 22:09:51 +0200
Subject: [PATCH 22/38] ensure that expected has no tail padding
---
libcxx/include/__expected/expected.h | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index f874a3a126a58f3..fbe9163cda9b9cf 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -18,6 +18,7 @@
#include <__memory/addressof.h>
#include <__memory/construct_at.h>
#include <__type_traits/conjunction.h>
+#include <__type_traits/datasizeof.h>
#include <__type_traits/disjunction.h>
#include <__type_traits/integral_constant.h>
#include <__type_traits/is_assignable.h>
@@ -942,8 +943,17 @@ class expected {
_LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
};
+ _LIBCPP_HIDE_FROM_ABI static constexpr auto __calculate_padding() {
+ struct __calc_expected {
+ _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
+ bool __has_val_;
+ };
+ return sizeof(__calc_expected) - __libcpp_datasizeof<__calc_expected>::value;
+ }
+
_LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
bool __has_val_;
+ char __padding_[__calculate_padding()]{};
};
template <class _Tp, class _Err>
@@ -1539,8 +1549,18 @@ class expected<_Tp, _Err> {
_LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
};
+
+ _LIBCPP_HIDE_FROM_ABI static constexpr auto __calculate_padding() {
+ struct __calc_expected {
+ _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
+ bool __has_val_;
+ };
+ return sizeof(__calc_expected) - __libcpp_datasizeof<__calc_expected>::value;
+ }
+
_LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
bool __has_val_;
+ char __padding_[__calculate_padding()]{};
};
_LIBCPP_END_NAMESPACE_STD
>From b13c90024b230b83c4dd6242ce891f51a26f62f4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Fri, 20 Oct 2023 19:40:18 +0200
Subject: [PATCH 23/38] handle GCC's warning in __libcpp_datasizeof
---
libcxx/include/__type_traits/datasizeof.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/include/__type_traits/datasizeof.h b/libcxx/include/__type_traits/datasizeof.h
index 5688e3293a69eb5..96404ad15a73376 100644
--- a/libcxx/include/__type_traits/datasizeof.h
+++ b/libcxx/include/__type_traits/datasizeof.h
@@ -51,6 +51,7 @@ struct __libcpp_datasizeof {
// the use as an extension.
_LIBCPP_DIAGNOSTIC_PUSH
_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Winvalid-offsetof")
+ _LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Winvalid-offsetof")
static const size_t value = offsetof(_FirstPaddingByte<>, __first_padding_byte_);
_LIBCPP_DIAGNOSTIC_POP
};
>From 14468ccd3dc05c79b62ee27013d55013d6253da8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Fri, 20 Oct 2023 19:53:22 +0200
Subject: [PATCH 24/38] implement different approach to padding calculation
This tries to fix two issues:
- There should be no artificial padding if the parameter type(s) don't
need it, for example if they have no tail padding
- Prevent the padding array being zero length
---
libcxx/include/__expected/expected.h | 53 +++++++++++++++++-----------
1 file changed, 32 insertions(+), 21 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index fbe9163cda9b9cf..2de1d05de601d24 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -89,6 +89,34 @@ _LIBCPP_HIDE_FROM_ABI void __throw_bad_expected_access(_Arg&& __arg) {
# endif
}
+template <size_t __Padding>
+struct __expected_padding {
+ using type = char[__Padding];
+};
+
+template <>
+struct __expected_padding<0> {
+ using type = struct {};
+};
+
+template <class _Union>
+_LIBCPP_HIDE_FROM_ABI constexpr size_t __expected_calculate_padding() {
+ struct __calc_expected {
+ _LIBCPP_NO_UNIQUE_ADDRESS _Union __union_;
+ bool __has_val_;
+ };
+
+ size_t __datasize = __libcpp_datasizeof<__calc_expected>::value;
+ return sizeof(_Union) < __datasize ? 0 : sizeof(_Union) - __datasize;
+}
+
+// An object of this type must be the last member of the `expected` class to
+// ensure `expected`'s datasize is large enough to fit the parameter type(s).
+// It should be value-initialized so it is safe to copy when `expected`'s
+// copy operators are invoked.
+template <typename _Union>
+using __expected_padding_t = __expected_padding<__expected_calculate_padding<_Union>()>::type;
+
template <class _Tp, class _Err>
class expected {
static_assert(
@@ -943,17 +971,9 @@ class expected {
_LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
};
- _LIBCPP_HIDE_FROM_ABI static constexpr auto __calculate_padding() {
- struct __calc_expected {
- _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
- bool __has_val_;
- };
- return sizeof(__calc_expected) - __libcpp_datasizeof<__calc_expected>::value;
- }
-
_LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
- bool __has_val_;
- char __padding_[__calculate_padding()]{};
+ _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
+ _LIBCPP_NO_UNIQUE_ADDRESS __expected_padding_t<__union_t<_Tp, _Err>> __padding_{};
};
template <class _Tp, class _Err>
@@ -1549,18 +1569,9 @@ class expected<_Tp, _Err> {
_LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
};
-
- _LIBCPP_HIDE_FROM_ABI static constexpr auto __calculate_padding() {
- struct __calc_expected {
- _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
- bool __has_val_;
- };
- return sizeof(__calc_expected) - __libcpp_datasizeof<__calc_expected>::value;
- }
-
_LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
- bool __has_val_;
- char __padding_[__calculate_padding()]{};
+ _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
+ _LIBCPP_NO_UNIQUE_ADDRESS __expected_padding_t<__union_t<_Err>> __padding_{};
};
_LIBCPP_END_NAMESPACE_STD
>From 535d6545689aeb3c79f35259f4d0d8d2aad91605 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Fri, 20 Oct 2023 19:56:59 +0200
Subject: [PATCH 25/38] add tests
---
.../no_unique_address.compile.pass.cpp | 17 +++++++++++++++++
.../no_unique_address.compile.pass.cpp | 15 +++++++++++++++
2 files changed, 32 insertions(+)
diff --git a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
index c8c217054ae6c43..06f597d54871ee1 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
@@ -26,6 +26,13 @@ struct B : public A {
virtual ~B() = default;
};
+struct BoolWithPadding {
+ explicit operator bool() { return b; }
+
+private:
+ alignas(1024) bool b = false;
+};
+
static_assert(sizeof(std::expected<Empty, Empty>) == sizeof(bool));
static_assert(sizeof(std::expected<Empty, A>) == 2 * sizeof(int) + alignof(std::expected<Empty, A>));
static_assert(sizeof(std::expected<Empty, B>) == sizeof(B) + alignof(std::expected<Empty, B>));
@@ -33,3 +40,13 @@ static_assert(sizeof(std::expected<A, Empty>) == 2 * sizeof(int) + alignof(std::
static_assert(sizeof(std::expected<A, A>) == 2 * sizeof(int) + alignof(std::expected<A, A>));
static_assert(sizeof(std::expected<B, Empty>) == sizeof(B) + alignof(std::expected<B, Empty>));
static_assert(sizeof(std::expected<B, B>) == sizeof(B) + alignof(std::expected<B, B>));
+
+// Check that `expected`'s datasize is large enough for the parameter type(s).
+static_assert(sizeof(std::expected<BoolWithPadding, Empty>) ==
+ std::__libcpp_datasizeof<std::expected<BoolWithPadding, Empty>>::value);
+static_assert(sizeof(std::expected<Empty, BoolWithPadding>) ==
+ std::__libcpp_datasizeof<std::expected<Empty, BoolWithPadding>>::value);
+
+// In this case, there should be tail padding in the `expected` because `A`
+// itself does _not_ have tail padding.
+static_assert(sizeof(std::expected<A, A>) > std::__libcpp_datasizeof<std::expected<A, A>>::value);
diff --git a/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
index dc59a6228386bcd..29cf8dff0d2b288 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
@@ -26,6 +26,21 @@ struct B : public A {
virtual ~B() = default;
};
+struct BoolWithPadding {
+ explicit operator bool() { return b; }
+
+private:
+ alignas(1024) bool b = false;
+};
+
static_assert(sizeof(std::expected<void, Empty>) == sizeof(bool));
static_assert(sizeof(std::expected<void, A>) == 2 * sizeof(int) + alignof(std::expected<void, A>));
static_assert(sizeof(std::expected<void, B>) == sizeof(B) + alignof(std::expected<void, B>));
+
+// Check that `expected`'s datasize is large enough for the parameter type(s).
+static_assert(sizeof(std::expected<void, BoolWithPadding>) ==
+ std::__libcpp_datasizeof<std::expected<void, BoolWithPadding>>::value);
+
+// In this case, there should be tail padding in the `expected` because `A`
+// itself does _not_ have tail padding.
+static_assert(sizeof(std::expected<void, A>) > std::__libcpp_datasizeof<std::expected<void, A>>::value);
>From 3803cd68c9d53289cc1af7532770ce2e6e822128 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Fri, 20 Oct 2023 22:02:20 +0200
Subject: [PATCH 26/38] implement repr/compact_pair approach
---
libcxx/include/__expected/expected.h | 443 +++++++++---------
.../no_unique_address.compile.pass.cpp | 1 +
.../no_unique_address.compile.pass.cpp | 1 +
3 files changed, 212 insertions(+), 233 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 2de1d05de601d24..9eaa20c30937d33 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -89,33 +89,11 @@ _LIBCPP_HIDE_FROM_ABI void __throw_bad_expected_access(_Arg&& __arg) {
# endif
}
-template <size_t __Padding>
-struct __expected_padding {
- using type = char[__Padding];
-};
-
-template <>
-struct __expected_padding<0> {
- using type = struct {};
-};
-
template <class _Union>
-_LIBCPP_HIDE_FROM_ABI constexpr size_t __expected_calculate_padding() {
- struct __calc_expected {
- _LIBCPP_NO_UNIQUE_ADDRESS _Union __union_;
- bool __has_val_;
- };
-
- size_t __datasize = __libcpp_datasizeof<__calc_expected>::value;
- return sizeof(_Union) < __datasize ? 0 : sizeof(_Union) - __datasize;
-}
-
-// An object of this type must be the last member of the `expected` class to
-// ensure `expected`'s datasize is large enough to fit the parameter type(s).
-// It should be value-initialized so it is safe to copy when `expected`'s
-// copy operators are invoked.
-template <typename _Union>
-using __expected_padding_t = __expected_padding<__expected_calculate_padding<_Union>()>::type;
+struct __expected_repr {
+ [[no_unique_address]] _Union __union_;
+ [[no_unique_address]] bool __has_val_;
+};
template <class _Tp, class _Err>
class expected {
@@ -148,7 +126,7 @@ class expected {
_LIBCPP_HIDE_FROM_ABI constexpr expected()
noexcept(is_nothrow_default_constructible_v<_Tp>) // strengthened
requires is_default_constructible_v<_Tp>
- : __union_(std::in_place), __has_val_(true) {}
+ : __repr_{.__union_{std::in_place}, .__has_val_ = true} {}
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
@@ -163,7 +141,7 @@ class expected {
noexcept(is_nothrow_copy_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Err>) // strengthened
requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err> &&
!(is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Err>))
- : __union_(__other.__has_val_, __other.__union_), __has_val_(__other.__has_val_) { }
+ : __repr_{.__union_{__other.__repr_.__has_val_, __other.__repr_.__union_}, .__has_val_ = __other.__repr_.__has_val_} { }
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err>
@@ -174,7 +152,8 @@ class expected {
noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_move_constructible_v<_Err>)
requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err> &&
!(is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>))
- : __union_(__other.__has_val_, std::move(__other.__union_)), __has_val_(__other.__has_val_) { }
+ : __repr_{.__union_{__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)},
+ .__has_val_ = __other.__repr_.__has_val_} { }
private:
template <class _Up, class _OtherErr, class _UfQual, class _OtherErrQual>
@@ -199,12 +178,12 @@ class expected {
template <class _Func, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
std::__expected_construct_in_place_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)
- : __union_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...), __has_val_(true) {}
+ : __repr_{.__union_{__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...}, .__has_val_ = true} {}
template <class _Func, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
std::__expected_construct_unexpected_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)
- : __union_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...), __has_val_(false) {}
+ : __repr_{.__union_{__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...}, .__has_val_ = false} {}
public:
template <class _Up, class _OtherErr>
@@ -214,14 +193,16 @@ class expected {
expected(const expected<_Up, _OtherErr>& __other)
noexcept(is_nothrow_constructible_v<_Tp, const _Up&> &&
is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __union_(__other.__has_val_, __other.__union_), __has_val_(__other.__has_val_) {}
+ : __repr_{.__union_{__other.__repr_.__has_val_, __other.__repr_.__union_},
+ .__has_val_ = __other.__repr_.__has_val_} {}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, _Up, _OtherErr>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp> || !is_convertible_v<_OtherErr, _Err>)
expected(expected<_Up, _OtherErr>&& __other)
noexcept(is_nothrow_constructible_v<_Tp, _Up> && is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __union_(__other.__has_val_, std::move(__other.__union_)), __has_val_(__other.__has_val_) {}
+ : __repr_{.__union_{__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)},
+ .__has_val_ = __other.__repr_.__has_val_} {}
template <class _Up = _Tp>
requires(!is_same_v<remove_cvref_t<_Up>, in_place_t> && !is_same_v<expected, remove_cvref_t<_Up>> &&
@@ -229,47 +210,47 @@ class expected {
(!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_expected<remove_cvref_t<_Up>>::value))
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp>)
expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
- : __union_(std::in_place, std::forward<_Up>(__u)), __has_val_(true) {}
+ : __repr_{.__union_{std::in_place, std::forward<_Up>(__u)}, .__has_val_ = true} {}
template <class _OtherErr>
requires is_constructible_v<_Err, const _OtherErr&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const unexpected<_OtherErr>& __unex)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __union_(std::unexpect, __unex.error()), __has_val_(false) {}
+ : __repr_{.__union_{std::unexpect, __unex.error()}, .__has_val_ = false} {}
template <class _OtherErr>
requires is_constructible_v<_Err, _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(unexpected<_OtherErr>&& __unex)
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __union_(std::unexpect, std::move(__unex.error())), __has_val_(false) {}
+ : __repr_{.__union_{std::unexpect, std::move(__unex.error())}, .__has_val_ = false} {}
template <class... _Args>
requires is_constructible_v<_Tp, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Tp, _Args...>) // strengthened
- : __union_(std::in_place, std::forward<_Args>(__args)...), __has_val_(true) {}
+ : __repr_{.__union_{std::in_place, std::forward<_Args>(__args)...}, .__has_val_ = true} {}
template <class _Up, class... _Args>
requires is_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit
expected(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, _Args...>) // strengthened
- : __union_(std::in_place, __il, std::forward<_Args>(__args)...), __has_val_(true) {}
+ : __repr_{.__union_{std::in_place, __il, std::forward<_Args>(__args)...}, .__has_val_ = true} {}
template <class... _Args>
requires is_constructible_v<_Err, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
- : __union_(std::unexpect, std::forward<_Args>(__args)...), __has_val_(false) {}
+ : __repr_{.__union_{std::unexpect, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
template <class _Up, class... _Args>
requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit
expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
- : __union_(std::unexpect, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
+ : __repr_{.__union_{std::unexpect, __il, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
// [expected.object.dtor], destructor
@@ -280,10 +261,10 @@ class expected {
_LIBCPP_HIDE_FROM_ABI constexpr ~expected()
requires(!is_trivially_destructible_v<_Tp> || !is_trivially_destructible_v<_Err>)
{
- if (__has_val_) {
- std::destroy_at(std::addressof(__union_.__val_));
+ if (__repr_.__has_val_) {
+ std::destroy_at(std::addressof(__repr_.__union_.__val_));
} else {
- std::destroy_at(std::addressof(__union_.__unex_));
+ std::destroy_at(std::addressof(__repr_.__union_.__unex_));
}
}
@@ -327,17 +308,17 @@ class expected {
(is_nothrow_move_constructible_v<_Tp> ||
is_nothrow_move_constructible_v<_Err>))
{
- if (__has_val_ && __rhs.__has_val_) {
- __union_.__val_ = __rhs.__union_.__val_;
- } else if (__has_val_) {
- __reinit_expected(__union_.__unex_, __union_.__val_, __rhs.__union_.__unex_);
- } else if (__rhs.__has_val_) {
- __reinit_expected(__union_.__val_, __union_.__unex_, __rhs.__union_.__val_);
+ if (__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
+ __repr_.__union_.__val_ = __rhs.__repr_.__union_.__val_;
+ } else if (__repr_.__has_val_) {
+ __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, __rhs.__repr_.__union_.__unex_);
+ } else if (__rhs.__repr_.__has_val_) {
+ __reinit_expected(__repr_.__union_.__val_, __repr_.__union_.__unex_, __rhs.__repr_.__union_.__val_);
} else {
- __union_.__unex_ = __rhs.__union_.__unex_;
+ __repr_.__union_.__unex_ = __rhs.__repr_.__union_.__unex_;
}
// note: only reached if no exception+rollback was done inside __reinit_expected
- __has_val_ = __rhs.__has_val_;
+ __repr_.__has_val_ = __rhs.__repr_.__has_val_;
return *this;
}
@@ -353,17 +334,17 @@ class expected {
(is_nothrow_move_constructible_v<_Tp> ||
is_nothrow_move_constructible_v<_Err>))
{
- if (__has_val_ && __rhs.__has_val_) {
- __union_.__val_ = std::move(__rhs.__union_.__val_);
- } else if (__has_val_) {
- __reinit_expected(__union_.__unex_, __union_.__val_, std::move(__rhs.__union_.__unex_));
- } else if (__rhs.__has_val_) {
- __reinit_expected(__union_.__val_, __union_.__unex_, std::move(__rhs.__union_.__val_));
+ if (__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
+ __repr_.__union_.__val_ = std::move(__rhs.__repr_.__union_.__val_);
+ } else if (__repr_.__has_val_) {
+ __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, std::move(__rhs.__repr_.__union_.__unex_));
+ } else if (__rhs.__repr_.__has_val_) {
+ __reinit_expected(__repr_.__union_.__val_, __repr_.__union_.__unex_, std::move(__rhs.__repr_.__union_.__val_));
} else {
- __union_.__unex_ = std::move(__rhs.__union_.__unex_);
+ __repr_.__union_.__unex_ = std::move(__rhs.__repr_.__union_.__unex_);
}
// note: only reached if no exception+rollback was done inside __reinit_expected
- __has_val_ = __rhs.__has_val_;
+ __repr_.__has_val_ = __rhs.__repr_.__has_val_;
return *this;
}
@@ -377,11 +358,11 @@ class expected {
is_nothrow_move_constructible_v<_Tp> ||
is_nothrow_move_constructible_v<_Err>))
{
- if (__has_val_) {
- __union_.__val_ = std::forward<_Up>(__v);
+ if (__repr_.__has_val_) {
+ __repr_.__union_.__val_ = std::forward<_Up>(__v);
} else {
- __reinit_expected(__union_.__val_, __union_.__unex_, std::forward<_Up>(__v));
- __has_val_ = true;
+ __reinit_expected(__repr_.__union_.__val_, __repr_.__union_.__unex_, std::forward<_Up>(__v));
+ __repr_.__has_val_ = true;
}
return *this;
}
@@ -400,11 +381,11 @@ class expected {
template <class _OtherErr>
requires(__can_assign_from_unexpected<const _OtherErr&>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const unexpected<_OtherErr>& __un) {
- if (__has_val_) {
- __reinit_expected(__union_.__unex_, __union_.__val_, __un.error());
- __has_val_ = false;
+ if (__repr_.__has_val_) {
+ __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, __un.error());
+ __repr_.__has_val_ = false;
} else {
- __union_.__unex_ = __un.error();
+ __repr_.__union_.__unex_ = __un.error();
}
return *this;
}
@@ -412,11 +393,11 @@ class expected {
template <class _OtherErr>
requires(__can_assign_from_unexpected<_OtherErr>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(unexpected<_OtherErr>&& __un) {
- if (__has_val_) {
- __reinit_expected(__union_.__unex_, __union_.__val_, std::move(__un.error()));
- __has_val_ = false;
+ if (__repr_.__has_val_) {
+ __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, std::move(__un.error()));
+ __repr_.__has_val_ = false;
} else {
- __union_.__unex_ = std::move(__un.error());
+ __repr_.__union_.__unex_ = std::move(__un.error());
}
return *this;
}
@@ -424,27 +405,27 @@ class expected {
template <class... _Args>
requires is_nothrow_constructible_v<_Tp, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr _Tp& emplace(_Args&&... __args) noexcept {
- if (__has_val_) {
- std::destroy_at(std::addressof(__union_.__val_));
+ if (__repr_.__has_val_) {
+ std::destroy_at(std::addressof(__repr_.__union_.__val_));
} else {
- std::destroy_at(std::addressof(__union_.__unex_));
+ std::destroy_at(std::addressof(__repr_.__union_.__unex_));
}
- std::construct_at(std::addressof(__union_.__val_), std::forward<_Args>(__args)...);
- __has_val_ = true;
- return *std::addressof(__union_.__val_);
+ std::construct_at(std::addressof(__repr_.__union_.__val_), std::forward<_Args>(__args)...);
+ __repr_.__has_val_ = true;
+ return *std::addressof(__repr_.__union_.__val_);
}
template <class _Up, class... _Args>
requires is_nothrow_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) noexcept {
- if (__has_val_) {
- std::destroy_at(std::addressof(__union_.__val_));
+ if (__repr_.__has_val_) {
+ std::destroy_at(std::addressof(__repr_.__union_.__val_));
} else {
- std::destroy_at(std::addressof(__union_.__unex_));
+ std::destroy_at(std::addressof(__repr_.__union_.__unex_));
}
- std::construct_at(std::addressof(__union_.__val_), __il, std::forward<_Args>(__args)...);
- __has_val_ = true;
- return *std::addressof(__union_.__val_);
+ std::construct_at(std::addressof(__repr_.__union_.__val_), __il, std::forward<_Args>(__args)...);
+ __repr_.__has_val_ = true;
+ return *std::addressof(__repr_.__union_.__val_);
}
@@ -464,46 +445,46 @@ class expected {
{
auto __swap_val_unex_impl = [&](expected& __with_val, expected& __with_err) {
if constexpr (is_nothrow_move_constructible_v<_Err>) {
- _Err __tmp(std::move(__with_err.__union_.__unex_));
- std::destroy_at(std::addressof(__with_err.__union_.__unex_));
+ _Err __tmp(std::move(__with_err.__repr_.__union_.__unex_));
+ std::destroy_at(std::addressof(__with_err.__repr_.__union_.__unex_));
auto __trans = std::__make_exception_guard([&] {
- std::construct_at(std::addressof(__with_err.__union_.__unex_), std::move(__tmp));
+ std::construct_at(std::addressof(__with_err.__repr_.__union_.__unex_), std::move(__tmp));
});
- std::construct_at(std::addressof(__with_err.__union_.__val_), std::move(__with_val.__union_.__val_));
+ std::construct_at(std::addressof(__with_err.__repr_.__union_.__val_), std::move(__with_val.__repr_.__union_.__val_));
__trans.__complete();
- std::destroy_at(std::addressof(__with_val.__union_.__val_));
- std::construct_at(std::addressof(__with_val.__union_.__unex_), std::move(__tmp));
+ std::destroy_at(std::addressof(__with_val.__repr_.__union_.__val_));
+ std::construct_at(std::addressof(__with_val.__repr_.__union_.__unex_), std::move(__tmp));
} else {
static_assert(is_nothrow_move_constructible_v<_Tp>,
"To provide strong exception guarantee, Tp has to satisfy `is_nothrow_move_constructible_v` so "
"that it can be reverted to the previous state in case an exception is thrown during swap.");
- _Tp __tmp(std::move(__with_val.__union_.__val_));
- std::destroy_at(std::addressof(__with_val.__union_.__val_));
+ _Tp __tmp(std::move(__with_val.__repr_.__union_.__val_));
+ std::destroy_at(std::addressof(__with_val.__repr_.__union_.__val_));
auto __trans = std::__make_exception_guard([&] {
- std::construct_at(std::addressof(__with_val.__union_.__val_), std::move(__tmp));
+ std::construct_at(std::addressof(__with_val.__repr_.__union_.__val_), std::move(__tmp));
});
- std::construct_at(std::addressof(__with_val.__union_.__unex_), std::move(__with_err.__union_.__unex_));
+ std::construct_at(std::addressof(__with_val.__repr_.__union_.__unex_), std::move(__with_err.__repr_.__union_.__unex_));
__trans.__complete();
- std::destroy_at(std::addressof(__with_err.__union_.__unex_));
- std::construct_at(std::addressof(__with_err.__union_.__val_), std::move(__tmp));
+ std::destroy_at(std::addressof(__with_err.__repr_.__union_.__unex_));
+ std::construct_at(std::addressof(__with_err.__repr_.__union_.__val_), std::move(__tmp));
}
- __with_val.__has_val_ = false;
- __with_err.__has_val_ = true;
+ __with_val.__repr_.__has_val_ = false;
+ __with_err.__repr_.__has_val_ = true;
};
- if (__has_val_) {
- if (__rhs.__has_val_) {
+ if (__repr_.__has_val_) {
+ if (__rhs.__repr_.__has_val_) {
using std::swap;
- swap(__union_.__val_, __rhs.__union_.__val_);
+ swap(__repr_.__union_.__val_, __rhs.__repr_.__union_.__val_);
} else {
__swap_val_unex_impl(*this, __rhs);
}
} else {
- if (__rhs.__has_val_) {
+ if (__rhs.__repr_.__has_val_) {
__swap_val_unex_impl(__rhs, *this);
} else {
using std::swap;
- swap(__union_.__unex_, __rhs.__union_.__unex_);
+ swap(__repr_.__union_.__unex_, __rhs.__repr_.__union_.__unex_);
}
}
}
@@ -517,105 +498,105 @@ class expected {
// [expected.object.obs], observers
_LIBCPP_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__has_val_, "expected::operator-> requires the expected to contain a value");
- return std::addressof(__union_.__val_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator-> requires the expected to contain a value");
+ return std::addressof(__repr_.__union_.__val_);
}
_LIBCPP_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__has_val_, "expected::operator-> requires the expected to contain a value");
- return std::addressof(__union_.__val_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator-> requires the expected to contain a value");
+ return std::addressof(__repr_.__union_.__val_);
}
_LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__has_val_, "expected::operator* requires the expected to contain a value");
- return __union_.__val_;
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
+ return __repr_.__union_.__val_;
}
_LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__has_val_, "expected::operator* requires the expected to contain a value");
- return __union_.__val_;
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
+ return __repr_.__union_.__val_;
}
_LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__has_val_, "expected::operator* requires the expected to contain a value");
- return std::move(__union_.__val_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
+ return std::move(__repr_.__union_.__val_);
}
_LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__has_val_, "expected::operator* requires the expected to contain a value");
- return std::move(__union_.__val_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
+ return std::move(__repr_.__union_.__val_);
}
- _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return __has_val_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return __repr_.__has_val_; }
- _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __has_val_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __repr_.__has_val_; }
_LIBCPP_HIDE_FROM_ABI constexpr const _Tp& value() const& {
static_assert(is_copy_constructible_v<_Err>, "error_type has to be copy constructible");
- if (!__has_val_) {
+ if (!__repr_.__has_val_) {
std::__throw_bad_expected_access<_Err>(std::as_const(error()));
}
- return __union_.__val_;
+ return __repr_.__union_.__val_;
}
_LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & {
static_assert(is_copy_constructible_v<_Err>, "error_type has to be copy constructible");
- if (!__has_val_) {
+ if (!__repr_.__has_val_) {
std::__throw_bad_expected_access<_Err>(std::as_const(error()));
}
- return __union_.__val_;
+ return __repr_.__union_.__val_;
}
_LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& value() const&& {
static_assert(is_copy_constructible_v<_Err> && is_constructible_v<_Err, decltype(std::move(error()))>,
"error_type has to be both copy constructible and constructible from decltype(std::move(error()))");
- if (!__has_val_) {
+ if (!__repr_.__has_val_) {
std::__throw_bad_expected_access<_Err>(std::move(error()));
}
- return std::move(__union_.__val_);
+ return std::move(__repr_.__union_.__val_);
}
_LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && {
static_assert(is_copy_constructible_v<_Err> && is_constructible_v<_Err, decltype(std::move(error()))>,
"error_type has to be both copy constructible and constructible from decltype(std::move(error()))");
- if (!__has_val_) {
+ if (!__repr_.__has_val_) {
std::__throw_bad_expected_access<_Err>(std::move(error()));
}
- return std::move(__union_.__val_);
+ return std::move(__repr_.__union_.__val_);
}
_LIBCPP_HIDE_FROM_ABI constexpr const _Err& error() const& noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
- return __union_.__unex_;
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return __repr_.__union_.__unex_;
}
_LIBCPP_HIDE_FROM_ABI constexpr _Err& error() & noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
- return __union_.__unex_;
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return __repr_.__union_.__unex_;
}
_LIBCPP_HIDE_FROM_ABI constexpr const _Err&& error() const&& noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
- return std::move(__union_.__unex_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return std::move(__repr_.__union_.__unex_);
}
_LIBCPP_HIDE_FROM_ABI constexpr _Err&& error() && noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
- return std::move(__union_.__unex_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return std::move(__repr_.__union_.__unex_);
}
template <class _Up>
_LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& {
static_assert(is_copy_constructible_v<_Tp>, "value_type has to be copy constructible");
static_assert(is_convertible_v<_Up, _Tp>, "argument has to be convertible to value_type");
- return __has_val_ ? __union_.__val_ : static_cast<_Tp>(std::forward<_Up>(__v));
+ return __repr_.__has_val_ ? __repr_.__union_.__val_ : static_cast<_Tp>(std::forward<_Up>(__v));
}
template <class _Up>
_LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
static_assert(is_move_constructible_v<_Tp>, "value_type has to be move constructible");
static_assert(is_convertible_v<_Up, _Tp>, "argument has to be convertible to value_type");
- return __has_val_ ? std::move(__union_.__val_) : static_cast<_Tp>(std::forward<_Up>(__v));
+ return __repr_.__has_val_ ? std::move(__repr_.__union_.__val_) : static_cast<_Tp>(std::forward<_Up>(__v));
}
template <class _Up = _Err>
@@ -645,7 +626,7 @@ class expected {
static_assert(is_same_v<typename _Up::error_type, _Err>,
"The result of f(**this) must have the same error_type as this expected");
if (has_value()) {
- return std::invoke(std::forward<_Func>(__f), __union_.__val_);
+ return std::invoke(std::forward<_Func>(__f), __repr_.__union_.__val_);
}
return _Up(unexpect, error());
}
@@ -658,7 +639,7 @@ class expected {
static_assert(is_same_v<typename _Up::error_type, _Err>,
"The result of f(**this) must have the same error_type as this expected");
if (has_value()) {
- return std::invoke(std::forward<_Func>(__f), __union_.__val_);
+ return std::invoke(std::forward<_Func>(__f), __repr_.__union_.__val_);
}
return _Up(unexpect, error());
}
@@ -672,7 +653,7 @@ class expected {
static_assert(is_same_v<typename _Up::error_type, _Err>,
"The result of f(std::move(**this)) must have the same error_type as this expected");
if (has_value()) {
- return std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_));
+ return std::invoke(std::forward<_Func>(__f), std::move(__repr_.__union_.__val_));
}
return _Up(unexpect, std::move(error()));
}
@@ -686,7 +667,7 @@ class expected {
static_assert(is_same_v<typename _Up::error_type, _Err>,
"The result of f(std::move(**this)) must have the same error_type as this expected");
if (has_value()) {
- return std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_));
+ return std::invoke(std::forward<_Func>(__f), std::move(__repr_.__union_.__val_));
}
return _Up(unexpect, std::move(error()));
}
@@ -699,7 +680,7 @@ class expected {
static_assert(is_same_v<typename _Gp::value_type, _Tp>,
"The result of f(error()) must have the same value_type as this expected");
if (has_value()) {
- return _Gp(in_place, __union_.__val_);
+ return _Gp(in_place, __repr_.__union_.__val_);
}
return std::invoke(std::forward<_Func>(__f), error());
}
@@ -712,7 +693,7 @@ class expected {
static_assert(is_same_v<typename _Gp::value_type, _Tp>,
"The result of f(error()) must have the same value_type as this expected");
if (has_value()) {
- return _Gp(in_place, __union_.__val_);
+ return _Gp(in_place, __repr_.__union_.__val_);
}
return std::invoke(std::forward<_Func>(__f), error());
}
@@ -726,7 +707,7 @@ class expected {
static_assert(is_same_v<typename _Gp::value_type, _Tp>,
"The result of f(std::move(error())) must have the same value_type as this expected");
if (has_value()) {
- return _Gp(in_place, std::move(__union_.__val_));
+ return _Gp(in_place, std::move(__repr_.__union_.__val_));
}
return std::invoke(std::forward<_Func>(__f), std::move(error()));
}
@@ -740,7 +721,7 @@ class expected {
static_assert(is_same_v<typename _Gp::value_type, _Tp>,
"The result of f(std::move(error())) must have the same value_type as this expected");
if (has_value()) {
- return _Gp(in_place, std::move(__union_.__val_));
+ return _Gp(in_place, std::move(__repr_.__union_.__val_));
}
return std::invoke(std::forward<_Func>(__f), std::move(error()));
}
@@ -753,9 +734,9 @@ class expected {
return expected<_Up, _Err>(unexpect, error());
}
if constexpr (!is_void_v<_Up>) {
- return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), __union_.__val_);
+ return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), __repr_.__union_.__val_);
} else {
- std::invoke(std::forward<_Func>(__f), __union_.__val_);
+ std::invoke(std::forward<_Func>(__f), __repr_.__union_.__val_);
return expected<_Up, _Err>();
}
}
@@ -768,9 +749,9 @@ class expected {
return expected<_Up, _Err>(unexpect, error());
}
if constexpr (!is_void_v<_Up>) {
- return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), __union_.__val_);
+ return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), __repr_.__union_.__val_);
} else {
- std::invoke(std::forward<_Func>(__f), __union_.__val_);
+ std::invoke(std::forward<_Func>(__f), __repr_.__union_.__val_);
return expected<_Up, _Err>();
}
}
@@ -784,9 +765,9 @@ class expected {
}
if constexpr (!is_void_v<_Up>) {
return expected<_Up, _Err>(
- __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(__union_.__val_));
+ __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(__repr_.__union_.__val_));
} else {
- std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_));
+ std::invoke(std::forward<_Func>(__f), std::move(__repr_.__union_.__val_));
return expected<_Up, _Err>();
}
}
@@ -800,9 +781,9 @@ class expected {
}
if constexpr (!is_void_v<_Up>) {
return expected<_Up, _Err>(
- __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(__union_.__val_));
+ __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(__repr_.__union_.__val_));
} else {
- std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_));
+ std::invoke(std::forward<_Func>(__f), std::move(__repr_.__union_.__val_));
return expected<_Up, _Err>();
}
}
@@ -814,7 +795,7 @@ class expected {
static_assert(__valid_std_unexpected<_Gp>::value,
"The result of f(error()) must be a valid template argument for unexpected");
if (has_value()) {
- return expected<_Tp, _Gp>(in_place, __union_.__val_);
+ return expected<_Tp, _Gp>(in_place, __repr_.__union_.__val_);
}
return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error());
}
@@ -826,7 +807,7 @@ class expected {
static_assert(__valid_std_unexpected<_Gp>::value,
"The result of f(error()) must be a valid template argument for unexpected");
if (has_value()) {
- return expected<_Tp, _Gp>(in_place, __union_.__val_);
+ return expected<_Tp, _Gp>(in_place, __repr_.__union_.__val_);
}
return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error());
}
@@ -838,7 +819,7 @@ class expected {
static_assert(__valid_std_unexpected<_Gp>::value,
"The result of f(std::move(error())) must be a valid template argument for unexpected");
if (has_value()) {
- return expected<_Tp, _Gp>(in_place, std::move(__union_.__val_));
+ return expected<_Tp, _Gp>(in_place, std::move(__repr_.__union_.__val_));
}
return expected<_Tp, _Gp>(
__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error()));
@@ -851,7 +832,7 @@ class expected {
static_assert(__valid_std_unexpected<_Gp>::value,
"The result of f(std::move(error())) must be a valid template argument for unexpected");
if (has_value()) {
- return expected<_Tp, _Gp>(in_place, std::move(__union_.__val_));
+ return expected<_Tp, _Gp>(in_place, std::move(__repr_.__union_.__val_));
}
return expected<_Tp, _Gp>(
__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error()));
@@ -861,25 +842,25 @@ class expected {
template <class _T2, class _E2>
requires(!is_void_v<_T2>)
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) {
- if (__x.__has_val_ != __y.__has_val_) {
+ if (__x.__repr_.__has_val_ != __y.__repr_.__has_val_) {
return false;
} else {
- if (__x.__has_val_) {
- return __x.__union_.__val_ == __y.__union_.__val_;
+ if (__x.__repr_.__has_val_) {
+ return __x.__repr_.__union_.__val_ == __y.__repr_.__union_.__val_;
} else {
- return __x.__union_.__unex_ == __y.__union_.__unex_;
+ return __x.__repr_.__union_.__unex_ == __y.__repr_.__union_.__unex_;
}
}
}
template <class _T2>
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v) {
- return __x.__has_val_ && static_cast<bool>(__x.__union_.__val_ == __v);
+ return __x.__repr_.__has_val_ && static_cast<bool>(__x.__repr_.__union_.__val_ == __v);
}
template <class _E2>
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __e) {
- return !__x.__has_val_ && static_cast<bool>(__x.__union_.__unex_ == __e.error());
+ return !__x.__repr_.__has_val_ && static_cast<bool>(__x.__repr_.__union_.__unex_ == __e.error());
}
private:
@@ -971,9 +952,7 @@ class expected {
_LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
};
- _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
- _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
- _LIBCPP_NO_UNIQUE_ADDRESS __expected_padding_t<__union_t<_Tp, _Err>> __padding_{};
+ __expected_repr<__union_t<_Tp, _Err>> __repr_;
};
template <class _Tp, class _Err>
@@ -1004,7 +983,7 @@ class expected<_Tp, _Err> {
using rebind = expected<_Up, error_type>;
// [expected.void.ctor], constructors
- _LIBCPP_HIDE_FROM_ABI constexpr expected() noexcept : __has_val_(true) {}
+ _LIBCPP_HIDE_FROM_ABI constexpr expected() noexcept : __repr_{.__union_{}, .__has_val_ = true} {}
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
@@ -1015,7 +994,7 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected& __rhs)
noexcept(is_nothrow_copy_constructible_v<_Err>) // strengthened
requires(is_copy_constructible_v<_Err> && !is_trivially_copy_constructible_v<_Err>)
- : __union_(__rhs.__has_val_, __rhs.__union_), __has_val_(__rhs.__has_val_) {}
+ : __repr_{.__union_{__rhs.__repr_.__has_val_, __rhs.__repr_.__union_}, .__has_val_ = __rhs.__repr_.__has_val_} {}
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
@@ -1024,61 +1003,61 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&& __rhs)
noexcept(is_nothrow_move_constructible_v<_Err>)
requires(is_move_constructible_v<_Err> && !is_trivially_move_constructible_v<_Err>)
- : __union_(__rhs.__has_val_, std::move(__rhs.__union_)), __has_val_(__rhs.__has_val_) {}
+ : __repr_{.__union_{__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)}, .__has_val_ = __rhs.__repr_.__has_val_} {}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, const _OtherErr&>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const expected<_Up, _OtherErr>& __rhs)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __union_(__rhs.__has_val_, __rhs.__union_), __has_val_(__rhs.__has_val_) {}
+ : __repr_{.__union_{__rhs.__repr_.__has_val_, __rhs.__repr_.__union_}, .__has_val_ = __rhs.__repr_.__has_val_} {}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, _OtherErr>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(expected<_Up, _OtherErr>&& __rhs)
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __union_(__rhs.__has_val_, std::move(__rhs.__union_)), __has_val_(__rhs.__has_val_) {}
+ : __repr_{.__union_{__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)}, .__has_val_ = __rhs.__repr_.__has_val_} {}
template <class _OtherErr>
requires is_constructible_v<_Err, const _OtherErr&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const unexpected<_OtherErr>& __unex)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __union_(std::unexpect, __unex.error()), __has_val_(false) {}
+ : __repr_{.__union_{std::unexpect, __unex.error()}, .__has_val_ = false} {}
template <class _OtherErr>
requires is_constructible_v<_Err, _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(unexpected<_OtherErr>&& __unex)
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __union_(std::unexpect, std::move(__unex.error())), __has_val_(false) {}
+ : __repr_{.__union_{std::unexpect, std::move(__unex.error())}, .__has_val_ = false} {}
- _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __has_val_(true) {}
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __repr_{.__union_{}, .__has_val_ = true} {}
template <class... _Args>
requires is_constructible_v<_Err, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
- : __union_(std::unexpect, std::forward<_Args>(__args)...), __has_val_(false) {}
+ : __repr_{.__union_{std::unexpect, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
template <class _Up, class... _Args>
requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
- : __union_(std::unexpect, __il, std::forward<_Args>(__args)...), __has_val_(false) {}
+ : __repr_{.__union_{std::unexpect, __il, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
private:
template <class _Func>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(__expected_construct_in_place_from_invoke_tag, _Func&& __f)
- : __has_val_(true) {
+ : __repr_{.__union_{}, .__has_val_ = true} {
std::invoke(std::forward<_Func>(__f));
}
template <class _Func, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
__expected_construct_unexpected_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)
- : __union_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...), __has_val_(false) {}
+ : __repr_{.__union_{__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...}, .__has_val_ = false} {}
public:
// [expected.void.dtor], destructor
@@ -1090,8 +1069,8 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr ~expected()
requires(!is_trivially_destructible_v<_Err>)
{
- if (!__has_val_) {
- std::destroy_at(std::addressof(__union_.__unex_));
+ if (!__repr_.__has_val_) {
+ std::destroy_at(std::addressof(__repr_.__union_.__unex_));
}
}
@@ -1103,17 +1082,17 @@ class expected<_Tp, _Err> {
noexcept(is_nothrow_copy_assignable_v<_Err> && is_nothrow_copy_constructible_v<_Err>) // strengthened
requires(is_copy_assignable_v<_Err> && is_copy_constructible_v<_Err>)
{
- if (__has_val_) {
- if (!__rhs.__has_val_) {
- std::construct_at(std::addressof(__union_.__unex_), __rhs.__union_.__unex_);
- __has_val_ = false;
+ if (__repr_.__has_val_) {
+ if (!__rhs.__repr_.__has_val_) {
+ std::construct_at(std::addressof(__repr_.__union_.__unex_), __rhs.__repr_.__union_.__unex_);
+ __repr_.__has_val_ = false;
}
} else {
- if (__rhs.__has_val_) {
- std::destroy_at(std::addressof(__union_.__unex_));
- __has_val_ = true;
+ if (__rhs.__repr_.__has_val_) {
+ std::destroy_at(std::addressof(__repr_.__union_.__unex_));
+ __repr_.__has_val_ = true;
} else {
- __union_.__unex_ = __rhs.__union_.__unex_;
+ __repr_.__union_.__unex_ = __rhs.__repr_.__union_.__unex_;
}
}
return *this;
@@ -1127,17 +1106,17 @@ class expected<_Tp, _Err> {
requires(is_move_assignable_v<_Err> &&
is_move_constructible_v<_Err>)
{
- if (__has_val_) {
- if (!__rhs.__has_val_) {
- std::construct_at(std::addressof(__union_.__unex_), std::move(__rhs.__union_.__unex_));
- __has_val_ = false;
+ if (__repr_.__has_val_) {
+ if (!__rhs.__repr_.__has_val_) {
+ std::construct_at(std::addressof(__repr_.__union_.__unex_), std::move(__rhs.__repr_.__union_.__unex_));
+ __repr_.__has_val_ = false;
}
} else {
- if (__rhs.__has_val_) {
- std::destroy_at(std::addressof(__union_.__unex_));
- __has_val_ = true;
+ if (__rhs.__repr_.__has_val_) {
+ std::destroy_at(std::addressof(__repr_.__union_.__unex_));
+ __repr_.__has_val_ = true;
} else {
- __union_.__unex_ = std::move(__rhs.__union_.__unex_);
+ __repr_.__union_.__unex_ = std::move(__rhs.__repr_.__union_.__unex_);
}
}
return *this;
@@ -1146,11 +1125,11 @@ class expected<_Tp, _Err> {
template <class _OtherErr>
requires(is_constructible_v<_Err, const _OtherErr&> && is_assignable_v<_Err&, const _OtherErr&>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const unexpected<_OtherErr>& __un) {
- if (__has_val_) {
- std::construct_at(std::addressof(__union_.__unex_), __un.error());
- __has_val_ = false;
+ if (__repr_.__has_val_) {
+ std::construct_at(std::addressof(__repr_.__union_.__unex_), __un.error());
+ __repr_.__has_val_ = false;
} else {
- __union_.__unex_ = __un.error();
+ __repr_.__union_.__unex_ = __un.error();
}
return *this;
}
@@ -1158,19 +1137,19 @@ class expected<_Tp, _Err> {
template <class _OtherErr>
requires(is_constructible_v<_Err, _OtherErr> && is_assignable_v<_Err&, _OtherErr>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(unexpected<_OtherErr>&& __un) {
- if (__has_val_) {
- std::construct_at(std::addressof(__union_.__unex_), std::move(__un.error()));
- __has_val_ = false;
+ if (__repr_.__has_val_) {
+ std::construct_at(std::addressof(__repr_.__union_.__unex_), std::move(__un.error()));
+ __repr_.__has_val_ = false;
} else {
- __union_.__unex_ = std::move(__un.error());
+ __repr_.__union_.__unex_ = std::move(__un.error());
}
return *this;
}
_LIBCPP_HIDE_FROM_ABI constexpr void emplace() noexcept {
- if (!__has_val_) {
- std::destroy_at(std::addressof(__union_.__unex_));
- __has_val_ = true;
+ if (!__repr_.__has_val_) {
+ std::destroy_at(std::addressof(__repr_.__union_.__unex_));
+ __repr_.__has_val_ = true;
}
}
@@ -1180,22 +1159,22 @@ class expected<_Tp, _Err> {
requires(is_swappable_v<_Err> && is_move_constructible_v<_Err>)
{
auto __swap_val_unex_impl = [&](expected& __with_val, expected& __with_err) {
- std::construct_at(std::addressof(__with_val.__union_.__unex_), std::move(__with_err.__union_.__unex_));
- std::destroy_at(std::addressof(__with_err.__union_.__unex_));
- __with_val.__has_val_ = false;
- __with_err.__has_val_ = true;
+ std::construct_at(std::addressof(__with_val.__repr_.__union_.__unex_), std::move(__with_err.__repr_.__union_.__unex_));
+ std::destroy_at(std::addressof(__with_err.__repr_.__union_.__unex_));
+ __with_val.__repr_.__has_val_ = false;
+ __with_err.__repr_.__has_val_ = true;
};
- if (__has_val_) {
- if (!__rhs.__has_val_) {
+ if (__repr_.__has_val_) {
+ if (!__rhs.__repr_.__has_val_) {
__swap_val_unex_impl(*this, __rhs);
}
} else {
- if (__rhs.__has_val_) {
+ if (__rhs.__repr_.__has_val_) {
__swap_val_unex_impl(__rhs, *this);
} else {
using std::swap;
- swap(__union_.__unex_, __rhs.__union_.__unex_);
+ swap(__repr_.__union_.__unex_, __rhs.__repr_.__union_.__unex_);
}
}
}
@@ -1208,44 +1187,44 @@ class expected<_Tp, _Err> {
}
// [expected.void.obs], observers
- _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return __has_val_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return __repr_.__has_val_; }
- _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __has_val_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __repr_.__has_val_; }
_LIBCPP_HIDE_FROM_ABI constexpr void operator*() const noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__has_val_, "expected::operator* requires the expected to contain a value");
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
}
_LIBCPP_HIDE_FROM_ABI constexpr void value() const& {
- if (!__has_val_) {
- std::__throw_bad_expected_access<_Err>(__union_.__unex_);
+ if (!__repr_.__has_val_) {
+ std::__throw_bad_expected_access<_Err>(__repr_.__union_.__unex_);
}
}
_LIBCPP_HIDE_FROM_ABI constexpr void value() && {
- if (!__has_val_) {
- std::__throw_bad_expected_access<_Err>(std::move(__union_.__unex_));
+ if (!__repr_.__has_val_) {
+ std::__throw_bad_expected_access<_Err>(std::move(__repr_.__union_.__unex_));
}
}
_LIBCPP_HIDE_FROM_ABI constexpr const _Err& error() const& noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
- return __union_.__unex_;
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return __repr_.__union_.__unex_;
}
_LIBCPP_HIDE_FROM_ABI constexpr _Err& error() & noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
- return __union_.__unex_;
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return __repr_.__union_.__unex_;
}
_LIBCPP_HIDE_FROM_ABI constexpr const _Err&& error() const&& noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
- return std::move(__union_.__unex_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return std::move(__repr_.__union_.__unex_);
}
_LIBCPP_HIDE_FROM_ABI constexpr _Err&& error() && noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__has_val_, "expected::error requires the expected to contain an error");
- return std::move(__union_.__unex_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return std::move(__repr_.__union_.__unex_);
}
template <class _Up = _Err>
@@ -1481,16 +1460,16 @@ class expected<_Tp, _Err> {
template <class _T2, class _E2>
requires is_void_v<_T2>
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) {
- if (__x.__has_val_ != __y.__has_val_) {
+ if (__x.__repr_.__has_val_ != __y.__repr_.__has_val_) {
return false;
} else {
- return __x.__has_val_ || static_cast<bool>(__x.__union_.__unex_ == __y.__union_.__unex_);
+ return __x.__repr_.__has_val_ || static_cast<bool>(__x.__repr_.__union_.__unex_ == __y.__repr_.__union_.__unex_);
}
}
template <class _E2>
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y) {
- return !__x.__has_val_ && static_cast<bool>(__x.__union_.__unex_ == __y.error());
+ return !__x.__repr_.__has_val_ && static_cast<bool>(__x.__repr_.__union_.__unex_ == __y.error());
}
private:
@@ -1569,9 +1548,7 @@ class expected<_Tp, _Err> {
_LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
};
- _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
- _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
- _LIBCPP_NO_UNIQUE_ADDRESS __expected_padding_t<__union_t<_Err>> __padding_{};
+ __expected_repr<__union_t<_Err>> __repr_;
};
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
index 06f597d54871ee1..80209db9045b506 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
@@ -13,6 +13,7 @@
// test [[no_unique_address]] is applied to the union
#include <expected>
+#include <memory>
struct Empty {};
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 29cf8dff0d2b288..db2e4259eb8c797 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
@@ -13,6 +13,7 @@
// test [[no_unique_address]] is applied to the union
#include <expected>
+#include <memory>
struct Empty {};
>From 8db00d2567c6656b44ec155ac9eff2b34410721f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 10:52:00 +0200
Subject: [PATCH 27/38] transparently replace whole 'repr' struct on mutating
operations
---
libcxx/include/__expected/expected.h | 208 ++++++++++--------
.../no_unique_address.compile.pass.cpp | 3 +-
.../no_unique_address.compile.pass.cpp | 3 +-
3 files changed, 125 insertions(+), 89 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 9eaa20c30937d33..dff31ef3fccc1d8 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -89,12 +89,6 @@ _LIBCPP_HIDE_FROM_ABI void __throw_bad_expected_access(_Arg&& __arg) {
# endif
}
-template <class _Union>
-struct __expected_repr {
- [[no_unique_address]] _Union __union_;
- [[no_unique_address]] bool __has_val_;
-};
-
template <class _Tp, class _Err>
class expected {
static_assert(
@@ -126,7 +120,7 @@ class expected {
_LIBCPP_HIDE_FROM_ABI constexpr expected()
noexcept(is_nothrow_default_constructible_v<_Tp>) // strengthened
requires is_default_constructible_v<_Tp>
- : __repr_{.__union_{std::in_place}, .__has_val_ = true} {}
+ : __repr_(std::in_place) {}
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
@@ -141,7 +135,7 @@ class expected {
noexcept(is_nothrow_copy_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Err>) // strengthened
requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err> &&
!(is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Err>))
- : __repr_{.__union_{__other.__repr_.__has_val_, __other.__repr_.__union_}, .__has_val_ = __other.__repr_.__has_val_} { }
+ : __repr_(__other.__repr_.__has_val_, __other.__repr_.__union_) { }
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err>
@@ -152,8 +146,7 @@ class expected {
noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_move_constructible_v<_Err>)
requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err> &&
!(is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>))
- : __repr_{.__union_{__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)},
- .__has_val_ = __other.__repr_.__has_val_} { }
+ : __repr_(__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)) { }
private:
template <class _Up, class _OtherErr, class _UfQual, class _OtherErrQual>
@@ -178,12 +171,12 @@ class expected {
template <class _Func, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
std::__expected_construct_in_place_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)
- : __repr_{.__union_{__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...}, .__has_val_ = true} {}
+ : __repr_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...) {}
template <class _Func, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
std::__expected_construct_unexpected_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)
- : __repr_{.__union_{__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...}, .__has_val_ = false} {}
+ : __repr_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...) {}
public:
template <class _Up, class _OtherErr>
@@ -193,16 +186,14 @@ class expected {
expected(const expected<_Up, _OtherErr>& __other)
noexcept(is_nothrow_constructible_v<_Tp, const _Up&> &&
is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __repr_{.__union_{__other.__repr_.__has_val_, __other.__repr_.__union_},
- .__has_val_ = __other.__repr_.__has_val_} {}
+ : __repr_(__other.__repr_.__has_val_, __other.__repr_.__union_) {}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, _Up, _OtherErr>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp> || !is_convertible_v<_OtherErr, _Err>)
expected(expected<_Up, _OtherErr>&& __other)
noexcept(is_nothrow_constructible_v<_Tp, _Up> && is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __repr_{.__union_{__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)},
- .__has_val_ = __other.__repr_.__has_val_} {}
+ : __repr_(__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)) {}
template <class _Up = _Tp>
requires(!is_same_v<remove_cvref_t<_Up>, in_place_t> && !is_same_v<expected, remove_cvref_t<_Up>> &&
@@ -210,84 +201,72 @@ class expected {
(!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_expected<remove_cvref_t<_Up>>::value))
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp>)
expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
- : __repr_{.__union_{std::in_place, std::forward<_Up>(__u)}, .__has_val_ = true} {}
+ : __repr_(std::in_place, std::forward<_Up>(__u)) {}
template <class _OtherErr>
requires is_constructible_v<_Err, const _OtherErr&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const unexpected<_OtherErr>& __unex)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __repr_{.__union_{std::unexpect, __unex.error()}, .__has_val_ = false} {}
+ : __repr_(std::unexpect, __unex.error()) {}
template <class _OtherErr>
requires is_constructible_v<_Err, _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(unexpected<_OtherErr>&& __unex)
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __repr_{.__union_{std::unexpect, std::move(__unex.error())}, .__has_val_ = false} {}
+ : __repr_(std::unexpect, std::move(__unex.error())) {}
template <class... _Args>
requires is_constructible_v<_Tp, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Tp, _Args...>) // strengthened
- : __repr_{.__union_{std::in_place, std::forward<_Args>(__args)...}, .__has_val_ = true} {}
+ : __repr_(std::in_place, std::forward<_Args>(__args)...) {}
template <class _Up, class... _Args>
requires is_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit
expected(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, _Args...>) // strengthened
- : __repr_{.__union_{std::in_place, __il, std::forward<_Args>(__args)...}, .__has_val_ = true} {}
+ : __repr_(std::in_place, __il, std::forward<_Args>(__args)...) {}
template <class... _Args>
requires is_constructible_v<_Err, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
- : __repr_{.__union_{std::unexpect, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
+ : __repr_(std::unexpect, std::forward<_Args>(__args)...) {}
template <class _Up, class... _Args>
requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit
expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
- : __repr_{.__union_{std::unexpect, __il, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
+ : __repr_(std::unexpect, __il, std::forward<_Args>(__args)...) {}
// [expected.object.dtor], destructor
- _LIBCPP_HIDE_FROM_ABI constexpr ~expected()
- requires(is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Err>)
- = default;
-
- _LIBCPP_HIDE_FROM_ABI constexpr ~expected()
- requires(!is_trivially_destructible_v<_Tp> || !is_trivially_destructible_v<_Err>)
- {
- if (__repr_.__has_val_) {
- std::destroy_at(std::addressof(__repr_.__union_.__val_));
- } else {
- std::destroy_at(std::addressof(__repr_.__union_.__unex_));
- }
- }
+ _LIBCPP_HIDE_FROM_ABI constexpr ~expected() = default;
private:
- template <class _T1, class _T2, class... _Args>
- _LIBCPP_HIDE_FROM_ABI static constexpr void __reinit_expected(_T1& __newval, _T2& __oldval, _Args&&... __args) {
+ template <class _Tag, class _OtherTag, class _T1, class _T2, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr void __reinit_expected(_T2& __oldval, _Args&&... __args) {
if constexpr (is_nothrow_constructible_v<_T1, _Args...>) {
- std::destroy_at(std::addressof(__oldval));
- std::construct_at(std::addressof(__newval), std::forward<_Args>(__args)...);
+ std::destroy_at(&__repr_);
+ std::construct_at(&__repr_, _Tag{}, std::forward<_Args>(__args)...);
} else if constexpr (is_nothrow_move_constructible_v<_T1>) {
_T1 __tmp(std::forward<_Args>(__args)...);
- std::destroy_at(std::addressof(__oldval));
- std::construct_at(std::addressof(__newval), std::move(__tmp));
+ std::destroy_at(&__repr_);
+ std::construct_at(&__repr_, _Tag{}, std::move(__tmp));
} else {
static_assert(
is_nothrow_move_constructible_v<_T2>,
"To provide strong exception guarantee, T2 has to satisfy `is_nothrow_move_constructible_v` so that it can "
"be reverted to the previous state in case an exception is thrown during the assignment.");
_T2 __tmp(std::move(__oldval));
- std::destroy_at(std::addressof(__oldval));
+ std::destroy_at(&__repr_);
auto __trans =
- std::__make_exception_guard([&] { std::construct_at(std::addressof(__oldval), std::move(__tmp)); });
- std::construct_at(std::addressof(__newval), std::forward<_Args>(__args)...);
+ std::__make_exception_guard([&] { std::construct_at(&__repr_, _OtherTag{}, std::move(__tmp)); });
+ std::construct_at(&__repr_, _Tag{}, std::forward<_Args>(__args)...);
__trans.__complete();
}
}
@@ -311,14 +290,14 @@ class expected {
if (__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
__repr_.__union_.__val_ = __rhs.__repr_.__union_.__val_;
} else if (__repr_.__has_val_) {
- __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, __rhs.__repr_.__union_.__unex_);
+ __reinit_expected<std::unexpect_t, std::in_place_t, _Err, _Tp>(
+ __repr_.__union_.__val_, __rhs.__repr_.__union_.__unex_);
} else if (__rhs.__repr_.__has_val_) {
- __reinit_expected(__repr_.__union_.__val_, __repr_.__union_.__unex_, __rhs.__repr_.__union_.__val_);
+ __reinit_expected<std::in_place_t, std::unexpect_t, _Tp, _Err>(
+ __repr_.__union_.__unex_, __rhs.__repr_.__union_.__val_);
} else {
__repr_.__union_.__unex_ = __rhs.__repr_.__union_.__unex_;
}
- // note: only reached if no exception+rollback was done inside __reinit_expected
- __repr_.__has_val_ = __rhs.__repr_.__has_val_;
return *this;
}
@@ -337,14 +316,14 @@ class expected {
if (__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
__repr_.__union_.__val_ = std::move(__rhs.__repr_.__union_.__val_);
} else if (__repr_.__has_val_) {
- __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, std::move(__rhs.__repr_.__union_.__unex_));
+ __reinit_expected<std::unexpect_t, std::in_place_t, _Err, _Tp>(
+ __repr_.__union_.__val_, std::move(__rhs.__repr_.__union_.__unex_));
} else if (__rhs.__repr_.__has_val_) {
- __reinit_expected(__repr_.__union_.__val_, __repr_.__union_.__unex_, std::move(__rhs.__repr_.__union_.__val_));
+ __reinit_expected<std::in_place_t, std::unexpect_t, _Tp, _Err>(
+ __repr_.__union_.__unex_, std::move(__rhs.__repr_.__union_.__val_));
} else {
__repr_.__union_.__unex_ = std::move(__rhs.__repr_.__union_.__unex_);
}
- // note: only reached if no exception+rollback was done inside __reinit_expected
- __repr_.__has_val_ = __rhs.__repr_.__has_val_;
return *this;
}
@@ -361,8 +340,8 @@ class expected {
if (__repr_.__has_val_) {
__repr_.__union_.__val_ = std::forward<_Up>(__v);
} else {
- __reinit_expected(__repr_.__union_.__val_, __repr_.__union_.__unex_, std::forward<_Up>(__v));
- __repr_.__has_val_ = true;
+ __reinit_expected<std::in_place_t, std::unexpect_t, _Tp, _Err>(
+ __repr_.__union_.__unex_, std::forward<_Up>(__v));
}
return *this;
}
@@ -382,8 +361,8 @@ class expected {
requires(__can_assign_from_unexpected<const _OtherErr&>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const unexpected<_OtherErr>& __un) {
if (__repr_.__has_val_) {
- __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, __un.error());
- __repr_.__has_val_ = false;
+ __reinit_expected<std::unexpect_t, std::in_place_t, _Err, _Tp>(
+ __repr_.__union_.__val_, __un.error());
} else {
__repr_.__union_.__unex_ = __un.error();
}
@@ -394,8 +373,8 @@ class expected {
requires(__can_assign_from_unexpected<_OtherErr>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(unexpected<_OtherErr>&& __un) {
if (__repr_.__has_val_) {
- __reinit_expected(__repr_.__union_.__unex_, __repr_.__union_.__val_, std::move(__un.error()));
- __repr_.__has_val_ = false;
+ __reinit_expected<std::unexpect_t, std::in_place_t, _Err, _Tp>(
+ __repr_.__union_.__val_, std::move(__un.error()));
} else {
__repr_.__union_.__unex_ = std::move(__un.error());
}
@@ -405,26 +384,16 @@ class expected {
template <class... _Args>
requires is_nothrow_constructible_v<_Tp, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr _Tp& emplace(_Args&&... __args) noexcept {
- if (__repr_.__has_val_) {
- std::destroy_at(std::addressof(__repr_.__union_.__val_));
- } else {
- std::destroy_at(std::addressof(__repr_.__union_.__unex_));
- }
- std::construct_at(std::addressof(__repr_.__union_.__val_), std::forward<_Args>(__args)...);
- __repr_.__has_val_ = true;
+ std::destroy_at(&__repr_);
+ std::construct_at(&__repr_, std::in_place, std::forward<_Args>(__args)...);
return *std::addressof(__repr_.__union_.__val_);
}
template <class _Up, class... _Args>
requires is_nothrow_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) noexcept {
- if (__repr_.__has_val_) {
- std::destroy_at(std::addressof(__repr_.__union_.__val_));
- } else {
- std::destroy_at(std::addressof(__repr_.__union_.__unex_));
- }
- std::construct_at(std::addressof(__repr_.__union_.__val_), __il, std::forward<_Args>(__args)...);
- __repr_.__has_val_ = true;
+ std::destroy_at(&__repr_);
+ std::construct_at(&__repr_, std::in_place, __il, std::forward<_Args>(__args)...);
return *std::addressof(__repr_.__union_.__val_);
}
@@ -443,33 +412,31 @@ class expected {
(is_nothrow_move_constructible_v<_Tp> ||
is_nothrow_move_constructible_v<_Err>))
{
- auto __swap_val_unex_impl = [&](expected& __with_val, expected& __with_err) {
+ auto __swap_val_unex_impl = [](expected& __with_val, expected& __with_err) {
if constexpr (is_nothrow_move_constructible_v<_Err>) {
_Err __tmp(std::move(__with_err.__repr_.__union_.__unex_));
- std::destroy_at(std::addressof(__with_err.__repr_.__union_.__unex_));
+ std::destroy_at(&__with_err.__repr_);
auto __trans = std::__make_exception_guard([&] {
- std::construct_at(std::addressof(__with_err.__repr_.__union_.__unex_), std::move(__tmp));
+ std::construct_at(&__with_err.__repr_, std::unexpect, std::move(__tmp));
});
- std::construct_at(std::addressof(__with_err.__repr_.__union_.__val_), std::move(__with_val.__repr_.__union_.__val_));
+ std::construct_at(&__with_err.__repr_, std::in_place, std::move(__with_val.__repr_.__union_.__val_));
__trans.__complete();
- std::destroy_at(std::addressof(__with_val.__repr_.__union_.__val_));
- std::construct_at(std::addressof(__with_val.__repr_.__union_.__unex_), std::move(__tmp));
+ std::destroy_at(&__with_val.__repr_);
+ std::construct_at(&__with_val.__repr_, std::unexpect, std::move(__tmp));
} else {
static_assert(is_nothrow_move_constructible_v<_Tp>,
"To provide strong exception guarantee, Tp has to satisfy `is_nothrow_move_constructible_v` so "
"that it can be reverted to the previous state in case an exception is thrown during swap.");
_Tp __tmp(std::move(__with_val.__repr_.__union_.__val_));
- std::destroy_at(std::addressof(__with_val.__repr_.__union_.__val_));
+ std::destroy_at(&__with_val.__repr_);
auto __trans = std::__make_exception_guard([&] {
- std::construct_at(std::addressof(__with_val.__repr_.__union_.__val_), std::move(__tmp));
+ std::construct_at(&__with_val.__repr_, std::in_place, std::move(__tmp));
});
- std::construct_at(std::addressof(__with_val.__repr_.__union_.__unex_), std::move(__with_err.__repr_.__union_.__unex_));
+ std::construct_at(&__with_val.__repr_, std::unexpect, std::move(__with_err.__repr_.__union_.__unex_));
__trans.__complete();
- std::destroy_at(std::addressof(__with_err.__repr_.__union_.__unex_));
- std::construct_at(std::addressof(__with_err.__repr_.__union_.__val_), std::move(__tmp));
+ std::destroy_at(&__with_err.__repr_);
+ std::construct_at(&__with_err.__repr_, std::in_place, std::move(__tmp));
}
- __with_val.__repr_.__has_val_ = false;
- __with_err.__repr_.__has_val_ = true;
};
if (__repr_.__has_val_) {
@@ -952,7 +919,74 @@ class expected {
_LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
};
- __expected_repr<__union_t<_Tp, _Err>> __repr_;
+ struct __expected_repr {
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr() = delete;
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(std::in_place_t __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(true) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(std::unexpect_t __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(
+ std::__expected_construct_in_place_from_invoke_tag __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(true) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(
+ std::__expected_construct_unexpected_from_invoke_tag __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(bool __has_val, _Args&&... __args)
+ : __union_(__has_val, std::forward<_Args>(__args)...), __has_val_(__has_val) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&) = delete;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&)
+ requires(is_copy_constructible_v<_Tp> &&
+ is_copy_constructible_v<_Err> &&
+ is_trivially_copy_constructible_v<_Tp> &&
+ is_trivially_copy_constructible_v<_Err>)
+ = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(__expected_repr&&) = delete;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(__expected_repr&&)
+ requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err>
+ && is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>)
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr& operator=(const __expected_repr&) = delete;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+ requires(is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Err>)
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+ requires(!is_trivially_destructible_v<_Tp> || !is_trivially_destructible_v<_Err>)
+ {
+ if (__has_val_) {
+ std::destroy_at(std::addressof(__union_.__val_));
+ } else {
+ std::destroy_at(std::addressof(__union_.__unex_));
+ }
+ }
+
+ private:
+ template <class _Up, class _OtherErr> friend class expected;
+
+ [[no_unique_address]] __union_t<_Tp, _Err> __union_;
+ [[no_unique_address]] bool __has_val_;
+ };
+
+ __expected_repr __repr_;
+};
+
+template <class _Union>
+struct __expected_repr {
+ [[no_unique_address]] _Union __union_;
+ [[no_unique_address]] bool __has_val_;
};
template <class _Tp, class _Err>
diff --git a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
index 80209db9045b506..8eeec70ff0e4243 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
@@ -50,4 +50,5 @@ static_assert(sizeof(std::expected<Empty, BoolWithPadding>) ==
// In this case, there should be tail padding in the `expected` because `A`
// itself does _not_ have tail padding.
-static_assert(sizeof(std::expected<A, A>) > std::__libcpp_datasizeof<std::expected<A, A>>::value);
+// XXX
+// static_assert(sizeof(std::expected<A, A>) > std::__libcpp_datasizeof<std::expected<A, A>>::value);
diff --git a/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
index db2e4259eb8c797..2129aa7b2405c7e 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
@@ -44,4 +44,5 @@ static_assert(sizeof(std::expected<void, BoolWithPadding>) ==
// In this case, there should be tail padding in the `expected` because `A`
// itself does _not_ have tail padding.
-static_assert(sizeof(std::expected<void, A>) > std::__libcpp_datasizeof<std::expected<void, A>>::value);
+// XXX
+// static_assert(sizeof(std::expected<void, A>) > std::__libcpp_datasizeof<std::expected<void, A>>::value);
>From 750afaf98e6a681e60c7db121efd9a0376fe7089 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 11:39:31 +0200
Subject: [PATCH 28/38] do the same for 'expected<void>'
---
libcxx/include/__expected/expected.h | 150 +++++++++++++++++----------
1 file changed, 97 insertions(+), 53 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index dff31ef3fccc1d8..5583eb85f4b437b 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -863,7 +863,7 @@ class expected {
requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>)
= default;
- // the expected's destructor handles this
+ // the __expected_repr's destructor handles this
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() {}
_ValueType __val_;
@@ -910,7 +910,7 @@ class expected {
requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>)
= default;
- // the expected's destructor handles this
+ // the __expected_repr's destructor handles this
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
requires(!is_trivially_destructible_v<_ValueType> || !is_trivially_destructible_v<_ErrorType>)
{}
@@ -974,7 +974,8 @@ class expected {
}
private:
- template <class _Up, class _OtherErr> friend class expected;
+ template <class, class>
+ friend class expected;
[[no_unique_address]] __union_t<_Tp, _Err> __union_;
[[no_unique_address]] bool __has_val_;
@@ -983,12 +984,6 @@ class expected {
__expected_repr __repr_;
};
-template <class _Union>
-struct __expected_repr {
- [[no_unique_address]] _Union __union_;
- [[no_unique_address]] bool __has_val_;
-};
-
template <class _Tp, class _Err>
requires is_void_v<_Tp>
class expected<_Tp, _Err> {
@@ -1017,7 +1012,7 @@ class expected<_Tp, _Err> {
using rebind = expected<_Up, error_type>;
// [expected.void.ctor], constructors
- _LIBCPP_HIDE_FROM_ABI constexpr expected() noexcept : __repr_{.__union_{}, .__has_val_ = true} {}
+ _LIBCPP_HIDE_FROM_ABI constexpr expected() noexcept : __repr_(std::in_place) {}
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
@@ -1028,7 +1023,7 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected& __rhs)
noexcept(is_nothrow_copy_constructible_v<_Err>) // strengthened
requires(is_copy_constructible_v<_Err> && !is_trivially_copy_constructible_v<_Err>)
- : __repr_{.__union_{__rhs.__repr_.__has_val_, __rhs.__repr_.__union_}, .__has_val_ = __rhs.__repr_.__has_val_} {}
+ : __repr_(__rhs.__repr_.__has_val_, __rhs.__repr_.__union_) {}
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
@@ -1037,79 +1032,86 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&& __rhs)
noexcept(is_nothrow_move_constructible_v<_Err>)
requires(is_move_constructible_v<_Err> && !is_trivially_move_constructible_v<_Err>)
- : __repr_{.__union_{__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)}, .__has_val_ = __rhs.__repr_.__has_val_} {}
+ : __repr_(__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)) {}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, const _OtherErr&>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const expected<_Up, _OtherErr>& __rhs)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __repr_{.__union_{__rhs.__repr_.__has_val_, __rhs.__repr_.__union_}, .__has_val_ = __rhs.__repr_.__has_val_} {}
+ : __repr_(__rhs.__repr_.__has_val_, __rhs.__repr_.__union_) {}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, _OtherErr>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(expected<_Up, _OtherErr>&& __rhs)
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __repr_{.__union_{__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)}, .__has_val_ = __rhs.__repr_.__has_val_} {}
+ : __repr_(__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)) {}
template <class _OtherErr>
requires is_constructible_v<_Err, const _OtherErr&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const unexpected<_OtherErr>& __unex)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __repr_{.__union_{std::unexpect, __unex.error()}, .__has_val_ = false} {}
+ : __repr_(std::unexpect, __unex.error()) {}
template <class _OtherErr>
requires is_constructible_v<_Err, _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(unexpected<_OtherErr>&& __unex)
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __repr_{.__union_{std::unexpect, std::move(__unex.error())}, .__has_val_ = false} {}
+ : __repr_(std::unexpect, std::move(__unex.error())) {}
- _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __repr_{.__union_{}, .__has_val_ = true} {}
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __repr_(std::in_place) {}
template <class... _Args>
requires is_constructible_v<_Err, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
- : __repr_{.__union_{std::unexpect, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
+ : __repr_(std::unexpect, std::forward<_Args>(__args)...) {}
template <class _Up, class... _Args>
requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
- : __repr_{.__union_{std::unexpect, __il, std::forward<_Args>(__args)...}, .__has_val_ = false} {}
+ : __repr_(std::unexpect, __il, std::forward<_Args>(__args)...) {}
private:
template <class _Func>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(__expected_construct_in_place_from_invoke_tag, _Func&& __f)
- : __repr_{.__union_{}, .__has_val_ = true} {
+ : __repr_(std::in_place) {
std::invoke(std::forward<_Func>(__f));
}
template <class _Func, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
__expected_construct_unexpected_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)
- : __repr_{.__union_{__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...}, .__has_val_ = false} {}
+ : __repr_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...) {}
public:
// [expected.void.dtor], destructor
- _LIBCPP_HIDE_FROM_ABI constexpr ~expected()
- requires is_trivially_destructible_v<_Err>
- = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr ~expected() = default;
- _LIBCPP_HIDE_FROM_ABI constexpr ~expected()
- requires(!is_trivially_destructible_v<_Err>)
- {
- if (!__repr_.__has_val_) {
- std::destroy_at(std::addressof(__repr_.__union_.__unex_));
- }
+private:
+ // precondition: has_value()
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr void __reinit_expected(std::unexpect_t, _Args&&... __args) {
+ std::destroy_at(&__repr_);
+ auto __trans =
+ std::__make_exception_guard([&] { std::construct_at(&__repr_, std::in_place); });
+ std::construct_at(&__repr_, std::unexpect, std::forward<_Args>(__args)...);
+ __trans.__complete();
}
- // [expected.void.assign], assignment
+ // precondition: !has_value()
+ _LIBCPP_HIDE_FROM_ABI constexpr void __reinit_expected(std::in_place_t) {
+ std::destroy_at(&__repr_);
+ std::construct_at(&__repr_, std::in_place);
+ }
+public:
+ // [expected.void.assign], assignment
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const expected&) = delete;
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const expected& __rhs)
@@ -1118,13 +1120,11 @@ class expected<_Tp, _Err> {
{
if (__repr_.__has_val_) {
if (!__rhs.__repr_.__has_val_) {
- std::construct_at(std::addressof(__repr_.__union_.__unex_), __rhs.__repr_.__union_.__unex_);
- __repr_.__has_val_ = false;
+ __reinit_expected(std::unexpect, __rhs.__repr_.__union_.__unex_);
}
} else {
if (__rhs.__repr_.__has_val_) {
- std::destroy_at(std::addressof(__repr_.__union_.__unex_));
- __repr_.__has_val_ = true;
+ __reinit_expected(std::in_place);
} else {
__repr_.__union_.__unex_ = __rhs.__repr_.__union_.__unex_;
}
@@ -1142,13 +1142,11 @@ class expected<_Tp, _Err> {
{
if (__repr_.__has_val_) {
if (!__rhs.__repr_.__has_val_) {
- std::construct_at(std::addressof(__repr_.__union_.__unex_), std::move(__rhs.__repr_.__union_.__unex_));
- __repr_.__has_val_ = false;
+ __reinit_expected(std::unexpect, std::move(__rhs.__repr_.__union_.__unex_));
}
} else {
if (__rhs.__repr_.__has_val_) {
- std::destroy_at(std::addressof(__repr_.__union_.__unex_));
- __repr_.__has_val_ = true;
+ __reinit_expected(std::in_place);
} else {
__repr_.__union_.__unex_ = std::move(__rhs.__repr_.__union_.__unex_);
}
@@ -1160,8 +1158,7 @@ class expected<_Tp, _Err> {
requires(is_constructible_v<_Err, const _OtherErr&> && is_assignable_v<_Err&, const _OtherErr&>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const unexpected<_OtherErr>& __un) {
if (__repr_.__has_val_) {
- std::construct_at(std::addressof(__repr_.__union_.__unex_), __un.error());
- __repr_.__has_val_ = false;
+ __reinit_expected(std::unexpect, __un.error());
} else {
__repr_.__union_.__unex_ = __un.error();
}
@@ -1172,8 +1169,7 @@ class expected<_Tp, _Err> {
requires(is_constructible_v<_Err, _OtherErr> && is_assignable_v<_Err&, _OtherErr>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(unexpected<_OtherErr>&& __un) {
if (__repr_.__has_val_) {
- std::construct_at(std::addressof(__repr_.__union_.__unex_), std::move(__un.error()));
- __repr_.__has_val_ = false;
+ __reinit_expected(std::unexpect, std::move(__un.error()));
} else {
__repr_.__union_.__unex_ = std::move(__un.error());
}
@@ -1182,8 +1178,7 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr void emplace() noexcept {
if (!__repr_.__has_val_) {
- std::destroy_at(std::addressof(__repr_.__union_.__unex_));
- __repr_.__has_val_ = true;
+ __reinit_expected(std::in_place);
}
}
@@ -1192,11 +1187,11 @@ class expected<_Tp, _Err> {
noexcept(is_nothrow_move_constructible_v<_Err> && is_nothrow_swappable_v<_Err>)
requires(is_swappable_v<_Err> && is_move_constructible_v<_Err>)
{
- auto __swap_val_unex_impl = [&](expected& __with_val, expected& __with_err) {
- std::construct_at(std::addressof(__with_val.__repr_.__union_.__unex_), std::move(__with_err.__repr_.__union_.__unex_));
- std::destroy_at(std::addressof(__with_err.__repr_.__union_.__unex_));
- __with_val.__repr_.__has_val_ = false;
- __with_err.__repr_.__has_val_ = true;
+ auto __swap_val_unex_impl = [](expected& __with_val, expected& __with_err) {
+ // May throw, but will re-engage `__with_val` in that case.
+ __with_val.__reinit_expected(std::unexpect, std::move(__with_err.__repr_.__union_.__unex_));
+ // Will not throw.
+ __with_err.__reinit_expected(std::in_place);
};
if (__repr_.__has_val_) {
@@ -1534,7 +1529,7 @@ class expected<_Tp, _Err> {
requires(is_trivially_destructible_v<_ErrorType>)
= default;
- // the expected's destructor handles this
+ // the __expected_repr's destructor handles this
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() {}
__empty_t __empty_;
@@ -1573,7 +1568,7 @@ class expected<_Tp, _Err> {
requires(is_trivially_destructible_v<_ErrorType>)
= default;
- // the expected's destructor handles this
+ // the __expected_repr's destructor handles this
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
requires(!is_trivially_destructible_v<_ErrorType>)
{}
@@ -1582,7 +1577,56 @@ class expected<_Tp, _Err> {
_LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
};
- __expected_repr<__union_t<_Err>> __repr_;
+ struct __expected_repr {
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr() = delete;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(std::in_place_t) : __union_(), __has_val_(true) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(std::unexpect_t __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(
+ std::__expected_construct_unexpected_from_invoke_tag __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(bool __has_val, _Args&&... __args)
+ : __union_(__has_val, std::forward<_Args>(__args)...), __has_val_(__has_val) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&) = delete;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&)
+ requires(is_copy_constructible_v<_Err> && is_trivially_copy_constructible_v<_Err>)
+ = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(__expected_repr&&) = delete;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(__expected_repr&&)
+ requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr& operator=(const __expected_repr&) = delete;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+ requires(is_trivially_destructible_v<_Err>)
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+ requires(!is_trivially_destructible_v<_Err>)
+ {
+ if (!__has_val_) {
+ std::destroy_at(std::addressof(__union_.__unex_));
+ }
+ }
+
+ private:
+ template <class, class>
+ friend class expected;
+
+ [[no_unique_address]] __union_t<_Err> __union_;
+ [[no_unique_address]] bool __has_val_;
+ };
+
+ __expected_repr __repr_;
};
_LIBCPP_END_NAMESPACE_STD
>From 48d576225af44a222e47959aa5073641283c30b9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 11:40:48 +0200
Subject: [PATCH 29/38] remove unused 'expected<void>' constructor
---
libcxx/include/__expected/expected.h | 6 ------
1 file changed, 6 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 5583eb85f4b437b..5ab10d9f3abf50b 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -1077,12 +1077,6 @@ class expected<_Tp, _Err> {
: __repr_(std::unexpect, __il, std::forward<_Args>(__args)...) {}
private:
- template <class _Func>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(__expected_construct_in_place_from_invoke_tag, _Func&& __f)
- : __repr_(std::in_place) {
- std::invoke(std::forward<_Func>(__f));
- }
-
template <class _Func, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
__expected_construct_unexpected_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)
>From e457df7b62e3c624332905f6272bfe65abca1989 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 11:48:59 +0200
Subject: [PATCH 30/38] remove unused datasizeof include
---
libcxx/include/__expected/expected.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 5ab10d9f3abf50b..531672526e28a88 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -18,7 +18,6 @@
#include <__memory/addressof.h>
#include <__memory/construct_at.h>
#include <__type_traits/conjunction.h>
-#include <__type_traits/datasizeof.h>
#include <__type_traits/disjunction.h>
#include <__type_traits/integral_constant.h>
#include <__type_traits/is_assignable.h>
>From f4847410f7d5fdc53943d2e5c04ab7a5c94b9d12 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 13:29:09 +0200
Subject: [PATCH 31/38] rename 'std::in_place_t' to 'in_place_t'
Co-authored-by: philnik777 <nikolasklauser at berlin.de>
---
libcxx/include/__expected/expected.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 531672526e28a88..390381741f0a0fc 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -833,7 +833,7 @@ class expected {
template <class _ValueType, class _ErrorType>
union __union_t {
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::in_place_t, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(in_place_t, _Args&&... __args)
: __val_(std::forward<_Args>(__args)...) {}
template <class... _Args>
>From ff4ab3e7b274b5a00aedfe52e4b53c2e541b59ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 13:32:27 +0200
Subject: [PATCH 32/38] std::in_place -> in_place
---
libcxx/include/__expected/expected.h | 56 ++++++++++++++--------------
1 file changed, 28 insertions(+), 28 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 390381741f0a0fc..c895c681f178b14 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -119,7 +119,7 @@ class expected {
_LIBCPP_HIDE_FROM_ABI constexpr expected()
noexcept(is_nothrow_default_constructible_v<_Tp>) // strengthened
requires is_default_constructible_v<_Tp>
- : __repr_(std::in_place) {}
+ : __repr_(in_place) {}
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
@@ -200,7 +200,7 @@ class expected {
(!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_expected<remove_cvref_t<_Up>>::value))
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp>)
expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
- : __repr_(std::in_place, std::forward<_Up>(__u)) {}
+ : __repr_(in_place, std::forward<_Up>(__u)) {}
template <class _OtherErr>
requires is_constructible_v<_Err, const _OtherErr&>
@@ -220,14 +220,14 @@ class expected {
requires is_constructible_v<_Tp, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Tp, _Args...>) // strengthened
- : __repr_(std::in_place, std::forward<_Args>(__args)...) {}
+ : __repr_(in_place, std::forward<_Args>(__args)...) {}
template <class _Up, class... _Args>
requires is_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit
expected(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, _Args...>) // strengthened
- : __repr_(std::in_place, __il, std::forward<_Args>(__args)...) {}
+ : __repr_(in_place, __il, std::forward<_Args>(__args)...) {}
template <class... _Args>
requires is_constructible_v<_Err, _Args...>
@@ -289,10 +289,10 @@ class expected {
if (__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
__repr_.__union_.__val_ = __rhs.__repr_.__union_.__val_;
} else if (__repr_.__has_val_) {
- __reinit_expected<std::unexpect_t, std::in_place_t, _Err, _Tp>(
+ __reinit_expected<std::unexpect_t, in_place_t, _Err, _Tp>(
__repr_.__union_.__val_, __rhs.__repr_.__union_.__unex_);
} else if (__rhs.__repr_.__has_val_) {
- __reinit_expected<std::in_place_t, std::unexpect_t, _Tp, _Err>(
+ __reinit_expected<in_place_t, std::unexpect_t, _Tp, _Err>(
__repr_.__union_.__unex_, __rhs.__repr_.__union_.__val_);
} else {
__repr_.__union_.__unex_ = __rhs.__repr_.__union_.__unex_;
@@ -315,10 +315,10 @@ class expected {
if (__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
__repr_.__union_.__val_ = std::move(__rhs.__repr_.__union_.__val_);
} else if (__repr_.__has_val_) {
- __reinit_expected<std::unexpect_t, std::in_place_t, _Err, _Tp>(
+ __reinit_expected<std::unexpect_t, in_place_t, _Err, _Tp>(
__repr_.__union_.__val_, std::move(__rhs.__repr_.__union_.__unex_));
} else if (__rhs.__repr_.__has_val_) {
- __reinit_expected<std::in_place_t, std::unexpect_t, _Tp, _Err>(
+ __reinit_expected<in_place_t, std::unexpect_t, _Tp, _Err>(
__repr_.__union_.__unex_, std::move(__rhs.__repr_.__union_.__val_));
} else {
__repr_.__union_.__unex_ = std::move(__rhs.__repr_.__union_.__unex_);
@@ -339,7 +339,7 @@ class expected {
if (__repr_.__has_val_) {
__repr_.__union_.__val_ = std::forward<_Up>(__v);
} else {
- __reinit_expected<std::in_place_t, std::unexpect_t, _Tp, _Err>(
+ __reinit_expected<in_place_t, std::unexpect_t, _Tp, _Err>(
__repr_.__union_.__unex_, std::forward<_Up>(__v));
}
return *this;
@@ -360,7 +360,7 @@ class expected {
requires(__can_assign_from_unexpected<const _OtherErr&>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const unexpected<_OtherErr>& __un) {
if (__repr_.__has_val_) {
- __reinit_expected<std::unexpect_t, std::in_place_t, _Err, _Tp>(
+ __reinit_expected<std::unexpect_t, in_place_t, _Err, _Tp>(
__repr_.__union_.__val_, __un.error());
} else {
__repr_.__union_.__unex_ = __un.error();
@@ -372,7 +372,7 @@ class expected {
requires(__can_assign_from_unexpected<_OtherErr>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(unexpected<_OtherErr>&& __un) {
if (__repr_.__has_val_) {
- __reinit_expected<std::unexpect_t, std::in_place_t, _Err, _Tp>(
+ __reinit_expected<std::unexpect_t, in_place_t, _Err, _Tp>(
__repr_.__union_.__val_, std::move(__un.error()));
} else {
__repr_.__union_.__unex_ = std::move(__un.error());
@@ -384,7 +384,7 @@ class expected {
requires is_nothrow_constructible_v<_Tp, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr _Tp& emplace(_Args&&... __args) noexcept {
std::destroy_at(&__repr_);
- std::construct_at(&__repr_, std::in_place, std::forward<_Args>(__args)...);
+ std::construct_at(&__repr_, in_place, std::forward<_Args>(__args)...);
return *std::addressof(__repr_.__union_.__val_);
}
@@ -392,7 +392,7 @@ class expected {
requires is_nothrow_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) noexcept {
std::destroy_at(&__repr_);
- std::construct_at(&__repr_, std::in_place, __il, std::forward<_Args>(__args)...);
+ std::construct_at(&__repr_, in_place, __il, std::forward<_Args>(__args)...);
return *std::addressof(__repr_.__union_.__val_);
}
@@ -418,7 +418,7 @@ class expected {
auto __trans = std::__make_exception_guard([&] {
std::construct_at(&__with_err.__repr_, std::unexpect, std::move(__tmp));
});
- std::construct_at(&__with_err.__repr_, std::in_place, std::move(__with_val.__repr_.__union_.__val_));
+ std::construct_at(&__with_err.__repr_, in_place, std::move(__with_val.__repr_.__union_.__val_));
__trans.__complete();
std::destroy_at(&__with_val.__repr_);
std::construct_at(&__with_val.__repr_, std::unexpect, std::move(__tmp));
@@ -429,12 +429,12 @@ class expected {
_Tp __tmp(std::move(__with_val.__repr_.__union_.__val_));
std::destroy_at(&__with_val.__repr_);
auto __trans = std::__make_exception_guard([&] {
- std::construct_at(&__with_val.__repr_, std::in_place, std::move(__tmp));
+ std::construct_at(&__with_val.__repr_, in_place, std::move(__tmp));
});
std::construct_at(&__with_val.__repr_, std::unexpect, std::move(__with_err.__repr_.__union_.__unex_));
__trans.__complete();
std::destroy_at(&__with_err.__repr_);
- std::construct_at(&__with_err.__repr_, std::in_place, std::move(__tmp));
+ std::construct_at(&__with_err.__repr_, in_place, std::move(__tmp));
}
};
@@ -880,7 +880,7 @@ class expected {
_LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(const __union_t&) = default;
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(std::in_place_t, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(in_place_t, _Args&&... __args)
: __val_(std::forward<_Args>(__args)...) {}
template <class... _Args>
@@ -922,7 +922,7 @@ class expected {
_LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr() = delete;
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(std::in_place_t __tag, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(in_place_t __tag, _Args&&... __args)
: __union_(__tag, std::forward<_Args>(__args)...), __has_val_(true) {}
template <class... _Args>
@@ -1011,7 +1011,7 @@ class expected<_Tp, _Err> {
using rebind = expected<_Up, error_type>;
// [expected.void.ctor], constructors
- _LIBCPP_HIDE_FROM_ABI constexpr expected() noexcept : __repr_(std::in_place) {}
+ _LIBCPP_HIDE_FROM_ABI constexpr expected() noexcept : __repr_(in_place) {}
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
@@ -1061,7 +1061,7 @@ class expected<_Tp, _Err> {
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
: __repr_(std::unexpect, std::move(__unex.error())) {}
- _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __repr_(std::in_place) {}
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __repr_(in_place) {}
template <class... _Args>
requires is_constructible_v<_Err, _Args...>
@@ -1092,15 +1092,15 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr void __reinit_expected(std::unexpect_t, _Args&&... __args) {
std::destroy_at(&__repr_);
auto __trans =
- std::__make_exception_guard([&] { std::construct_at(&__repr_, std::in_place); });
+ std::__make_exception_guard([&] { std::construct_at(&__repr_, in_place); });
std::construct_at(&__repr_, std::unexpect, std::forward<_Args>(__args)...);
__trans.__complete();
}
// precondition: !has_value()
- _LIBCPP_HIDE_FROM_ABI constexpr void __reinit_expected(std::in_place_t) {
+ _LIBCPP_HIDE_FROM_ABI constexpr void __reinit_expected(in_place_t) {
std::destroy_at(&__repr_);
- std::construct_at(&__repr_, std::in_place);
+ std::construct_at(&__repr_, in_place);
}
public:
@@ -1117,7 +1117,7 @@ class expected<_Tp, _Err> {
}
} else {
if (__rhs.__repr_.__has_val_) {
- __reinit_expected(std::in_place);
+ __reinit_expected(in_place);
} else {
__repr_.__union_.__unex_ = __rhs.__repr_.__union_.__unex_;
}
@@ -1139,7 +1139,7 @@ class expected<_Tp, _Err> {
}
} else {
if (__rhs.__repr_.__has_val_) {
- __reinit_expected(std::in_place);
+ __reinit_expected(in_place);
} else {
__repr_.__union_.__unex_ = std::move(__rhs.__repr_.__union_.__unex_);
}
@@ -1171,7 +1171,7 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr void emplace() noexcept {
if (!__repr_.__has_val_) {
- __reinit_expected(std::in_place);
+ __reinit_expected(in_place);
}
}
@@ -1184,7 +1184,7 @@ class expected<_Tp, _Err> {
// May throw, but will re-engage `__with_val` in that case.
__with_val.__reinit_expected(std::unexpect, std::move(__with_err.__repr_.__union_.__unex_));
// Will not throw.
- __with_err.__reinit_expected(std::in_place);
+ __with_err.__reinit_expected(in_place);
};
if (__repr_.__has_val_) {
@@ -1573,7 +1573,7 @@ class expected<_Tp, _Err> {
struct __expected_repr {
_LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr() = delete;
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(std::in_place_t) : __union_(), __has_val_(true) {}
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(in_place_t) : __union_(), __has_val_(true) {}
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(std::unexpect_t __tag, _Args&&... __args)
>From 75bab67780f329cf0bff21faff820682fe1b1d44 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 13:33:55 +0200
Subject: [PATCH 33/38] std::unexpect -> unexpect
---
libcxx/include/__expected/expected.h | 62 ++++++++++++++--------------
1 file changed, 31 insertions(+), 31 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index c895c681f178b14..fb9e3e91e8ac839 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -207,14 +207,14 @@ class expected {
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const unexpected<_OtherErr>& __unex)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __repr_(std::unexpect, __unex.error()) {}
+ : __repr_(unexpect, __unex.error()) {}
template <class _OtherErr>
requires is_constructible_v<_Err, _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(unexpected<_OtherErr>&& __unex)
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __repr_(std::unexpect, std::move(__unex.error())) {}
+ : __repr_(unexpect, std::move(__unex.error())) {}
template <class... _Args>
requires is_constructible_v<_Tp, _Args...>
@@ -233,14 +233,14 @@ class expected {
requires is_constructible_v<_Err, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
- : __repr_(std::unexpect, std::forward<_Args>(__args)...) {}
+ : __repr_(unexpect, std::forward<_Args>(__args)...) {}
template <class _Up, class... _Args>
requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit
expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
- : __repr_(std::unexpect, __il, std::forward<_Args>(__args)...) {}
+ : __repr_(unexpect, __il, std::forward<_Args>(__args)...) {}
// [expected.object.dtor], destructor
@@ -289,10 +289,10 @@ class expected {
if (__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
__repr_.__union_.__val_ = __rhs.__repr_.__union_.__val_;
} else if (__repr_.__has_val_) {
- __reinit_expected<std::unexpect_t, in_place_t, _Err, _Tp>(
+ __reinit_expected<unexpect_t, in_place_t, _Err, _Tp>(
__repr_.__union_.__val_, __rhs.__repr_.__union_.__unex_);
} else if (__rhs.__repr_.__has_val_) {
- __reinit_expected<in_place_t, std::unexpect_t, _Tp, _Err>(
+ __reinit_expected<in_place_t, unexpect_t, _Tp, _Err>(
__repr_.__union_.__unex_, __rhs.__repr_.__union_.__val_);
} else {
__repr_.__union_.__unex_ = __rhs.__repr_.__union_.__unex_;
@@ -315,10 +315,10 @@ class expected {
if (__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
__repr_.__union_.__val_ = std::move(__rhs.__repr_.__union_.__val_);
} else if (__repr_.__has_val_) {
- __reinit_expected<std::unexpect_t, in_place_t, _Err, _Tp>(
+ __reinit_expected<unexpect_t, in_place_t, _Err, _Tp>(
__repr_.__union_.__val_, std::move(__rhs.__repr_.__union_.__unex_));
} else if (__rhs.__repr_.__has_val_) {
- __reinit_expected<in_place_t, std::unexpect_t, _Tp, _Err>(
+ __reinit_expected<in_place_t, unexpect_t, _Tp, _Err>(
__repr_.__union_.__unex_, std::move(__rhs.__repr_.__union_.__val_));
} else {
__repr_.__union_.__unex_ = std::move(__rhs.__repr_.__union_.__unex_);
@@ -339,7 +339,7 @@ class expected {
if (__repr_.__has_val_) {
__repr_.__union_.__val_ = std::forward<_Up>(__v);
} else {
- __reinit_expected<in_place_t, std::unexpect_t, _Tp, _Err>(
+ __reinit_expected<in_place_t, unexpect_t, _Tp, _Err>(
__repr_.__union_.__unex_, std::forward<_Up>(__v));
}
return *this;
@@ -360,7 +360,7 @@ class expected {
requires(__can_assign_from_unexpected<const _OtherErr&>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const unexpected<_OtherErr>& __un) {
if (__repr_.__has_val_) {
- __reinit_expected<std::unexpect_t, in_place_t, _Err, _Tp>(
+ __reinit_expected<unexpect_t, in_place_t, _Err, _Tp>(
__repr_.__union_.__val_, __un.error());
} else {
__repr_.__union_.__unex_ = __un.error();
@@ -372,7 +372,7 @@ class expected {
requires(__can_assign_from_unexpected<_OtherErr>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(unexpected<_OtherErr>&& __un) {
if (__repr_.__has_val_) {
- __reinit_expected<std::unexpect_t, in_place_t, _Err, _Tp>(
+ __reinit_expected<unexpect_t, in_place_t, _Err, _Tp>(
__repr_.__union_.__val_, std::move(__un.error()));
} else {
__repr_.__union_.__unex_ = std::move(__un.error());
@@ -416,12 +416,12 @@ class expected {
_Err __tmp(std::move(__with_err.__repr_.__union_.__unex_));
std::destroy_at(&__with_err.__repr_);
auto __trans = std::__make_exception_guard([&] {
- std::construct_at(&__with_err.__repr_, std::unexpect, std::move(__tmp));
+ std::construct_at(&__with_err.__repr_, unexpect, std::move(__tmp));
});
std::construct_at(&__with_err.__repr_, in_place, std::move(__with_val.__repr_.__union_.__val_));
__trans.__complete();
std::destroy_at(&__with_val.__repr_);
- std::construct_at(&__with_val.__repr_, std::unexpect, std::move(__tmp));
+ std::construct_at(&__with_val.__repr_, 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 "
@@ -431,7 +431,7 @@ class expected {
auto __trans = std::__make_exception_guard([&] {
std::construct_at(&__with_val.__repr_, in_place, std::move(__tmp));
});
- std::construct_at(&__with_val.__repr_, std::unexpect, std::move(__with_err.__repr_.__union_.__unex_));
+ std::construct_at(&__with_val.__repr_, unexpect, std::move(__with_err.__repr_.__union_.__unex_));
__trans.__complete();
std::destroy_at(&__with_err.__repr_);
std::construct_at(&__with_err.__repr_, in_place, std::move(__tmp));
@@ -837,7 +837,7 @@ class expected {
: __val_(std::forward<_Args>(__args)...) {}
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>
@@ -884,7 +884,7 @@ class expected {
: __val_(std::forward<_Args>(__args)...) {}
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>
@@ -926,7 +926,7 @@ class expected {
: __union_(__tag, std::forward<_Args>(__args)...), __has_val_(true) {}
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(std::unexpect_t __tag, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(unexpect_t __tag, _Args&&... __args)
: __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
template <class... _Args>
@@ -1052,14 +1052,14 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const unexpected<_OtherErr>& __unex)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __repr_(std::unexpect, __unex.error()) {}
+ : __repr_(unexpect, __unex.error()) {}
template <class _OtherErr>
requires is_constructible_v<_Err, _OtherErr>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(unexpected<_OtherErr>&& __unex)
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __repr_(std::unexpect, std::move(__unex.error())) {}
+ : __repr_(unexpect, std::move(__unex.error())) {}
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __repr_(in_place) {}
@@ -1067,13 +1067,13 @@ class expected<_Tp, _Err> {
requires is_constructible_v<_Err, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened
- : __repr_(std::unexpect, std::forward<_Args>(__args)...) {}
+ : __repr_(unexpect, std::forward<_Args>(__args)...) {}
template <class _Up, class... _Args>
requires is_constructible_v< _Err, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened
- : __repr_(std::unexpect, __il, std::forward<_Args>(__args)...) {}
+ : __repr_(unexpect, __il, std::forward<_Args>(__args)...) {}
private:
template <class _Func, class... _Args>
@@ -1089,11 +1089,11 @@ class expected<_Tp, _Err> {
private:
// precondition: has_value()
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr void __reinit_expected(std::unexpect_t, _Args&&... __args) {
+ _LIBCPP_HIDE_FROM_ABI constexpr void __reinit_expected(unexpect_t, _Args&&... __args) {
std::destroy_at(&__repr_);
auto __trans =
std::__make_exception_guard([&] { std::construct_at(&__repr_, in_place); });
- std::construct_at(&__repr_, std::unexpect, std::forward<_Args>(__args)...);
+ std::construct_at(&__repr_, unexpect, std::forward<_Args>(__args)...);
__trans.__complete();
}
@@ -1113,7 +1113,7 @@ class expected<_Tp, _Err> {
{
if (__repr_.__has_val_) {
if (!__rhs.__repr_.__has_val_) {
- __reinit_expected(std::unexpect, __rhs.__repr_.__union_.__unex_);
+ __reinit_expected(unexpect, __rhs.__repr_.__union_.__unex_);
}
} else {
if (__rhs.__repr_.__has_val_) {
@@ -1135,7 +1135,7 @@ class expected<_Tp, _Err> {
{
if (__repr_.__has_val_) {
if (!__rhs.__repr_.__has_val_) {
- __reinit_expected(std::unexpect, std::move(__rhs.__repr_.__union_.__unex_));
+ __reinit_expected(unexpect, std::move(__rhs.__repr_.__union_.__unex_));
}
} else {
if (__rhs.__repr_.__has_val_) {
@@ -1151,7 +1151,7 @@ class expected<_Tp, _Err> {
requires(is_constructible_v<_Err, const _OtherErr&> && is_assignable_v<_Err&, const _OtherErr&>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const unexpected<_OtherErr>& __un) {
if (__repr_.__has_val_) {
- __reinit_expected(std::unexpect, __un.error());
+ __reinit_expected(unexpect, __un.error());
} else {
__repr_.__union_.__unex_ = __un.error();
}
@@ -1162,7 +1162,7 @@ class expected<_Tp, _Err> {
requires(is_constructible_v<_Err, _OtherErr> && is_assignable_v<_Err&, _OtherErr>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(unexpected<_OtherErr>&& __un) {
if (__repr_.__has_val_) {
- __reinit_expected(std::unexpect, std::move(__un.error()));
+ __reinit_expected(unexpect, std::move(__un.error()));
} else {
__repr_.__union_.__unex_ = std::move(__un.error());
}
@@ -1182,7 +1182,7 @@ class expected<_Tp, _Err> {
{
auto __swap_val_unex_impl = [](expected& __with_val, expected& __with_err) {
// May throw, but will re-engage `__with_val` in that case.
- __with_val.__reinit_expected(std::unexpect, std::move(__with_err.__repr_.__union_.__unex_));
+ __with_val.__reinit_expected(unexpect, std::move(__with_err.__repr_.__union_.__unex_));
// Will not throw.
__with_err.__reinit_expected(in_place);
};
@@ -1502,7 +1502,7 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr __union_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>
@@ -1541,7 +1541,7 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr __union_t& operator=(const __union_t&) = default;
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t(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>
@@ -1576,7 +1576,7 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(in_place_t) : __union_(), __has_val_(true) {}
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(std::unexpect_t __tag, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(unexpect_t __tag, _Args&&... __args)
: __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
template <class... _Args>
>From 62bb11864f96bb5949268d8fb2b4b564f9835d73 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 13:37:54 +0200
Subject: [PATCH 34/38] use _LIBCPP_NO_UNIQUE_ADDRESS
Co-authored-by: philnik777 <nikolasklauser at berlin.de>
---
libcxx/include/__expected/expected.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index fb9e3e91e8ac839..e55f4185431b78e 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -976,8 +976,8 @@ class expected {
template <class, class>
friend class expected;
- [[no_unique_address]] __union_t<_Tp, _Err> __union_;
- [[no_unique_address]] bool __has_val_;
+ _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
+ _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
};
__expected_repr __repr_;
>From cd3e77297de13a4170d8c11e1d89a25127cb4b05 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 13:39:00 +0200
Subject: [PATCH 35/38] use _LIBCPP_NO_UNIQUE_ADDRESS
---
libcxx/include/__expected/expected.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index e55f4185431b78e..6c254e8a2cf4f1e 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -1615,8 +1615,8 @@ class expected<_Tp, _Err> {
template <class, class>
friend class expected;
- [[no_unique_address]] __union_t<_Err> __union_;
- [[no_unique_address]] bool __has_val_;
+ _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
+ _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
};
__expected_repr __repr_;
>From 3bd7c7269941be94221f804d37a178c71697444d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 13:43:23 +0200
Subject: [PATCH 36/38] remove _LIBCPP_GCC_DIAGNOSTIC_IGNORED (should be
separate issue)
---
libcxx/include/__type_traits/datasizeof.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/libcxx/include/__type_traits/datasizeof.h b/libcxx/include/__type_traits/datasizeof.h
index 96404ad15a73376..5688e3293a69eb5 100644
--- a/libcxx/include/__type_traits/datasizeof.h
+++ b/libcxx/include/__type_traits/datasizeof.h
@@ -51,7 +51,6 @@ struct __libcpp_datasizeof {
// the use as an extension.
_LIBCPP_DIAGNOSTIC_PUSH
_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Winvalid-offsetof")
- _LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Winvalid-offsetof")
static const size_t value = offsetof(_FirstPaddingByte<>, __first_padding_byte_);
_LIBCPP_DIAGNOSTIC_POP
};
>From 1d3f19a0a82dd2cf7cd81262b90514fdeb4f80ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 21 Oct 2023 14:10:41 +0200
Subject: [PATCH 37/38] rename __expected_repr to __repr
---
libcxx/include/__expected/expected.h | 66 ++++++++++++++--------------
1 file changed, 33 insertions(+), 33 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 6c254e8a2cf4f1e..8dd06d2fe3df337 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -862,7 +862,7 @@ class expected {
requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>)
= default;
- // the __expected_repr's destructor handles this
+ // __repr's destructor handles this
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() {}
_ValueType __val_;
@@ -909,7 +909,7 @@ class expected {
requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>)
= default;
- // the __expected_repr's destructor handles this
+ // __repr's destructor handles this
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
requires(!is_trivially_destructible_v<_ValueType> || !is_trivially_destructible_v<_ErrorType>)
{}
@@ -918,51 +918,51 @@ class expected {
_LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
};
- struct __expected_repr {
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr() = delete;
+ struct __repr {
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr() = delete;
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(in_place_t __tag, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(in_place_t __tag, _Args&&... __args)
: __union_(__tag, std::forward<_Args>(__args)...), __has_val_(true) {}
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(unexpect_t __tag, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(unexpect_t __tag, _Args&&... __args)
: __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(
std::__expected_construct_in_place_from_invoke_tag __tag, _Args&&... __args)
: __union_(__tag, std::forward<_Args>(__args)...), __has_val_(true) {}
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(
std::__expected_construct_unexpected_from_invoke_tag __tag, _Args&&... __args)
: __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(bool __has_val, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(bool __has_val, _Args&&... __args)
: __union_(__has_val, std::forward<_Args>(__args)...), __has_val_(__has_val) {}
- _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&) = delete;
- _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&)
+ _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 __expected_repr(__expected_repr&&) = delete;
- _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(__expected_repr&&)
+ _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 __expected_repr& operator=(const __expected_repr&) = delete;
+ _LIBCPP_HIDE_FROM_ABI constexpr __repr& operator=(const __repr&) = delete;
- _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__repr()
requires(is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Err>)
= default;
- _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__repr()
requires(!is_trivially_destructible_v<_Tp> || !is_trivially_destructible_v<_Err>)
{
if (__has_val_) {
@@ -980,7 +980,7 @@ class expected {
_LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
};
- __expected_repr __repr_;
+ __repr __repr_;
};
template <class _Tp, class _Err>
@@ -1522,7 +1522,7 @@ class expected<_Tp, _Err> {
requires(is_trivially_destructible_v<_ErrorType>)
= default;
- // the __expected_repr's destructor handles this
+ // __repr's destructor handles this
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() {}
__empty_t __empty_;
@@ -1561,7 +1561,7 @@ class expected<_Tp, _Err> {
requires(is_trivially_destructible_v<_ErrorType>)
= default;
- // the __expected_repr's destructor handles this
+ // __repr's destructor handles this
_LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
requires(!is_trivially_destructible_v<_ErrorType>)
{}
@@ -1570,40 +1570,40 @@ class expected<_Tp, _Err> {
_LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
};
- struct __expected_repr {
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr() = delete;
+ struct __repr {
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr() = delete;
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(in_place_t) : __union_(), __has_val_(true) {}
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(in_place_t) : __union_(), __has_val_(true) {}
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(unexpect_t __tag, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(unexpect_t __tag, _Args&&... __args)
: __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(
std::__expected_construct_unexpected_from_invoke_tag __tag, _Args&&... __args)
: __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(bool __has_val, _Args&&... __args)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(bool __has_val, _Args&&... __args)
: __union_(__has_val, std::forward<_Args>(__args)...), __has_val_(__has_val) {}
- _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&) = delete;
- _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&)
+ _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 __expected_repr(__expected_repr&&) = delete;
- _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(__expected_repr&&)
+ _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;
- _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr& operator=(const __expected_repr&) = delete;
+ _LIBCPP_HIDE_FROM_ABI constexpr __repr& operator=(const __repr&) = delete;
- _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__repr()
requires(is_trivially_destructible_v<_Err>)
= default;
- _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__repr()
requires(!is_trivially_destructible_v<_Err>)
{
if (!__has_val_) {
@@ -1619,7 +1619,7 @@ class expected<_Tp, _Err> {
_LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
};
- __expected_repr __repr_;
+ __repr __repr_;
};
_LIBCPP_END_NAMESPACE_STD
>From 7b67afc52ccd784fc00b7ec37a1bbe26cfe0e881 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 24 Oct 2023 16:52:35 +0200
Subject: [PATCH 38/38] implement optimized layout for types without tail
padding
---
libcxx/include/__expected/expected.h | 1124 ++++++++++-------
.../no_unique_address.compile.pass.cpp | 22 +-
.../transform_error.mandates.verify.cpp | 1 +
.../no_unique_address.compile.pass.cpp | 19 +-
.../transform_error.mandates.verify.cpp | 2 +
5 files changed, 726 insertions(+), 442 deletions(-)
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 8dd06d2fe3df337..fa8910e25d92607 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -88,8 +88,288 @@ _LIBCPP_HIDE_FROM_ABI void __throw_bad_expected_access(_Arg&& __arg) {
# endif
}
+template <class _Union, class _OtherUnion>
+_LIBCPP_HIDE_FROM_ABI constexpr _Union __make_expected_union(bool __has_val, _OtherUnion&& __other)
+{
+ if (__has_val)
+ return _Union(in_place, std::forward<_OtherUnion>(__other).__val_);
+ else
+ return _Union(unexpect, std::forward<_OtherUnion>(__other).__unex_);
+}
+
+template <class _ValueType, class _ErrorType>
+union __expected_union_t {
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_union_t(in_place_t, _Args&&... __args)
+ : __val_(std::forward<_Args>(__args)...) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_union_t(unexpect_t, _Args&&... __args)
+ : __unex_(std::forward<_Args>(__args)...) {}
+
+ template <class _Func, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_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 __expected_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 ~__expected_union_t()
+ requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>)
+ = default;
+
+ // __expected_repr's destructor handles this
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_union_t() {}
+
+ // XXX Why are those not [[no_unique_address]]?
+ _ValueType __val_;
+ _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 _ValueType, class _ErrorType>
+ requires(is_trivially_move_constructible_v<_ValueType> && is_trivially_move_constructible_v<_ErrorType>)
+union __expected_union_t<_ValueType, _ErrorType> {
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_union_t(const __expected_union_t&) = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_union_t& operator=(const __expected_union_t&) = default;
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_union_t(in_place_t, _Args&&... __args)
+ : __val_(std::forward<_Args>(__args)...) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_union_t(unexpect_t, _Args&&... __args)
+ : __unex_(std::forward<_Args>(__args)...) {}
+
+ template <class _Func, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_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 __expected_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 ~__expected_union_t()
+ requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>)
+ = default;
+
+ // __expected_repr's destructor handles this
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_union_t() {}
+
+ _LIBCPP_NO_UNIQUE_ADDRESS _ValueType __val_;
+ _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
+};
+
+template <class _Tp, class _Err, bool _StuffTail = false>
+struct __expected_repr {
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr() = delete;
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(in_place_t __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(true) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(unexpect_t __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(
+ std::__expected_construct_in_place_from_invoke_tag __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(true) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(
+ std::__expected_construct_unexpected_from_invoke_tag __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+ template <class _OtherUnion>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(bool __has_val, _OtherUnion&& __other)
+ : __union_(__make_expected_union<__expected_union_t<_Tp, _Err>>(__has_val,
+ std::forward<_OtherUnion>(__other))),
+ __has_val_(__has_val) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&) = delete;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&)
+ requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err> &&
+ is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Err>)
+ = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(__expected_repr&&) = delete;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(__expected_repr&&)
+ requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err> &&
+ is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>)
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr& operator=(const __expected_repr&) = delete;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+ requires(is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Err>)
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+ requires(!is_trivially_destructible_v<_Tp> || !is_trivially_destructible_v<_Err>)
+ {
+ __destroy_union_member();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void __destroy_union()
+ requires(is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Err>)
+ {
+ std::destroy_at(&__union_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void __destroy_union()
+ requires(!is_trivially_destructible_v<_Tp> || !is_trivially_destructible_v<_Err>)
+ {
+ __destroy_union_member();
+ std::destroy_at(&__union_);
+ }
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr void __construct_union(in_place_t, _Args&&... __args)
+ {
+ std::construct_at(&__union_, in_place, std::forward<_Args>(__args)...);
+ __has_val_ = true;
+ }
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr void __construct_union(unexpect_t, _Args&&... __args)
+ {
+ std::construct_at(&__union_, unexpect, std::forward<_Args>(__args)...);
+ __has_val_ = false;
+ }
+
+private:
+ template <class, class>
+ friend class expected;
+
+ _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_.__val_));
+ } else {
+ std::destroy_at(std::addressof(__union_.__unex_));
+ }
+ }
+
+ __expected_union_t<_Tp, _Err> __union_;
+ _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
+};
+
+template <class _Tp, class _Err>
+struct __expected_repr<_Tp, _Err, true> {
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr() = delete;
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(in_place_t __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(true) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(unexpect_t __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(
+ std::__expected_construct_in_place_from_invoke_tag __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(true) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(
+ std::__expected_construct_unexpected_from_invoke_tag __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+ template <class _OtherUnion>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_repr(bool __has_val, _OtherUnion&& __other)
+ : __union_(__make_expected_union<__expected_union_t<_Tp, _Err>>(__has_val,
+ std::forward<_OtherUnion>(__other))),
+ __has_val_(__has_val) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&) = delete;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(const __expected_repr&)
+ requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err> &&
+ is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Err>)
+ = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(__expected_repr&&) = delete;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr(__expected_repr&&)
+ requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err> &&
+ is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>)
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_repr& operator=(const __expected_repr&) = delete;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+ requires(is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Err>)
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_repr()
+ requires(!is_trivially_destructible_v<_Tp> || !is_trivially_destructible_v<_Err>)
+ {
+ if (__has_val_) {
+ std::destroy_at(std::addressof(__union_.__val_));
+ } else {
+ std::destroy_at(std::addressof(__union_.__unex_));
+ }
+ }
+
+private:
+ template <class, class>
+ friend class expected;
+
+ _LIBCPP_NO_UNIQUE_ADDRESS __expected_union_t<_Tp, _Err> __union_;
+ _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
+};
+
+template <class _Tp, class _Err>
+struct __expected_base {
+protected:
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_base(_Args&&... __args)
+ : __repr_(std::forward<_Args>(__args)...) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void __destroy() {
+ __repr_.__destroy_union();
+ }
+
+ template <class _Tag, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr void __construct(_Tag __tag, _Args&&... __args)
+ {
+ __repr_.__construct_union(__tag, std::forward<_Args>(__args)...);
+ }
+
+ _LIBCPP_NO_UNIQUE_ADDRESS __expected_repr<_Tp, _Err, false> __repr_;
+};
+
template <class _Tp, class _Err>
-class expected {
+ requires (sizeof(__expected_repr<_Tp, _Err, true>) == sizeof(__expected_union_t<_Tp, _Err>))
+struct __expected_base<_Tp, _Err> {
+protected:
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_base(_Args&&... __args)
+ : __repr_(std::forward<_Args>(__args)...) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void __destroy() {
+ std::destroy_at(&__repr_);
+ }
+
+ template <class _Tag, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr void __construct(_Tag __tag, _Args&&... __args)
+ {
+ std::construct_at(&__repr_, __tag, std::forward<_Args>(__args)...);
+ }
+
+ __expected_repr<_Tp, _Err, true> __repr_;
+};
+
+template <class _Tp, class _Err>
+class expected : private __expected_base<_Tp, _Err> {
static_assert(
!is_reference_v<_Tp> &&
!is_function_v<_Tp> &&
@@ -107,6 +387,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;
@@ -119,22 +401,20 @@ class expected {
_LIBCPP_HIDE_FROM_ABI constexpr expected()
noexcept(is_nothrow_default_constructible_v<_Tp>) // strengthened
requires is_default_constructible_v<_Tp>
- : __repr_(in_place) {}
+ : __base(in_place) {}
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&)
- requires(is_copy_constructible_v<_Tp> &&
- is_copy_constructible_v<_Err> &&
- is_trivially_copy_constructible_v<_Tp> &&
- is_trivially_copy_constructible_v<_Err>)
+ requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err> &&
+ is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Err>)
= default;
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected& __other)
noexcept(is_nothrow_copy_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Err>) // strengthened
requires(is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Err> &&
!(is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Err>))
- : __repr_(__other.__repr_.__has_val_, __other.__repr_.__union_) { }
+ : __base(__other.__repr_.__has_val_, __other.__repr_.__union_) { }
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err>
@@ -145,7 +425,7 @@ class expected {
noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_move_constructible_v<_Err>)
requires(is_move_constructible_v<_Tp> && is_move_constructible_v<_Err> &&
!(is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>))
- : __repr_(__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)) { }
+ : __base(__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)) { }
private:
template <class _Up, class _OtherErr, class _UfQual, class _OtherErrQual>
@@ -170,12 +450,12 @@ class expected {
template <class _Func, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
std::__expected_construct_in_place_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)
- : __repr_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...) {}
+ : __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)
- : __repr_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...) {}
+ : __base(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...) {}
public:
template <class _Up, class _OtherErr>
@@ -185,14 +465,14 @@ class expected {
expected(const expected<_Up, _OtherErr>& __other)
noexcept(is_nothrow_constructible_v<_Tp, const _Up&> &&
is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __repr_(__other.__repr_.__has_val_, __other.__repr_.__union_) {}
+ : __base(__other.__repr_.__has_val_, __other.__repr_.__union_) {}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, _Up, _OtherErr>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp> || !is_convertible_v<_OtherErr, _Err>)
expected(expected<_Up, _OtherErr>&& __other)
noexcept(is_nothrow_constructible_v<_Tp, _Up> && is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __repr_(__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)) {}
+ : __base(__other.__repr_.__has_val_, std::move(__other.__repr_.__union_)) {}
template <class _Up = _Tp>
requires(!is_same_v<remove_cvref_t<_Up>, in_place_t> && !is_same_v<expected, remove_cvref_t<_Up>> &&
@@ -200,47 +480,47 @@ class expected {
(!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_expected<remove_cvref_t<_Up>>::value))
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp>)
expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
- : __repr_(in_place, std::forward<_Up>(__u)) {}
+ : __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
- : __repr_(unexpect, __unex.error()) {}
+ : __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
- : __repr_(unexpect, std::move(__unex.error())) {}
+ : __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
- : __repr_(in_place, std::forward<_Args>(__args)...) {}
+ : __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
- : __repr_(in_place, __il, std::forward<_Args>(__args)...) {}
+ : __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
- : __repr_(unexpect, std::forward<_Args>(__args)...) {}
+ : __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
- : __repr_(unexpect, __il, std::forward<_Args>(__args)...) {}
+ : __base(unexpect, __il, std::forward<_Args>(__args)...) {}
// [expected.object.dtor], destructor
@@ -250,22 +530,21 @@ class expected {
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(&__repr_);
- std::construct_at(&__repr_, _Tag{}, 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(&__repr_);
- std::construct_at(&__repr_, _Tag{}, 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(&__repr_);
- auto __trans =
- std::__make_exception_guard([&] { std::construct_at(&__repr_, _OtherTag{}, std::move(__tmp)); });
- std::construct_at(&__repr_, _Tag{}, 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();
}
}
@@ -286,16 +565,16 @@ class expected {
(is_nothrow_move_constructible_v<_Tp> ||
is_nothrow_move_constructible_v<_Err>))
{
- if (__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
- __repr_.__union_.__val_ = __rhs.__repr_.__union_.__val_;
- } else if (__repr_.__has_val_) {
+ if (this->__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
+ this->__repr_.__union_.__val_ = __rhs.__repr_.__union_.__val_;
+ } else if (this->__repr_.__has_val_) {
__reinit_expected<unexpect_t, in_place_t, _Err, _Tp>(
- __repr_.__union_.__val_, __rhs.__repr_.__union_.__unex_);
+ this->__repr_.__union_.__val_, __rhs.__repr_.__union_.__unex_);
} else if (__rhs.__repr_.__has_val_) {
__reinit_expected<in_place_t, unexpect_t, _Tp, _Err>(
- __repr_.__union_.__unex_, __rhs.__repr_.__union_.__val_);
+ this->__repr_.__union_.__unex_, __rhs.__repr_.__union_.__val_);
} else {
- __repr_.__union_.__unex_ = __rhs.__repr_.__union_.__unex_;
+ this->__repr_.__union_.__unex_ = __rhs.__repr_.__union_.__unex_;
}
return *this;
}
@@ -312,16 +591,16 @@ class expected {
(is_nothrow_move_constructible_v<_Tp> ||
is_nothrow_move_constructible_v<_Err>))
{
- if (__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
- __repr_.__union_.__val_ = std::move(__rhs.__repr_.__union_.__val_);
- } else if (__repr_.__has_val_) {
+ if (this->__repr_.__has_val_ && __rhs.__repr_.__has_val_) {
+ this->__repr_.__union_.__val_ = std::move(__rhs.__repr_.__union_.__val_);
+ } else if (this->__repr_.__has_val_) {
__reinit_expected<unexpect_t, in_place_t, _Err, _Tp>(
- __repr_.__union_.__val_, std::move(__rhs.__repr_.__union_.__unex_));
+ this->__repr_.__union_.__val_, std::move(__rhs.__repr_.__union_.__unex_));
} else if (__rhs.__repr_.__has_val_) {
__reinit_expected<in_place_t, unexpect_t, _Tp, _Err>(
- __repr_.__union_.__unex_, std::move(__rhs.__repr_.__union_.__val_));
+ this->__repr_.__union_.__unex_, std::move(__rhs.__repr_.__union_.__val_));
} else {
- __repr_.__union_.__unex_ = std::move(__rhs.__repr_.__union_.__unex_);
+ this->__repr_.__union_.__unex_ = std::move(__rhs.__repr_.__union_.__unex_);
}
return *this;
}
@@ -336,11 +615,11 @@ class expected {
is_nothrow_move_constructible_v<_Tp> ||
is_nothrow_move_constructible_v<_Err>))
{
- if (__repr_.__has_val_) {
- __repr_.__union_.__val_ = std::forward<_Up>(__v);
+ if (this->__repr_.__has_val_) {
+ this->__repr_.__union_.__val_ = std::forward<_Up>(__v);
} else {
__reinit_expected<in_place_t, unexpect_t, _Tp, _Err>(
- __repr_.__union_.__unex_, std::forward<_Up>(__v));
+ this->__repr_.__union_.__unex_, std::forward<_Up>(__v));
}
return *this;
}
@@ -359,11 +638,11 @@ class expected {
template <class _OtherErr>
requires(__can_assign_from_unexpected<const _OtherErr&>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const unexpected<_OtherErr>& __un) {
- if (__repr_.__has_val_) {
+ if (this->__repr_.__has_val_) {
__reinit_expected<unexpect_t, in_place_t, _Err, _Tp>(
- __repr_.__union_.__val_, __un.error());
+ this->__repr_.__union_.__val_, __un.error());
} else {
- __repr_.__union_.__unex_ = __un.error();
+ this->__repr_.__union_.__unex_ = __un.error();
}
return *this;
}
@@ -371,11 +650,11 @@ class expected {
template <class _OtherErr>
requires(__can_assign_from_unexpected<_OtherErr>)
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(unexpected<_OtherErr>&& __un) {
- if (__repr_.__has_val_) {
+ if (this->__repr_.__has_val_) {
__reinit_expected<unexpect_t, in_place_t, _Err, _Tp>(
- __repr_.__union_.__val_, std::move(__un.error()));
+ this->__repr_.__union_.__val_, std::move(__un.error()));
} else {
- __repr_.__union_.__unex_ = std::move(__un.error());
+ this->__repr_.__union_.__unex_ = std::move(__un.error());
}
return *this;
}
@@ -383,20 +662,19 @@ class expected {
template <class... _Args>
requires is_nothrow_constructible_v<_Tp, _Args...>
_LIBCPP_HIDE_FROM_ABI constexpr _Tp& emplace(_Args&&... __args) noexcept {
- std::destroy_at(&__repr_);
- std::construct_at(&__repr_, in_place, std::forward<_Args>(__args)...);
- return *std::addressof(__repr_.__union_.__val_);
+ this->__destroy();
+ this->__construct(in_place, std::forward<_Args>(__args)...);
+ return *std::addressof(this->__repr_.__union_.__val_);
}
template <class _Up, class... _Args>
requires is_nothrow_constructible_v< _Tp, initializer_list<_Up>&, _Args... >
_LIBCPP_HIDE_FROM_ABI constexpr _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) noexcept {
- std::destroy_at(&__repr_);
- std::construct_at(&__repr_, in_place, __il, std::forward<_Args>(__args)...);
- return *std::addressof(__repr_.__union_.__val_);
+ this->__destroy();
+ this->__construct(in_place, __il, std::forward<_Args>(__args)...);
+ return *std::addressof(this->__repr_.__union_.__val_);
}
-
public:
// [expected.object.swap], swap
_LIBCPP_HIDE_FROM_ABI constexpr void swap(expected& __rhs)
@@ -414,34 +692,34 @@ class expected {
auto __swap_val_unex_impl = [](expected& __with_val, expected& __with_err) {
if constexpr (is_nothrow_move_constructible_v<_Err>) {
_Err __tmp(std::move(__with_err.__repr_.__union_.__unex_));
- std::destroy_at(&__with_err.__repr_);
+ __with_err.__destroy();
auto __trans = std::__make_exception_guard([&] {
- std::construct_at(&__with_err.__repr_, unexpect, std::move(__tmp));
+ __with_err.__construct(unexpect, std::move(__tmp));
});
- std::construct_at(&__with_err.__repr_, in_place, std::move(__with_val.__repr_.__union_.__val_));
+ __with_err.__construct(in_place, std::move(__with_val.__repr_.__union_.__val_));
__trans.__complete();
- std::destroy_at(&__with_val.__repr_);
- std::construct_at(&__with_val.__repr_, unexpect, 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.__repr_.__union_.__val_));
- std::destroy_at(&__with_val.__repr_);
+ __with_val.__destroy();
auto __trans = std::__make_exception_guard([&] {
- std::construct_at(&__with_val.__repr_, in_place, std::move(__tmp));
+ __with_val.__construct(in_place, std::move(__tmp));
});
- std::construct_at(&__with_val.__repr_, unexpect, std::move(__with_err.__repr_.__union_.__unex_));
+ __with_val.__construct(unexpect, std::move(__with_err.__repr_.__union_.__unex_));
__trans.__complete();
- std::destroy_at(&__with_err.__repr_);
- std::construct_at(&__with_err.__repr_, in_place, std::move(__tmp));
+ __with_err.__destroy();
+ __with_err.__construct(in_place, std::move(__tmp));
}
};
- if (__repr_.__has_val_) {
+ if (this->__repr_.__has_val_) {
if (__rhs.__repr_.__has_val_) {
using std::swap;
- swap(__repr_.__union_.__val_, __rhs.__repr_.__union_.__val_);
+ swap(this->__repr_.__union_.__val_, __rhs.__repr_.__union_.__val_);
} else {
__swap_val_unex_impl(*this, __rhs);
}
@@ -450,7 +728,7 @@ class expected {
__swap_val_unex_impl(__rhs, *this);
} else {
using std::swap;
- swap(__repr_.__union_.__unex_, __rhs.__repr_.__union_.__unex_);
+ swap(this->__repr_.__union_.__unex_, __rhs.__repr_.__union_.__unex_);
}
}
}
@@ -464,105 +742,105 @@ class expected {
// [expected.object.obs], observers
_LIBCPP_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator-> requires the expected to contain a value");
- return std::addressof(__repr_.__union_.__val_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->__repr_.__has_val_, "expected::operator-> requires the expected to contain a value");
+ return std::addressof(this->__repr_.__union_.__val_);
}
_LIBCPP_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator-> requires the expected to contain a value");
- return std::addressof(__repr_.__union_.__val_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->__repr_.__has_val_, "expected::operator-> requires the expected to contain a value");
+ return std::addressof(this->__repr_.__union_.__val_);
}
_LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
- return __repr_.__union_.__val_;
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
+ return this->__repr_.__union_.__val_;
}
_LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
- return __repr_.__union_.__val_;
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
+ return this->__repr_.__union_.__val_;
}
_LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
- return std::move(__repr_.__union_.__val_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
+ return std::move(this->__repr_.__union_.__val_);
}
_LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
- return std::move(__repr_.__union_.__val_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
+ return std::move(this->__repr_.__union_.__val_);
}
- _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return __repr_.__has_val_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return this->__repr_.__has_val_; }
- _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __repr_.__has_val_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__repr_.__has_val_; }
_LIBCPP_HIDE_FROM_ABI constexpr const _Tp& value() const& {
static_assert(is_copy_constructible_v<_Err>, "error_type has to be copy constructible");
- if (!__repr_.__has_val_) {
+ if (!this->__repr_.__has_val_) {
std::__throw_bad_expected_access<_Err>(std::as_const(error()));
}
- return __repr_.__union_.__val_;
+ return this->__repr_.__union_.__val_;
}
_LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & {
static_assert(is_copy_constructible_v<_Err>, "error_type has to be copy constructible");
- if (!__repr_.__has_val_) {
+ if (!this->__repr_.__has_val_) {
std::__throw_bad_expected_access<_Err>(std::as_const(error()));
}
- return __repr_.__union_.__val_;
+ return this->__repr_.__union_.__val_;
}
_LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& value() const&& {
static_assert(is_copy_constructible_v<_Err> && is_constructible_v<_Err, decltype(std::move(error()))>,
"error_type has to be both copy constructible and constructible from decltype(std::move(error()))");
- if (!__repr_.__has_val_) {
+ if (!this->__repr_.__has_val_) {
std::__throw_bad_expected_access<_Err>(std::move(error()));
}
- return std::move(__repr_.__union_.__val_);
+ return std::move(this->__repr_.__union_.__val_);
}
_LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && {
static_assert(is_copy_constructible_v<_Err> && is_constructible_v<_Err, decltype(std::move(error()))>,
"error_type has to be both copy constructible and constructible from decltype(std::move(error()))");
- if (!__repr_.__has_val_) {
+ if (!this->__repr_.__has_val_) {
std::__throw_bad_expected_access<_Err>(std::move(error()));
}
- return std::move(__repr_.__union_.__val_);
+ return std::move(this->__repr_.__union_.__val_);
}
_LIBCPP_HIDE_FROM_ABI constexpr const _Err& error() const& noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
- return __repr_.__union_.__unex_;
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!this->__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return this->__repr_.__union_.__unex_;
}
_LIBCPP_HIDE_FROM_ABI constexpr _Err& error() & noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
- return __repr_.__union_.__unex_;
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!this->__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return this->__repr_.__union_.__unex_;
}
_LIBCPP_HIDE_FROM_ABI constexpr const _Err&& error() const&& noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
- return std::move(__repr_.__union_.__unex_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!this->__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return std::move(this->__repr_.__union_.__unex_);
}
_LIBCPP_HIDE_FROM_ABI constexpr _Err&& error() && noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
- return std::move(__repr_.__union_.__unex_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!this->__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return std::move(this->__repr_.__union_.__unex_);
}
template <class _Up>
_LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& {
static_assert(is_copy_constructible_v<_Tp>, "value_type has to be copy constructible");
static_assert(is_convertible_v<_Up, _Tp>, "argument has to be convertible to value_type");
- return __repr_.__has_val_ ? __repr_.__union_.__val_ : static_cast<_Tp>(std::forward<_Up>(__v));
+ return this->__repr_.__has_val_ ? this->__repr_.__union_.__val_ : static_cast<_Tp>(std::forward<_Up>(__v));
}
template <class _Up>
_LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
static_assert(is_move_constructible_v<_Tp>, "value_type has to be move constructible");
static_assert(is_convertible_v<_Up, _Tp>, "argument has to be convertible to value_type");
- return __repr_.__has_val_ ? std::move(__repr_.__union_.__val_) : static_cast<_Tp>(std::forward<_Up>(__v));
+ return this->__repr_.__has_val_ ? std::move(this->__repr_.__union_.__val_) : static_cast<_Tp>(std::forward<_Up>(__v));
}
template <class _Up = _Err>
@@ -592,7 +870,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), __repr_.__union_.__val_);
+ return std::invoke(std::forward<_Func>(__f), this->__repr_.__union_.__val_);
}
return _Up(unexpect, error());
}
@@ -605,7 +883,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), __repr_.__union_.__val_);
+ return std::invoke(std::forward<_Func>(__f), this->__repr_.__union_.__val_);
}
return _Up(unexpect, error());
}
@@ -619,7 +897,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(__repr_.__union_.__val_));
+ return std::invoke(std::forward<_Func>(__f), std::move(this->__repr_.__union_.__val_));
}
return _Up(unexpect, std::move(error()));
}
@@ -633,7 +911,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(__repr_.__union_.__val_));
+ return std::invoke(std::forward<_Func>(__f), std::move(this->__repr_.__union_.__val_));
}
return _Up(unexpect, std::move(error()));
}
@@ -646,7 +924,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, __repr_.__union_.__val_);
+ return _Gp(in_place, this->__repr_.__union_.__val_);
}
return std::invoke(std::forward<_Func>(__f), error());
}
@@ -659,7 +937,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, __repr_.__union_.__val_);
+ return _Gp(in_place, this->__repr_.__union_.__val_);
}
return std::invoke(std::forward<_Func>(__f), error());
}
@@ -673,7 +951,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(__repr_.__union_.__val_));
+ return _Gp(in_place, std::move(this->__repr_.__union_.__val_));
}
return std::invoke(std::forward<_Func>(__f), std::move(error()));
}
@@ -687,7 +965,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(__repr_.__union_.__val_));
+ return _Gp(in_place, std::move(this->__repr_.__union_.__val_));
}
return std::invoke(std::forward<_Func>(__f), std::move(error()));
}
@@ -700,9 +978,9 @@ class expected {
return expected<_Up, _Err>(unexpect, error());
}
if constexpr (!is_void_v<_Up>) {
- return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), __repr_.__union_.__val_);
+ return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), this->__repr_.__union_.__val_);
} else {
- std::invoke(std::forward<_Func>(__f), __repr_.__union_.__val_);
+ std::invoke(std::forward<_Func>(__f), this->__repr_.__union_.__val_);
return expected<_Up, _Err>();
}
}
@@ -715,9 +993,9 @@ class expected {
return expected<_Up, _Err>(unexpect, error());
}
if constexpr (!is_void_v<_Up>) {
- return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), __repr_.__union_.__val_);
+ return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), this->__repr_.__union_.__val_);
} else {
- std::invoke(std::forward<_Func>(__f), __repr_.__union_.__val_);
+ std::invoke(std::forward<_Func>(__f), this->__repr_.__union_.__val_);
return expected<_Up, _Err>();
}
}
@@ -731,9 +1009,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(__repr_.__union_.__val_));
+ __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(this->__repr_.__union_.__val_));
} else {
- std::invoke(std::forward<_Func>(__f), std::move(__repr_.__union_.__val_));
+ std::invoke(std::forward<_Func>(__f), std::move(this->__repr_.__union_.__val_));
return expected<_Up, _Err>();
}
}
@@ -747,9 +1025,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(__repr_.__union_.__val_));
+ __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(this->__repr_.__union_.__val_));
} else {
- std::invoke(std::forward<_Func>(__f), std::move(__repr_.__union_.__val_));
+ std::invoke(std::forward<_Func>(__f), std::move(this->__repr_.__union_.__val_));
return expected<_Up, _Err>();
}
}
@@ -761,7 +1039,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, __repr_.__union_.__val_);
+ return expected<_Tp, _Gp>(in_place, this->__repr_.__union_.__val_);
}
return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error());
}
@@ -773,7 +1051,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, __repr_.__union_.__val_);
+ return expected<_Tp, _Gp>(in_place, this->__repr_.__union_.__val_);
}
return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error());
}
@@ -785,7 +1063,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(__repr_.__union_.__val_));
+ return expected<_Tp, _Gp>(in_place, std::move(this->__repr_.__union_.__val_));
}
return expected<_Tp, _Gp>(
__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error()));
@@ -798,7 +1076,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(__repr_.__union_.__val_));
+ return expected<_Tp, _Gp>(in_place, std::move(this->__repr_.__union_.__val_));
}
return expected<_Tp, _Gp>(
__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error()));
@@ -828,164 +1106,260 @@ class expected {
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __e) {
return !__x.__repr_.__has_val_ && static_cast<bool>(__x.__repr_.__union_.__unex_ == __e.error());
}
+};
+
+struct __expected_empty_t {};
+
+template <class _Union, class _OtherUnion>
+_LIBCPP_HIDE_FROM_ABI constexpr _Union __make_expected_void_union(bool __has_val, _OtherUnion&& __other)
+{
+ if (__has_val)
+ return _Union(in_place);
+ else
+ return _Union(unexpect, std::forward<_OtherUnion>(__other).__unex_);
+}
+
+template <class _ErrorType>
+union __expected_void_union_t {
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_union_t(in_place_t) : __empty_() {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_union_t(unexpect_t, _Args&&... __args)
+ : __unex_(std::forward<_Args>(__args)...) {}
+
+ template <class _Func, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_union_t(
+ __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 ~__expected_void_union_t()
+ requires(is_trivially_destructible_v<_ErrorType>)
+ = default;
+
+ // __expected_repr's destructor handles this
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_void_union_t() {}
+
+ // XXX: Why are those not [[no_unique_address]]?
+ __expected_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 __expected_void_union_t<_ErrorType> {
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_void_union_t(const __expected_void_union_t&) = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_void_union_t& operator=(const __expected_void_union_t&) = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_union_t(in_place_t) : __empty_() {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_union_t(unexpect_t, _Args&&... __args)
+ : __unex_(std::forward<_Args>(__args)...) {}
+
+ template <class _Func, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_union_t(
+ __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 ~__expected_void_union_t()
+ requires(is_trivially_destructible_v<_ErrorType>)
+ = default;
+
+ // __expected_repr's destructor handles this
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_void_union_t()
+ requires(!is_trivially_destructible_v<_ErrorType>)
+ {}
+
+ _LIBCPP_NO_UNIQUE_ADDRESS __expected_empty_t __empty_;
+ _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
+};
+
+template <class _Err, bool _StuffTail = false>
+struct __expected_void_repr {
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_repr() = delete;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_repr(in_place_t __tag)
+ : __union_(__tag), __has_val_(true) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_repr(unexpect_t __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_repr(
+ std::__expected_construct_unexpected_from_invoke_tag __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+ template <class _OtherUnion>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_repr(bool __has_val, _OtherUnion&& __other)
+ : __union_(__make_expected_void_union<__expected_void_union_t<_Err>>(__has_val,
+ std::forward<_OtherUnion>(__other))),
+ __has_val_(__has_val) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_void_repr(const __expected_void_repr&) = delete;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_void_repr(const __expected_void_repr&)
+ requires(is_copy_constructible_v<_Err> && is_trivially_copy_constructible_v<_Err>)
+ = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_void_repr(__expected_void_repr&&) = delete;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_void_repr(__expected_void_repr&&)
+ requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_void_repr& operator=(const __expected_void_repr&) = delete;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_void_repr()
+ requires(is_trivially_destructible_v<_Err>)
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_void_repr()
+ requires(!is_trivially_destructible_v<_Err>)
+ {
+ __destroy_union_member();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void __destroy_union()
+ requires(is_trivially_destructible_v<_Err>)
+ {
+ std::destroy_at(&__union_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void __destroy_union()
+ requires(!is_trivially_destructible_v<_Err>)
+ {
+ __destroy_union_member();
+ std::destroy_at(&__union_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void __construct_union(in_place_t)
+ {
+ std::construct_at(&__union_, in_place);
+ __has_val_ = true;
+ }
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr void __construct_union(unexpect_t, _Args&&... __args)
+ {
+ std::construct_at(&__union_, unexpect, std::forward<_Args>(__args)...);
+ __has_val_ = false;
+ }
private:
- template <class _ValueType, class _ErrorType>
- union __union_t {
- 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)...)) {}
-
- 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>)
- = default;
-
- // __repr's destructor handles this
- _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() {}
-
- _ValueType __val_;
- _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 _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;
-
- 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)...)) {}
-
- 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>)
- = default;
-
- // __repr's destructor handles this
- _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t()
- requires(!is_trivially_destructible_v<_ValueType> || !is_trivially_destructible_v<_ErrorType>)
- {}
-
- _LIBCPP_NO_UNIQUE_ADDRESS _ValueType __val_;
- _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_;
- };
-
- 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_(__tag, std::forward<_Args>(__args)...), __has_val_(true) {}
-
- template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(unexpect_t __tag, _Args&&... __args)
- : __union_(__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_(__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_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
-
- template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(bool __has_val, _Args&&... __args)
- : __union_(__has_val, std::forward<_Args>(__args)...), __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()
- 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>)
- {
- if (__has_val_) {
- std::destroy_at(std::addressof(__union_.__val_));
- } else {
- std::destroy_at(std::addressof(__union_.__unex_));
- }
+ template <class, class>
+ friend class expected;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void __destroy_union_member()
+ requires(!is_trivially_destructible_v<_Err>)
+ {
+ if (!__has_val_) {
+ std::destroy_at(std::addressof(__union_.__unex_));
}
+ }
+
+ __expected_void_union_t<_Err> __union_;
+ _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
+};
+
+template <class _Err>
+struct __expected_void_repr<_Err, true> {
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_repr() = delete;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_repr(in_place_t __tag)
+ : __union_(__tag), __has_val_(true) {}
+
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_repr(unexpect_t __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
- private:
- template <class, class>
- friend class expected;
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_repr(
+ std::__expected_construct_unexpected_from_invoke_tag __tag, _Args&&... __args)
+ : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
+
+ template <class _OtherUnion>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_repr(bool __has_val, _OtherUnion&& __other)
+ : __union_(__make_expected_void_union<__expected_void_union_t<_Err>>(__has_val,
+ std::forward<_OtherUnion>(__other))),
+ __has_val_(__has_val) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_void_repr(const __expected_void_repr&) = delete;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_void_repr(const __expected_void_repr&)
+ requires(is_copy_constructible_v<_Err> && is_trivially_copy_constructible_v<_Err>)
+ = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_void_repr(__expected_void_repr&&) = delete;
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_void_repr(__expected_void_repr&&)
+ requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __expected_void_repr& operator=(const __expected_void_repr&) = delete;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_void_repr()
+ requires(is_trivially_destructible_v<_Err>)
+ = default;
- _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_;
- _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
- };
+ _LIBCPP_HIDE_FROM_ABI constexpr ~__expected_void_repr()
+ requires(!is_trivially_destructible_v<_Err>)
+ {
+ if (!__has_val_) {
+ std::destroy_at(std::addressof(__union_.__unex_));
+ }
+ }
+
+private:
+ template <class, class>
+ friend class expected;
- __repr __repr_;
+ _LIBCPP_NO_UNIQUE_ADDRESS __expected_void_union_t<_Err> __union_;
+ _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
+};
+
+template <class _Err>
+struct __expected_void_base {
+protected:
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_base(_Args&&... __args)
+ : __repr_(std::forward<_Args>(__args)...) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void __destroy() {
+ __repr_.__destroy_union();
+ }
+
+ template <class _Tag, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr void __construct(_Tag __tag, _Args&&... __args)
+ {
+ __repr_.__construct_union(__tag, std::forward<_Args>(__args)...);
+ }
+
+ _LIBCPP_NO_UNIQUE_ADDRESS __expected_void_repr<_Err, false> __repr_;
+};
+
+template <class _Err>
+ requires (sizeof(__expected_void_repr<_Err, true>) == sizeof(__expected_void_union_t<_Err>))
+struct __expected_void_base<_Err> {
+protected:
+ template <class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __expected_void_base(_Args&&... __args)
+ : __repr_(std::forward<_Args>(__args)...) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void __destroy() {
+ std::destroy_at(&__repr_);
+ }
+
+ template <class _Tag, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI constexpr void __construct(_Tag __tag, _Args&&... __args)
+ {
+ std::construct_at(&__repr_, __tag, std::forward<_Args>(__args)...);
+ }
+
+ __expected_void_repr<_Err, true> __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");
@@ -1002,6 +1376,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;
@@ -1011,7 +1387,7 @@ class expected<_Tp, _Err> {
using rebind = expected<_Up, error_type>;
// [expected.void.ctor], constructors
- _LIBCPP_HIDE_FROM_ABI constexpr expected() noexcept : __repr_(in_place) {}
+ _LIBCPP_HIDE_FROM_ABI constexpr expected() noexcept : __base(in_place) {}
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete;
@@ -1022,7 +1398,7 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr expected(const expected& __rhs)
noexcept(is_nothrow_copy_constructible_v<_Err>) // strengthened
requires(is_copy_constructible_v<_Err> && !is_trivially_copy_constructible_v<_Err>)
- : __repr_(__rhs.__repr_.__has_val_, __rhs.__repr_.__union_) {}
+ : __base(__rhs.__repr_.__has_val_, __rhs.__repr_.__union_) {}
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&&)
requires(is_move_constructible_v<_Err> && is_trivially_move_constructible_v<_Err>)
@@ -1031,55 +1407,55 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI constexpr expected(expected&& __rhs)
noexcept(is_nothrow_move_constructible_v<_Err>)
requires(is_move_constructible_v<_Err> && !is_trivially_move_constructible_v<_Err>)
- : __repr_(__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)) {}
+ : __base(__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)) {}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, const _OtherErr&>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const expected<_Up, _OtherErr>& __rhs)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __repr_(__rhs.__repr_.__has_val_, __rhs.__repr_.__union_) {}
+ : __base(__rhs.__repr_.__has_val_, __rhs.__repr_.__union_) {}
template <class _Up, class _OtherErr>
requires __can_convert<_Up, _OtherErr, _OtherErr>::value
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherErr, _Err>)
expected(expected<_Up, _OtherErr>&& __rhs)
noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
- : __repr_(__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)) {}
+ : __base(__rhs.__repr_.__has_val_, std::move(__rhs.__repr_.__union_)) {}
template <class _OtherErr>
requires is_constructible_v<_Err, const _OtherErr&>
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)
expected(const unexpected<_OtherErr>& __unex)
noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened
- : __repr_(unexpect, __unex.error()) {}
+ : __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
- : __repr_(unexpect, std::move(__unex.error())) {}
+ : __base(unexpect, std::move(__unex.error())) {}
- _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __repr_(in_place) {}
+ _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
- : __repr_(unexpect, std::forward<_Args>(__args)...) {}
+ : __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
- : __repr_(unexpect, __il, std::forward<_Args>(__args)...) {}
+ : __base(unexpect, __il, std::forward<_Args>(__args)...) {}
private:
template <class _Func, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr explicit expected(
__expected_construct_unexpected_from_invoke_tag __tag, _Func&& __f, _Args&&... __args)
- : __repr_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...) {}
+ : __base(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...) {}
public:
// [expected.void.dtor], destructor
@@ -1090,17 +1466,16 @@ class expected<_Tp, _Err> {
// precondition: has_value()
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr void __reinit_expected(unexpect_t, _Args&&... __args) {
- std::destroy_at(&__repr_);
- auto __trans =
- std::__make_exception_guard([&] { std::construct_at(&__repr_, in_place); });
- std::construct_at(&__repr_, unexpect, std::forward<_Args>(__args)...);
+ this->__destroy();
+ auto __trans = std::__make_exception_guard([&] { this->__construct(in_place); });
+ this->__construct(unexpect, std::forward<_Args>(__args)...);
__trans.__complete();
}
// precondition: !has_value()
_LIBCPP_HIDE_FROM_ABI constexpr void __reinit_expected(in_place_t) {
- std::destroy_at(&__repr_);
- std::construct_at(&__repr_, in_place);
+ this->__destroy();
+ this->__construct(in_place);
}
public:
@@ -1111,7 +1486,7 @@ class expected<_Tp, _Err> {
noexcept(is_nothrow_copy_assignable_v<_Err> && is_nothrow_copy_constructible_v<_Err>) // strengthened
requires(is_copy_assignable_v<_Err> && is_copy_constructible_v<_Err>)
{
- if (__repr_.__has_val_) {
+ if (this->__repr_.__has_val_) {
if (!__rhs.__repr_.__has_val_) {
__reinit_expected(unexpect, __rhs.__repr_.__union_.__unex_);
}
@@ -1119,7 +1494,7 @@ class expected<_Tp, _Err> {
if (__rhs.__repr_.__has_val_) {
__reinit_expected(in_place);
} else {
- __repr_.__union_.__unex_ = __rhs.__repr_.__union_.__unex_;
+ this->__repr_.__union_.__unex_ = __rhs.__repr_.__union_.__unex_;
}
}
return *this;
@@ -1133,7 +1508,7 @@ class expected<_Tp, _Err> {
requires(is_move_assignable_v<_Err> &&
is_move_constructible_v<_Err>)
{
- if (__repr_.__has_val_) {
+ if (this->__repr_.__has_val_) {
if (!__rhs.__repr_.__has_val_) {
__reinit_expected(unexpect, std::move(__rhs.__repr_.__union_.__unex_));
}
@@ -1141,7 +1516,7 @@ class expected<_Tp, _Err> {
if (__rhs.__repr_.__has_val_) {
__reinit_expected(in_place);
} else {
- __repr_.__union_.__unex_ = std::move(__rhs.__repr_.__union_.__unex_);
+ this->__repr_.__union_.__unex_ = std::move(__rhs.__repr_.__union_.__unex_);
}
}
return *this;
@@ -1150,10 +1525,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 (__repr_.__has_val_) {
+ if (this->__repr_.__has_val_) {
__reinit_expected(unexpect, __un.error());
} else {
- __repr_.__union_.__unex_ = __un.error();
+ this->__repr_.__union_.__unex_ = __un.error();
}
return *this;
}
@@ -1161,16 +1536,16 @@ 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 (__repr_.__has_val_) {
+ if (this->__repr_.__has_val_) {
__reinit_expected(unexpect, std::move(__un.error()));
} else {
- __repr_.__union_.__unex_ = std::move(__un.error());
+ this->__repr_.__union_.__unex_ = std::move(__un.error());
}
return *this;
}
_LIBCPP_HIDE_FROM_ABI constexpr void emplace() noexcept {
- if (!__repr_.__has_val_) {
+ if (!this->__repr_.__has_val_) {
__reinit_expected(in_place);
}
}
@@ -1187,7 +1562,7 @@ class expected<_Tp, _Err> {
__with_err.__reinit_expected(in_place);
};
- if (__repr_.__has_val_) {
+ if (this->__repr_.__has_val_) {
if (!__rhs.__repr_.__has_val_) {
__swap_val_unex_impl(*this, __rhs);
}
@@ -1196,7 +1571,7 @@ class expected<_Tp, _Err> {
__swap_val_unex_impl(__rhs, *this);
} else {
using std::swap;
- swap(__repr_.__union_.__unex_, __rhs.__repr_.__union_.__unex_);
+ swap(this->__repr_.__union_.__unex_, __rhs.__repr_.__union_.__unex_);
}
}
}
@@ -1209,44 +1584,44 @@ class expected<_Tp, _Err> {
}
// [expected.void.obs], observers
- _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return __repr_.__has_val_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return this->__repr_.__has_val_; }
- _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __repr_.__has_val_; }
+ _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__repr_.__has_val_; }
_LIBCPP_HIDE_FROM_ABI constexpr void operator*() const noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->__repr_.__has_val_, "expected::operator* requires the expected to contain a value");
}
_LIBCPP_HIDE_FROM_ABI constexpr void value() const& {
- if (!__repr_.__has_val_) {
- std::__throw_bad_expected_access<_Err>(__repr_.__union_.__unex_);
+ if (!this->__repr_.__has_val_) {
+ std::__throw_bad_expected_access<_Err>(this->__repr_.__union_.__unex_);
}
}
_LIBCPP_HIDE_FROM_ABI constexpr void value() && {
- if (!__repr_.__has_val_) {
- std::__throw_bad_expected_access<_Err>(std::move(__repr_.__union_.__unex_));
+ if (!this->__repr_.__has_val_) {
+ std::__throw_bad_expected_access<_Err>(std::move(this->__repr_.__union_.__unex_));
}
}
_LIBCPP_HIDE_FROM_ABI constexpr const _Err& error() const& noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
- return __repr_.__union_.__unex_;
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!this->__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return this->__repr_.__union_.__unex_;
}
_LIBCPP_HIDE_FROM_ABI constexpr _Err& error() & noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
- return __repr_.__union_.__unex_;
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!this->__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return this->__repr_.__union_.__unex_;
}
_LIBCPP_HIDE_FROM_ABI constexpr const _Err&& error() const&& noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
- return std::move(__repr_.__union_.__unex_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!this->__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return std::move(this->__repr_.__union_.__unex_);
}
_LIBCPP_HIDE_FROM_ABI constexpr _Err&& error() && noexcept {
- _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!__repr_.__has_val_, "expected::error requires the expected to contain an error");
- return std::move(__repr_.__union_.__unex_);
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!this->__repr_.__has_val_, "expected::error requires the expected to contain an error");
+ return std::move(this->__repr_.__union_.__unex_);
}
template <class _Up = _Err>
@@ -1493,133 +1868,6 @@ class expected<_Tp, _Err> {
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y) {
return !__x.__repr_.__has_val_ && static_cast<bool>(__x.__repr_.__union_.__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(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;
-
- // __repr'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(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;
-
- // __repr'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_;
- };
-
- struct __repr {
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr() = delete;
-
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(in_place_t) : __union_(), __has_val_(true) {}
-
- template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(unexpect_t __tag, _Args&&... __args)
- : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
-
- template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(
- std::__expected_construct_unexpected_from_invoke_tag __tag, _Args&&... __args)
- : __union_(__tag, std::forward<_Args>(__args)...), __has_val_(false) {}
-
- template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __repr(bool __has_val, _Args&&... __args)
- : __union_(__has_val, std::forward<_Args>(__args)...), __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;
-
- _LIBCPP_HIDE_FROM_ABI constexpr __repr& operator=(const __repr&) = delete;
-
- _LIBCPP_HIDE_FROM_ABI constexpr ~__repr()
- requires(is_trivially_destructible_v<_Err>)
- = default;
-
- _LIBCPP_HIDE_FROM_ABI constexpr ~__repr()
- requires(!is_trivially_destructible_v<_Err>)
- {
- if (!__has_val_) {
- std::destroy_at(std::addressof(__union_.__unex_));
- }
- }
-
- private:
- template <class, class>
- friend class expected;
-
- _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_;
- _LIBCPP_NO_UNIQUE_ADDRESS bool __has_val_;
- };
-
- __repr __repr_;
};
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
index 8eeec70ff0e4243..a7ddd13e7930aa1 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp
@@ -13,6 +13,7 @@
// test [[no_unique_address]] is applied to the union
#include <expected>
+#include <optional>
#include <memory>
struct Empty {};
@@ -50,5 +51,22 @@ static_assert(sizeof(std::expected<Empty, BoolWithPadding>) ==
// In this case, there should be tail padding in the `expected` because `A`
// itself does _not_ have tail padding.
-// XXX
-// static_assert(sizeof(std::expected<A, A>) > std::__libcpp_datasizeof<std::expected<A, A>>::value);
+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 965e82a7b403465..16faddb280d3a33 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
@@ -48,6 +48,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@*:* {{{{(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@*:* {{{{(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 2129aa7b2405c7e..fe4ef4f1e0e2389 100644
--- a/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp
@@ -13,6 +13,7 @@
// test [[no_unique_address]] is applied to the union
#include <expected>
+#include <optional>
#include <memory>
struct Empty {};
@@ -44,5 +45,19 @@ static_assert(sizeof(std::expected<void, BoolWithPadding>) ==
// In this case, there should be tail padding in the `expected` because `A`
// itself does _not_ have tail padding.
-// XXX
-// static_assert(sizeof(std::expected<void, A>) > std::__libcpp_datasizeof<std::expected<void, A>>::value);
+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 09aa1332e980005..7687a42cb1a8655 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
@@ -48,6 +48,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@*:* {{{{(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@*:* {{union member {{.*}} has reference type {{.*}}}}
+ // expected-error-re@*:* {{call to deleted constructor of {{.*}}}}
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@*:* {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}}
More information about the libcxx-commits
mailing list