Skip to content

Commit 134c915

Browse files
authored
[libc++] Fix UB in <expected> related to "has value" flag (#68552) (#68733)
The calls to std::construct_at might overwrite the previously set __has_value_ flag in the case where the flag is overlapping with the actual value or error being stored (since we use [[no_unique_address]]). To fix this issue, this patch ensures that we initialize the __has_value_ flag after we call std::construct_at. Fixes #68552
1 parent 849f963 commit 134c915

30 files changed

+534
-162
lines changed

libcxx/include/__expected/expected.h

Lines changed: 82 additions & 95 deletions
Large diffs are not rendered by default.

libcxx/test/std/utilities/expected/expected.expected/assign/emplace.intializer_list.pass.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ constexpr bool test() {
8181
assert(e.value().i == 10);
8282
}
8383

84+
// TailClobberer
85+
{
86+
std::expected<TailClobberer<0>, bool> e(std::unexpect);
87+
auto list = {4, 5, 6};
88+
e.emplace(list);
89+
assert(e.has_value());
90+
}
91+
8492
return true;
8593
}
8694

libcxx/test/std/utilities/expected/expected.expected/assign/emplace.pass.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ constexpr bool test() {
7373
assert(e.value() == 10);
7474
}
7575

76+
// TailClobberer
77+
{
78+
std::expected<TailClobberer<0>, bool> e(std::unexpect);
79+
e.emplace();
80+
assert(e.has_value());
81+
}
82+
7683
return true;
7784
}
7885

libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.copy.pass.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include <utility>
4646

4747
#include "test_macros.h"
48+
#include "../../types.h"
4849

4950
// Test Constraints:
5051
template <class T1, class Err1, class T2, class Err2>
@@ -161,13 +162,19 @@ constexpr bool test() {
161162
assert(e1.error() == 5);
162163
}
163164

165+
// convert TailClobberer
166+
{
167+
const std::expected<TailClobbererNonTrivialMove<0>, char> e1;
168+
std::expected<TailClobberer<0>, char> e2 = e1;
169+
assert(e2.has_value());
170+
assert(e1.has_value());
171+
}
172+
164173
return true;
165174
}
166175

167176
void testException() {
168177
#ifndef TEST_HAS_NO_EXCEPTIONS
169-
struct Except {};
170-
171178
struct ThrowingInt {
172179
ThrowingInt(int) { throw Except{}; }
173180
};

libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.move.pass.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646

4747
#include "MoveOnly.h"
4848
#include "test_macros.h"
49+
#include "../../types.h"
4950

5051
// Test Constraints:
5152
template <class T1, class Err1, class T2, class Err2>
@@ -160,13 +161,19 @@ constexpr bool test() {
160161
assert(e1.error().get() == 0);
161162
}
162163

164+
// convert TailClobberer
165+
{
166+
std::expected<TailClobbererNonTrivialMove<0>, char> e1;
167+
std::expected<TailClobberer<0>, char> e2 = std::move(e1);
168+
assert(e2.has_value());
169+
assert(e1.has_value());
170+
}
171+
163172
return true;
164173
}
165174

