-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path0001-Fix-non-copyable-class-s-optional-move.patch
183 lines (165 loc) · 6.32 KB
/
0001-Fix-non-copyable-class-s-optional-move.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
From 0e5ad0b98f821a995443aee087f085734e2fc6df Mon Sep 17 00:00:00 2001
From: Hidehiko Abe <[email protected]>
Date: Fri, 19 Jan 2018 23:50:24 +0000
Subject: [PATCH] Fix non-copyable class's optional move.
BUG=784732
TEST=Ran base_unittests -gtest_filter=*Optional*
Change-Id: Ibb5d7cc5d62deacdba7f811f5a7b83c1c58c3907
Reviewed-on: https://chromium-review.googlesource.com/855976
Reviewed-by: danakj <[email protected]>
Commit-Queue: Hidehiko Abe <[email protected]>
Cr-Commit-Position: refs/heads/master@{#530663}
---
base/optional.h | 24 +++++++++++++--
base/optional_unittest.cc | 74 +++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 89 insertions(+), 9 deletions(-)
diff --git a/base/optional.h b/base/optional.h
index c763acf824ee..5a50eb455be6 100644
--- a/base/optional.h
+++ b/base/optional.h
@@ -45,6 +45,15 @@ struct OptionalStorageBase {
// When T is not trivially destructible we must call its
// destructor before deallocating its memory.
+ // Note that this hides the (implicitly declared) move constructor, which
+ // would be used for constexpr move constructor in OptionalStorage<T>.
+ // It is needed iff T is trivially move constructible. However, the current
+ // is_trivially_{copy,move}_constructible implementation requires
+ // is_trivially_destructible (which looks a bug, cf:
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452 and
+ // http://cplusplus.github.io/LWG/lwg-active.html#2116), so it is not
+ // necessary for this case at the moment. Please see also the destructor
+ // comment in "is_trivially_destructible = true" specialization below.
~OptionalStorageBase() {
if (!is_null_)
value_.~T();
@@ -78,9 +87,18 @@ struct OptionalStorageBase<T, true /* trivially destructible */> {
: is_null_(false), value_(std::forward<Args>(args)...) {}
// When T is trivially destructible (i.e. its destructor does nothing) there
- // is no need to call it. Explicitly defaulting the destructor means it's not
- // user-provided. Those two together make this destructor trivial.
- ~OptionalStorageBase() = default;
+ // is no need to call it. Implicitly defined destructor is trivial, because
+ // both members (bool and union containing only variants which are trivially
+ // destructible) are trivially destructible.
+ // Explicitly-defaulted destructor is also trivial, but do not use it here,
+ // because it hides the implicit move constructor. It is needed to implement
+ // constexpr move constructor in OptionalStorage iff T is trivially move
+ // constructible. Note that, if T is trivially move constructible, the move
+ // constructor of OptionalStorageBase<T> is also implicitly defined and it is
+ // trivially move constructor. If T is not trivially move constructible,
+ // "not declaring move constructor without destructor declaration" here means
+ // "delete move constructor", which works because any move constructor of
+ // OptionalStorage will not refer to it in that case.
template <class... Args>
void Init(Args&&... args) {
diff --git a/base/optional_unittest.cc b/base/optional_unittest.cc
index 91e63e75d0db..7cc05ef2987d 100644
--- a/base/optional_unittest.cc
+++ b/base/optional_unittest.cc
@@ -115,6 +115,35 @@ class DeletedDefaultConstructor {
int foo_;
};
+class DeletedCopyConstructor {
+ public:
+ explicit DeletedCopyConstructor(int foo) : foo_(foo) {}
+ DeletedCopyConstructor(const DeletedCopyConstructor&) = delete;
+ DeletedCopyConstructor(DeletedCopyConstructor&&) = default;
+
+ int foo() const { return foo_; }
+
+ private:
+ int foo_;
+};
+
+class NonTriviallyDestructibleDeletedCopyConstructor {
+ public:
+ explicit NonTriviallyDestructibleDeletedCopyConstructor(int foo)
+ : foo_(foo) {}
+ NonTriviallyDestructibleDeletedCopyConstructor(
+ const NonTriviallyDestructibleDeletedCopyConstructor&) = delete;
+ NonTriviallyDestructibleDeletedCopyConstructor(
+ NonTriviallyDestructibleDeletedCopyConstructor&&) = default;
+
+ ~NonTriviallyDestructibleDeletedCopyConstructor() {}
+
+ int foo() const { return foo_; }
+
+ private:
+ int foo_;
+};
+
class DeleteNewOperators {
public:
void* operator new(size_t) = delete;
@@ -168,6 +197,15 @@ TEST(OptionalTest, CopyConstructor) {
EXPECT_EQ(first, other);
}
+ {
+ const Optional<std::string> first("foo");
+ Optional<std::string> other(first);
+
+ EXPECT_TRUE(other);
+ EXPECT_EQ(other.value(), "foo");
+ EXPECT_EQ(first, other);
+ }
+
{
Optional<TestObject> first(TestObject(3, 0.1));
Optional<TestObject> other(first);
@@ -210,33 +248,57 @@ TEST(OptionalTest, MoveConstructor) {
constexpr Optional<float> first(0.1f);
constexpr Optional<float> second(std::move(first));
- EXPECT_TRUE(second);
+ EXPECT_TRUE(second.has_value());
EXPECT_EQ(second.value(), 0.1f);
- EXPECT_TRUE(first);
+ EXPECT_TRUE(first.has_value());
}
{
Optional<std::string> first("foo");
Optional<std::string> second(std::move(first));
- EXPECT_TRUE(second);
+ EXPECT_TRUE(second.has_value());
EXPECT_EQ("foo", second.value());
- EXPECT_TRUE(first);
+ EXPECT_TRUE(first.has_value());
}
{
Optional<TestObject> first(TestObject(3, 0.1));
Optional<TestObject> second(std::move(first));
- EXPECT_TRUE(!!second);
+ EXPECT_TRUE(second.has_value());
EXPECT_EQ(TestObject::State::MOVE_CONSTRUCTED, second->state());
EXPECT_TRUE(TestObject(3, 0.1) == second.value());
- EXPECT_TRUE(!!first);
+ EXPECT_TRUE(first.has_value());
EXPECT_EQ(TestObject::State::MOVED_FROM, first->state());
}
+
+ // Even if copy constructor is deleted, move constructor needs to work.
+ // Note that it couldn't be constexpr.
+ {
+ Optional<DeletedCopyConstructor> first(in_place, 42);
+ Optional<DeletedCopyConstructor> second(std::move(first));
+
+ EXPECT_TRUE(second.has_value());
+ EXPECT_EQ(42, second->foo());
+
+ EXPECT_TRUE(first.has_value());
+ }
+
+ {
+ Optional<NonTriviallyDestructibleDeletedCopyConstructor> first(in_place,
+ 42);
+ Optional<NonTriviallyDestructibleDeletedCopyConstructor> second(
+ std::move(first));
+
+ EXPECT_TRUE(second.has_value());
+ EXPECT_EQ(42, second->foo());
+
+ EXPECT_TRUE(first.has_value());
+ }
}
TEST(OptionalTest, MoveValueConstructor) {
--
2.14.3