Skip to content

Commit 0a12742

Browse files
jiixyjtru
authored andcommitted
[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 (cherry picked from commit 134c915)
1 parent 42f8800 commit 0a12742

30 files changed

+540
-164
lines changed

libcxx/include/__expected/expected.h

Lines changed: 85 additions & 97 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
@@ -76,6 +76,13 @@ constexpr bool test() {
7676
assert(e.value() == 10);
7777
}
7878

79+
// TailClobberer
80+
{
81+
std::expected<TailClobberer<0>, bool> e(std::unexpect);
82+
e.emplace();
83+
assert(e.has_value());
84+
}
85+
7986
return true;
8087
}
8188

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
@@ -31,6 +31,7 @@
3131

3232
#include "MoveOnly.h"
3333
#include "test_macros.h"
34+
#include "../../types.h"
3435

3536
// Test Constraints:
3637
static_assert(
@@ -93,13 +94,17 @@ constexpr bool test() {
9394
assert(m.get() == 0);
9495
}
9596

97+
// TailClobberer
98+
{
99+
std::expected<TailClobberer<0>, bool> e(std::in_place, {1, 2, 3});
100+
assert(e.has_value());
101+
}
102+
96103
return true;
97104
}
98105

99106
void testException() {
100107
#ifndef TEST_HAS_NO_EXCEPTIONS
101-
struct Except {};
102-
103108
struct Throwing {
104109
Throwing(std::initializer_list<int>, int) { throw Except{}; };
105110
};

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: 13 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>);
@@ -67,24 +68,27 @@ struct CopyOnly {
6768
friend constexpr bool operator==(const CopyOnly& mi, int ii) { return mi.i == ii; }
6869
};
6970

70-
template <class T>
71+
struct BaseError {};
72+
struct DerivedError : BaseError {};
73+
74+
template <class T, class E = int>
7175
constexpr void testInt() {
72-
std::expected<T, int> e(5);
76+
std::expected<T, E> e(5);
7377
assert(e.has_value());
7478
assert(e.value() == 5);
7579
}
7680

77-
template <class T>
81+
template <class T, class E = int>
7882
constexpr void testLValue() {
7983
T t(5);
80-
std::expected<T, int> e(t);
84+
std::expected<T, E> e(t);
8185
assert(e.has_value());
8286
assert(e.value() == 5);
8387
}
8488

85-
template <class T>
89+
template <class T, class E = int>
8690
constexpr void testRValue() {
87-
std::expected<T, int> e(T(5));
91+
std::expected<T, E> e(T(5));
8892
assert(e.has_value());
8993
assert(e.value() == 5);
9094
}
@@ -93,10 +97,13 @@ constexpr bool test() {
9397
testInt<int>();
9498
testInt<CopyOnly>();
9599
testInt<MoveOnly>();
100+
testInt<TailClobberer<0>, bool>();
96101
testLValue<int>();
97102
testLValue<CopyOnly>();
103+
testLValue<TailClobberer<0>, bool>();
98104
testRValue<int>();
99105
testRValue<MoveOnly>();
106+
testRValue<TailClobberer<0>, bool>();
100107

101108
// Test default template argument.
102109
// Without it, the template parameter cannot be deduced from an initializer list
@@ -129,8 +136,6 @@ constexpr bool test() {
129136

130137
void testException() {
131138
#ifndef TEST_HAS_NO_EXCEPTIONS
132-
struct Except {};
133-
134139
struct Throwing {
135140
Throwing(int) { throw Except{}; };
136141
};

0 commit comments

Comments
 (0)