166175
void testException() {
167176
#ifndef TEST_HAS_NO_EXCEPTIONS
168-
struct Except {};
169-
170177
struct ThrowingInt {
171178
ThrowingInt(int) { throw Except{}; }
172179
};

libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.copy.pass.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <utility>
3131

3232
#include "test_macros.h"
33+
#include "../../types.h"
3334

3435
struct NonCopyable {
3536
NonCopyable(const NonCopyable&) = delete;
@@ -93,13 +94,26 @@ constexpr bool test() {
9394
assert(!e2.has_value());
9495
assert(e2.error() == 5);
9596
}
97+
98+
// copy TailClobberer as value
99+
{
100+
const std::expected<TailClobberer<0>, bool> e1;
101+
auto e2 = e1;
102+
assert(e2.has_value());
103+
}
104+
105+
// copy TailClobberer as error
106+
{
107+
const std::expected<bool, TailClobberer<1>> e1(std::unexpect);
108+
auto e2 = e1;
109+
assert(!e2.has_value());
110+
}
111+
96112
return true;
97113
}
98114

99115
void testException() {
100116
#ifndef TEST_HAS_NO_EXCEPTIONS
101-
struct Except {};
102-
103117
struct Throwing {
104118
Throwing() = default;
105119
Throwing(const Throwing&) { throw Except{}; }

libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.default.pass.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <type_traits>
2323

2424
#include "test_macros.h"
25+
#include "../../types.h"
2526

2627
struct NoDedefaultCtor {
2728
NoDedefaultCtor() = delete;
@@ -45,20 +46,20 @@ constexpr void testDefaultCtor() {
4546

4647
template <class T>
4748
constexpr void testTypes() {
49+
testDefaultCtor<T, bool>();
4850
testDefaultCtor<T, int>();
4951
testDefaultCtor<T, NoDedefaultCtor>();
5052
}
5153

5254
constexpr bool test() {
5355
testTypes<int>();
5456
testTypes<MyInt>();
57+
testTypes<TailClobberer<0>>();
5558
return true;
5659
}
5760

5861
void testException() {
5962
#ifndef TEST_HAS_NO_EXCEPTIONS
60-
struct Except {};
61-
6263
struct Throwing {
6364
Throwing() { throw Except{}; };
6465
};

libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace.pass.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#include "MoveOnly.h"
2828
#include "test_macros.h"
29+
#include "../../types.h"
2930

3031
// Test Constraints:
3132
static_assert(std::is_constructible_v<std::expected<int, int>, std::in_place_t>);
@@ -54,24 +55,24 @@ struct CopyOnly {
5455
friend constexpr bool operator==(const CopyOnly& mi, int ii) { return mi.i == ii; }
5556
};
5657

57-
template <class T>
58+
template <class T, class E = int>
5859
constexpr void testInt() {
59-
std::expected<T, int> e(std::in_place, 5);
60+
std::expected<T, E> e(std::in_place, 5);
6061
assert(e.has_value());
6162
assert(e.value() == 5);
6263
}
6364

64-
template <class T>
65+
template <class T, class E = int>
6566
constexpr void testLValue() {
6667
T t(5);
67-
std::expected<T, int> e(std::in_place, t);
68+
std::expected<T, E> e(std::in_place, t);
6869
assert(e.has_value());
6970
assert(e.value() == 5);
7071
}
7172

72-
template <class T>
73+
template <class T, class E = int>
7374
constexpr void testRValue() {
74-
std::expected<T, int> e(std::in_place, T(5));
75+
std::expected<T, E> e(std::in_place, T(5));
7576
assert(e.has_value());
7677
assert(e.value() == 5);
7778
}
@@ -80,10 +81,13 @@ constexpr bool test() {
8081
testInt<int>();
8182
testInt<CopyOnly>();
8283
testInt<MoveOnly>();
84+
testInt<TailClobberer<0>, bool>();
8385
testLValue<int>();
8486
testLValue<CopyOnly>();
87+
testLValue<TailClobberer<0>, bool>();
8588
testRValue<int>();
8689
testRValue<MoveOnly>();
90+
testRValue<TailClobberer<0>, bool>();
8791

8892
// no arg
8993
{
@@ -111,8 +115,6 @@ constexpr bool test() {
111115

112116
void testException() {
113117
#ifndef TEST_HAS_NO_EXCEPTIONS
114-
struct Except {};
115-
116118
struct Throwing {
117119
Throwing(int) { throw Except{}; };
118120
};

libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.inplace_init_list.pass.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include "MoveOnly.h"
3030
#include "test_macros.h"
31+
#include "../../types.h"
3132

3233
// Test Constraints:
3334
static_assert(
@@ -90,13 +91,17 @@ constexpr bool test() {
9091
assert(m.get() == 0);
9192
}
9293

94+
// TailClobberer
95+
{
96+
std::expected<TailClobberer<0>, bool> e(std::in_place, {1, 2, 3});
97+
assert(e.has_value());
98+
}
99+
93100
return true;
94101
}
95102

96103
void testException() {
97104
#ifndef TEST_HAS_NO_EXCEPTIONS
98-
struct Except {};
99-
100105
struct Throwing {
101106
Throwing(std::initializer_list<int>, int) { throw Except{}; };
102107
};

libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.move.pass.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <utility>
3333

3434
#include "test_macros.h"
35+
#include "../../types.h"
3536

3637
struct NonMovable {
3738
NonMovable(NonMovable&&) = delete;
@@ -112,13 +113,28 @@ constexpr bool test() {
112113
assert(e2.error() == 5);
113114
assert(!e1.has_value());
114115
}
116+
117+
// move TailClobbererNonTrivialMove as value
118+
{
119+
std::expected<TailClobbererNonTrivialMove<0>, bool> e1;
120+
auto e2 = std::move(e1);
121+
assert(e2.has_value());
122+
assert(e1.has_value());
123+
}
124+
125+
// move TailClobbererNonTrivialMove as error
126+
{
127+
std::expected<bool, TailClobbererNonTrivialMove<1>> e1(std::unexpect);
128+
auto e2 = std::move(e1);
129+
assert(!e2.has_value());
130+
assert(!e1.has_value());
131+
}
132+
115133
return true;
116134
}
117135

118136
void testException() {
119137
#ifndef TEST_HAS_NO_EXCEPTIONS
120-
struct Except {};
121-
122138
struct Throwing {
123139
Throwing() = default;
124140
Throwing(Throwing&&) { throw Except{}; }

libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
#include "MoveOnly.h"
3131
#include "test_macros.h"
32+
#include "../../types.h"
3233

3334
// Test Constraints:
3435
static_assert(std::is_constructible_v<std::expected<int, int>, int>);
@@ -70,24 +71,24 @@ struct CopyOnly {
7071
struct BaseError {};
7172
struct DerivedError : BaseError {};
7273

73-
template <class T>
74+
template <class T, class E = int>
7475
constexpr void testInt() {
75-
std::expected<T, int> e(5);
76+
std::expected<T, E> e(5);
7677
assert(e.has_value());
7778
assert(e.value() == 5);
7879
}
7980

80-
template <class T>
81+
template <class T, class E = int>
8182
constexpr void testLValue() {
8283
T t(5);
83-
std::expected<T, int> e(t);
84+
std::expected<T, E> e(t);
8485
assert(e.has_value());
8586
assert(e.value() == 5);
8687
}
8788

88-
template <class T>
89+
template <class T, class E = int>
8990
constexpr void testRValue() {
90-
std::expected<T, int> e(T(5));
91+
std::expected<T, E> e(T(5));
9192
assert(e.has_value());
9293
assert(e.value() == 5);
9394
}
@@ -96,10 +97,13 @@ constexpr bool test() {
9697
testInt<int>();
9798
testInt<CopyOnly>();
9899
testInt<MoveOnly>();
100+
testInt<TailClobberer<0>, bool>();
99101
testLValue<int>();
100102
testLValue<CopyOnly>();
103+
testLValue<TailClobberer<0>, bool>();
101104
testRValue<int>();
102105
testRValue<MoveOnly>();
106+
testRValue<TailClobberer<0>, bool>();
103107

104108
// Test default template argument.
105109
// Without it, the template parameter cannot be deduced from an initializer list
@@ -153,8 +157,6 @@ constexpr bool test() {
153157

154158
void testException() {
155159
#ifndef TEST_HAS_NO_EXCEPTIONS
156-
struct Except {};
157-
158160
struct Throwing {
159161
Throwing(int) { throw Except{}; };
160162
};

0 commit comments

Comments
 (0)