more-dtors-cfg-output.cpp
9.74 KB
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
// RUN: rm -f %t.14 %t.2a
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++14 -DCXX2A=0 -fblocks -Wall -Wno-unused -Werror %s > %t.14 2>&1
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -std=c++2a -DCXX2A=1 -fblocks -Wall -Wno-unused -Werror %s > %t.2a 2>&1
// RUN: FileCheck --input-file=%t.14 -check-prefixes=CHECK,CXX14 -implicit-check-not=destructor %s
// RUN: FileCheck --input-file=%t.2a -check-prefixes=CHECK,CXX2A -implicit-check-not=destructor %s
int puts(const char *);
struct Foo {
Foo() = delete;
#if CXX2A
// Guarantee that the elided examples are actually elided by deleting the
// copy constructor.
Foo(const Foo &) = delete;
#else
// No elision support, so we need a copy constructor.
Foo(const Foo &);
#endif
~Foo();
};
struct TwoFoos {
Foo foo1, foo2;
~TwoFoos();
};
Foo get_foo();
struct Bar {
Bar();
Bar(const Bar &);
~Bar();
Bar &operator=(const Bar &);
};
Bar get_bar();
struct TwoBars {
Bar foo1, foo2;
~TwoBars();
};
// Start of tests:
void elided_assign() {
Foo x = get_foo();
}
// CHECK: void elided_assign()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Implicit destructor)
void nonelided_assign() {
Bar x = (const Bar &)get_bar();
}
// CHECK: void nonelided_assign()
// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
// CHECK: ~Bar() (Temporary object destructor)
// CHECK: ~Bar() (Implicit destructor)
void elided_paren_init() {
Foo x(get_foo());
}
// CHECK: void elided_paren_init()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Implicit destructor)
void nonelided_paren_init() {
Bar x((const Bar &)get_bar());
}
// CHECK: void nonelided_paren_init()
// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
// CHECK: ~Bar() (Temporary object destructor)
// CHECK: ~Bar() (Implicit destructor)
void elided_brace_init() {
Foo x{get_foo()};
}
// CHECK: void elided_brace_init()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Implicit destructor)
void nonelided_brace_init() {
Bar x{(const Bar &)get_bar()};
}
// CHECK: void nonelided_brace_init()
// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
// CHECK: ~Bar() (Temporary object destructor)
// CHECK: ~Bar() (Implicit destructor)
void elided_lambda_capture_init() {
// The copy from get_foo() into the lambda should be elided. Should call
// the lambda's destructor, but not ~Foo() separately.
// (This syntax is C++14 'generalized lambda captures'.)
auto z = [x=get_foo()]() {};
}
// CHECK: void elided_lambda_capture_init()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~(lambda at {{.*}})() (Temporary object destructor)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~(lambda at {{.*}})() (Implicit destructor)
void nonelided_lambda_capture_init() {
// Should call the lambda's destructor as well as ~Bar() for the temporary.
auto z = [x((const Bar &)get_bar())]() {};
}
// CHECK: void nonelided_lambda_capture_init()
// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
// CXX14: ~(lambda at {{.*}})() (Temporary object destructor)
// CHECK: ~Bar() (Temporary object destructor)
// CHECK: ~(lambda at {{.*}})() (Implicit destructor)
Foo elided_return_stmt_expr() {
// Two copies, both elided in C++17.
return ({ get_foo(); });
}
// CHECK: Foo elided_return_stmt_expr()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
void elided_stmt_expr() {
// One copy, elided in C++17.
({ get_foo(); });
}
// CHECK: void elided_stmt_expr()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Temporary object destructor)
void elided_stmt_expr_multiple_stmts() {
// Make sure that only the value returned out of a statement expression is
// elided.
({ get_bar(); get_foo(); });
}
// CHECK: void elided_stmt_expr_multiple_stmts()
// CHECK: ~Bar() (Temporary object destructor)
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Temporary object destructor)
void unelided_stmt_expr() {
({ (const Bar &)get_bar(); });
}
// CHECK: void unelided_stmt_expr()
// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
// CHECK: ~Bar() (Temporary object destructor)
// CHECK: ~Bar() (Temporary object destructor)
void elided_aggregate_init() {
TwoFoos x{get_foo(), get_foo()};
}
// CHECK: void elided_aggregate_init()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~TwoFoos() (Implicit destructor)
void nonelided_aggregate_init() {
TwoBars x{(const Bar &)get_bar(), (const Bar &)get_bar()};
}
// CHECK: void nonelided_aggregate_init()
// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
// CHECK: (CXXConstructExpr{{.*}}, struct Bar)
// CHECK: ~Bar() (Temporary object destructor)
// CHECK: ~Bar() (Temporary object destructor)
// CHECK: ~TwoBars() (Implicit destructor)
TwoFoos return_aggregate_init() {
return TwoFoos{get_foo(), get_foo()};
}
// CHECK: TwoFoos return_aggregate_init()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~TwoFoos() (Temporary object destructor)
// CXX14: ~Foo() (Temporary object destructor)
// CXX14: ~Foo() (Temporary object destructor)
void lifetime_extended() {
const Foo &x = (get_foo(), get_foo());
puts("one destroyed before, one destroyed after");
}
// CHECK: void lifetime_extended()
// CHECK: ~Foo() (Temporary object destructor)
// CHECK: one destroyed before, one destroyed after
// CHECK: ~Foo() (Implicit destructor)
void not_lifetime_extended() {
Foo x = (get_foo(), get_foo());
puts("one destroyed before, one destroyed after");
}
// CHECK: void not_lifetime_extended()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CHECK: ~Foo() (Temporary object destructor)
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: one destroyed before, one destroyed after
// CHECK: ~Foo() (Implicit destructor)
void compound_literal() {
(void)(Bar[]){{}, {}};
}
// CHECK: void compound_literal()
// CHECK: (CXXConstructExpr, struct Bar)
// CHECK: (CXXConstructExpr, struct Bar)
// CHECK: ~Bar [2]() (Temporary object destructor)
Foo elided_return() {
return get_foo();
}
// CHECK: Foo elided_return()
// CXX14: (CXXConstructExpr{{.*}}, struct Foo)
// CXX14: ~Foo() (Temporary object destructor)
auto elided_return_lambda() {
return [x=get_foo()]() {};
}
// CHECK: (lambda at {{.*}}) elided_return_lambda()
// CXX14: (CXXConstructExpr{{.*}}, class (lambda at {{.*}}))
// CXX14: ~(lambda at {{.*}})() (Temporary object destructor)
// CXX14: ~Foo() (Temporary object destructor)
void const_auto_obj() {
const Bar bar;
}
// CHECK: void const_auto_obj()
// CHECK: .~Bar() (Implicit destructor)
void has_default_arg(Foo foo = get_foo());
void test_default_arg() {
// FIXME: This emits a destructor but no constructor. Search CFG.cpp for
// 'PR13385' for details.
has_default_arg();
}
// CHECK: void test_default_arg()
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Temporary object destructor)
struct DefaultArgInCtor {
DefaultArgInCtor(Foo foo = get_foo());
~DefaultArgInCtor();
};
void default_ctor_with_default_arg() {
// FIXME: Default arguments are mishandled in two ways:
// - The constructor is not emitted at all (not specific to arrays; see fixme
// in CFG.cpp that mentions PR13385).
// - The destructor is emitted once, even though the default argument will be
// constructed and destructed once per array element.
// Ideally, the CFG would expand array constructions into a loop that
// constructs each array element, allowing default argument
// constructor/destructor calls to be correctly placed inside the loop.
DefaultArgInCtor qux[3];
}
// CHECK: void default_ctor_with_default_arg()
// CHECK: CXXConstructExpr, {{.*}}, struct DefaultArgInCtor [3]
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Temporary object destructor)
// CHECK: .~DefaultArgInCtor [3]() (Implicit destructor)
void new_default_ctor_with_default_arg(long count) {
// Same problems as above.
new DefaultArgInCtor[count];
}
// CHECK: void new_default_ctor_with_default_arg(long count)
// CHECK: CXXConstructExpr, {{.*}}, struct DefaultArgInCtor []
// CXX14: ~Foo() (Temporary object destructor)
// CHECK: ~Foo() (Temporary object destructor)
#if CXX2A
// Boilerplate needed to test co_return:
namespace std::experimental {
template <typename Promise>
struct coroutine_handle {
static coroutine_handle from_address(void *) noexcept;
};
}
struct TestPromise {
TestPromise initial_suspend();
TestPromise final_suspend() noexcept;
bool await_ready() noexcept;
void await_suspend(const std::experimental::coroutine_handle<TestPromise> &) noexcept;
void await_resume() noexcept;
Foo return_value(const Bar &);
Bar get_return_object();
void unhandled_exception();
};
namespace std::experimental {
template <typename Ret, typename... Args>
struct coroutine_traits;
template <>
struct coroutine_traits<Bar> {
using promise_type = TestPromise;
};
}
Bar coreturn() {
co_return get_bar();
// This expands to something like:
// promise.return_value(get_bar());
// get_bar() is passed by reference to return_value() and is then destroyed;
// there is no equivalent of RVO. TestPromise::return_value also returns a
// Foo, which should be immediately destroyed.
// FIXME: The generated CFG completely ignores get_return_object().
}
// CXX2A: Bar coreturn()
// CXX2A: ~Foo() (Temporary object destructor)
// CXX2A: ~Bar() (Temporary object destructor)
#endif