xref: /llvm-project/clang/test/Analysis/copy-elision.cpp (revision dddeec4becabf71d4067080bcc2c09a9e67c3025)
1 // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 \
2 // RUN:    -analyzer-config eagerly-assume=false -verify %s
3 // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 \
4 // RUN:    -analyzer-config eagerly-assume=false -verify %s
5 // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 \
6 // RUN:    -analyzer-config elide-constructors=false -DNO_ELIDE_FLAG              \
7 // RUN:    -analyzer-config eagerly-assume=false -verify=expected,no-elide %s
8 // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 \
9 // RUN:    -analyzer-config elide-constructors=false                              \
10 // RUN:    -analyzer-config eagerly-assume=false -verify %s
11 
12 // Copy elision always occurs in C++17, otherwise it's under
13 // an on-by-default flag.
14 #if __cplusplus >= 201703L
15   #define ELIDE 1
16 #else
17   #ifndef NO_ELIDE_FLAG
18     #define ELIDE 1
19   #endif
20 #endif
21 
22 void clang_analyzer_eval(bool);
23 void clang_analyzer_dump(int);
24 
25 namespace variable_functional_cast_crash {
26 
27 struct A {
28   A(int) {}
29 };
30 
31 void foo() {
32   A a = A(0);
33 }
34 
35 struct B {
36   A a;
37   B(): a(A(0)) {}
38 };
39 
40 } // namespace variable_functional_cast_crash
41 
42 
43 namespace ctor_initializer {
44 
45 struct S {
46   int x, y, z;
47 };
48 
49 struct T {
50   S s;
51   int w;
52   T(int w): s(), w(w) {}
53 };
54 
55 class C {
56   T t;
57 public:
58   C() : t(T(4)) {
59     S s = {1, 2, 3};
60     t.s = s;
61     // FIXME: Should be TRUE regardless of copy elision.
62     clang_analyzer_eval(t.w == 4);
63 #ifdef ELIDE
64     // expected-warning@-2{{TRUE}}
65 #else
66     // expected-warning@-4{{UNKNOWN}}
67 #endif
68   }
69 };
70 
71 
72 struct A {
73   int x;
74   A(): x(0) {}
75   ~A() {}
76 };
77 
78 struct B {
79   A a;
80   B() : a(A()) {}
81 };
82 
83 void foo() {
84   B b;
85   clang_analyzer_eval(b.a.x == 0); // expected-warning{{TRUE}}
86 }
87 
88 } // namespace ctor_initializer
89 
90 
91 namespace elision_on_ternary_op_branches {
92 class C1 {
93   int x;
94 public:
95   C1(int x): x(x) {}
96   int getX() const { return x; }
97   ~C1();
98 };
99 
100 class C2 {
101   int x;
102   int y;
103 public:
104   C2(int x, int y): x(x), y(y) {}
105   int getX() const { return x; }
106   int getY() const { return y; }
107   ~C2();
108 };
109 
110 void foo(int coin) {
111   C1 c1 = coin ? C1(1) : C1(2);
112   if (coin) {
113     clang_analyzer_eval(c1.getX() == 1); // expected-warning{{TRUE}}
114   } else {
115     clang_analyzer_eval(c1.getX() == 2); // expected-warning{{TRUE}}
116   }
117   C2 c2 = coin ? C2(3, 4) : C2(5, 6);
118   if (coin) {
119     clang_analyzer_eval(c2.getX() == 3); // expected-warning{{TRUE}}
120     clang_analyzer_eval(c2.getY() == 4); // expected-warning{{TRUE}}
121   } else {
122     clang_analyzer_eval(c2.getX() == 5); // expected-warning{{TRUE}}
123     clang_analyzer_eval(c2.getY() == 6); // expected-warning{{TRUE}}
124   }
125 }
126 } // namespace elision_on_ternary_op_branches
127 
128 
129 namespace address_vector_tests {
130 
131 template <typename T> struct AddressVector {
132   T *buf[20];
133   int len;
134 
135   AddressVector() : len(0) {}
136 
137   void push(T *t) {
138     buf[len] = t;
139     ++len;
140   }
141 };
142 
143 class ClassWithoutDestructor {
144   AddressVector<ClassWithoutDestructor> &v;
145 
146 public:
147   ClassWithoutDestructor(AddressVector<ClassWithoutDestructor> &v) : v(v) {
148     push();
149   }
150 
151   ClassWithoutDestructor(ClassWithoutDestructor &&c) : v(c.v) { push(); }
152   ClassWithoutDestructor(const ClassWithoutDestructor &c) : v(c.v) { push(); }
153 
154   void push() { v.push(this); }
155 };
156 
157 ClassWithoutDestructor make1(AddressVector<ClassWithoutDestructor> &v) {
158   return ClassWithoutDestructor(v);
159   // no-elide-warning@-1 {{Address of stack memory associated with temporary \
160 object of type 'ClassWithoutDestructor' is still \
161 referred to by the caller variable 'v' upon returning to the caller}}
162 }
163 ClassWithoutDestructor make2(AddressVector<ClassWithoutDestructor> &v) {
164   return make1(v);
165   // no-elide-warning@-1 {{Address of stack memory associated with temporary \
166 object of type 'ClassWithoutDestructor' is still \
167 referred to by the caller variable 'v' upon returning to the caller}}
168 }
169 ClassWithoutDestructor make3(AddressVector<ClassWithoutDestructor> &v) {
170   return make2(v);
171   // no-elide-warning@-1 {{Address of stack memory associated with temporary \
172 object of type 'ClassWithoutDestructor' is still \
173 referred to by the caller variable 'v' upon returning to the caller}}
174 }
175 
176 void testMultipleReturns() {
177   AddressVector<ClassWithoutDestructor> v;
178   ClassWithoutDestructor c = make3(v);
179 
180 #if ELIDE
181   clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
182   clang_analyzer_eval(v.buf[0] == &c); // expected-warning{{TRUE}}
183 #else
184   clang_analyzer_eval(v.len == 5); // expected-warning{{TRUE}}
185   clang_analyzer_eval(v.buf[0] != v.buf[1]); // expected-warning{{TRUE}}
186   clang_analyzer_eval(v.buf[1] != v.buf[2]); // expected-warning{{TRUE}}
187   clang_analyzer_eval(v.buf[2] != v.buf[3]); // expected-warning{{TRUE}}
188   clang_analyzer_eval(v.buf[3] != v.buf[4]); // expected-warning{{TRUE}}
189   clang_analyzer_eval(v.buf[4] == &c); // expected-warning{{TRUE}}
190 #endif
191 }
192 
193 void consume(ClassWithoutDestructor c) {
194   c.push();
195   // expected-warning@-1 {{Address of stack memory associated with local \
196 variable 'c' is still referred to by the caller variable 'v' upon returning \
197 to the caller}}
198 }
199 
200 void testArgumentConstructorWithoutDestructor() {
201   AddressVector<ClassWithoutDestructor> v;
202 
203   consume(make3(v));
204 
205 #if ELIDE
206   clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
207   clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
208 #else
209   clang_analyzer_eval(v.len == 6); // expected-warning{{TRUE}}
210   clang_analyzer_eval(v.buf[0] != v.buf[1]); // expected-warning{{TRUE}}
211   clang_analyzer_eval(v.buf[1] != v.buf[2]); // expected-warning{{TRUE}}
212   clang_analyzer_eval(v.buf[2] != v.buf[3]); // expected-warning{{TRUE}}
213   clang_analyzer_eval(v.buf[3] != v.buf[4]); // expected-warning{{TRUE}}
214   // We forced a push() in consume(), let's see if the address here matches
215   // the address during construction.
216   clang_analyzer_eval(v.buf[4] == v.buf[5]); // expected-warning{{TRUE}}
217 #endif
218 }
219 
220 class ClassWithDestructor {
221   AddressVector<ClassWithDestructor> &v;
222 
223 public:
224   ClassWithDestructor(AddressVector<ClassWithDestructor> &v) : v(v) {
225     push();
226   }
227 
228   ClassWithDestructor(ClassWithDestructor &&c) : v(c.v) { push(); }
229   ClassWithDestructor(const ClassWithDestructor &c) : v(c.v) { push(); }
230 
231   ~ClassWithDestructor() { push(); }
232 
233   void push() { v.push(this); }
234 };
235 
236 void testVariable() {
237   AddressVector<ClassWithDestructor> v;
238   {
239     ClassWithDestructor c = ClassWithDestructor(v);
240     // Check if the last destructor is an automatic destructor.
241     // A temporary destructor would have fired by now.
242 #if ELIDE
243     clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
244 #else
245     clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}
246 #endif
247   }
248 #if ELIDE
249   // 0. Construct the variable.
250   // 1. Destroy the variable.
251   clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
252   clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
253 #else
254   // 0. Construct the temporary.
255   // 1. Construct the variable.
256   // 2. Destroy the temporary.
257   // 3. Destroy the variable.
258   clang_analyzer_eval(v.len == 4); // expected-warning{{TRUE}}
259   clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}
260   clang_analyzer_eval(v.buf[1] == v.buf[3]); // expected-warning{{TRUE}}
261 #endif
262 }
263 
264 struct TestCtorInitializer {
265   ClassWithDestructor c;
266   TestCtorInitializer(AddressVector<ClassWithDestructor> &refParam)
267     : c(ClassWithDestructor(refParam)) {}
268 };
269 
270 void testCtorInitializer() {
271   AddressVector<ClassWithDestructor> v;
272   {
273     TestCtorInitializer t(v);
274     // no-elide-warning@-1 {{Address of stack memory associated with temporary \
275 object of type 'ClassWithDestructor' is still referred \
276 to by the caller variable 'v' upon returning to the caller}}
277     // Check if the last destructor is an automatic destructor.
278     // A temporary destructor would have fired by now.
279 #if ELIDE
280     clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
281 #else
282     clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}
283 #endif
284   }
285 #if ELIDE
286   // 0. Construct the member variable.
287   // 1. Destroy the member variable.
288   clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
289   clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
290 #else
291   // 0. Construct the temporary.
292   // 1. Construct the member variable.
293   // 2. Destroy the temporary.
294   // 3. Destroy the member variable.
295   clang_analyzer_eval(v.len == 4); // expected-warning{{TRUE}}
296   clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}
297   clang_analyzer_eval(v.buf[1] == v.buf[3]); // expected-warning{{TRUE}}
298 #endif
299 }
300 
301 
302 ClassWithDestructor make1(AddressVector<ClassWithDestructor> &v) {
303   return ClassWithDestructor(v);
304   // no-elide-warning@-1 {{Address of stack memory associated with temporary \
305 object of type 'ClassWithDestructor' is still referred \
306 to by the caller variable 'v' upon returning to the caller}}
307 }
308 ClassWithDestructor make2(AddressVector<ClassWithDestructor> &v) {
309   return make1(v);
310   // no-elide-warning@-1 {{Address of stack memory associated with temporary \
311 object of type 'ClassWithDestructor' is still referred \
312 to by the caller variable 'v' upon returning to the caller}}
313 }
314 ClassWithDestructor make3(AddressVector<ClassWithDestructor> &v) {
315   return make2(v);
316   // no-elide-warning@-1 {{Address of stack memory associated with temporary \
317 object of type 'ClassWithDestructor' is still referred \
318 to by the caller variable 'v' upon returning to the caller}}
319 }
320 
321 void testMultipleReturnsWithDestructors() {
322   AddressVector<ClassWithDestructor> v;
323   {
324     ClassWithDestructor c = make3(v);
325     // Check if the last destructor is an automatic destructor.
326     // A temporary destructor would have fired by now.
327 #if ELIDE
328     clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}
329 #else
330     clang_analyzer_eval(v.len == 9); // expected-warning{{TRUE}}
331 #endif
332   }
333 
334 #if ELIDE
335   // 0. Construct the variable. Yes, constructor in make1() constructs
336   //    the variable 'c'.
337   // 1. Destroy the variable.
338   clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
339   clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
340 #else
341   // 0. Construct the temporary in make1().
342   // 1. Construct the temporary in make2().
343   // 2. Destroy the temporary in make1().
344   // 3. Construct the temporary in make3().
345   // 4. Destroy the temporary in make2().
346   // 5. Construct the temporary here.
347   // 6. Destroy the temporary in make3().
348   // 7. Construct the variable.
349   // 8. Destroy the temporary here.
350   // 9. Destroy the variable.
351   clang_analyzer_eval(v.len == 10); // expected-warning{{TRUE}}
352   clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}
353   clang_analyzer_eval(v.buf[1] == v.buf[4]); // expected-warning{{TRUE}}
354   clang_analyzer_eval(v.buf[3] == v.buf[6]); // expected-warning{{TRUE}}
355   clang_analyzer_eval(v.buf[5] == v.buf[8]); // expected-warning{{TRUE}}
356   clang_analyzer_eval(v.buf[7] == v.buf[9]); // expected-warning{{TRUE}}
357 #endif
358 }
359 
360 void consume(ClassWithDestructor c) {
361   c.push();
362   // expected-warning@-1 {{Address of stack memory associated with local \
363 variable 'c' is still referred to by the caller variable 'v' upon returning \
364 to the caller}}
365 }
366 
367 void testArgumentConstructorWithDestructor() {
368   AddressVector<ClassWithDestructor> v;
369 
370   consume(make3(v));
371 
372 #if ELIDE
373   // 0. Construct the argument.
374   // 1. Forced push() in consume().
375   // 2. Destroy the argument.
376   clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}
377   clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
378   clang_analyzer_eval(v.buf[1] == v.buf[2]); // expected-warning{{TRUE}}
379 #else
380   // 0. Construct the temporary in make1().
381   // 1. Construct the temporary in make2().
382   // 2. Destroy the temporary in make1().
383   // 3. Construct the temporary in make3().
384   // 4. Destroy the temporary in make2().
385   // 5. Construct the temporary here.
386   // 6. Destroy the temporary in make3().
387   // 7. Construct the argument.
388   // 8. Forced push() in consume().
389   // 9. Destroy the argument. Notice the reverse order!
390   // 10. Destroy the temporary here.
391   clang_analyzer_eval(v.len == 11); // expected-warning{{TRUE}}
392   clang_analyzer_eval(v.buf[0] == v.buf[2]); // expected-warning{{TRUE}}
393   clang_analyzer_eval(v.buf[1] == v.buf[4]); // expected-warning{{TRUE}}
394   clang_analyzer_eval(v.buf[3] == v.buf[6]); // expected-warning{{TRUE}}
395   clang_analyzer_eval(v.buf[5] == v.buf[10]); // expected-warning{{TRUE}}
396   clang_analyzer_eval(v.buf[7] == v.buf[8]); // expected-warning{{TRUE}}
397   clang_analyzer_eval(v.buf[8] == v.buf[9]); // expected-warning{{TRUE}}
398 #endif
399 }
400 
401 struct Foo {
402   Foo(Foo **q) {
403     *q = this;
404   }
405 };
406 
407 Foo make1(Foo **r) {
408   return Foo(r);
409   // no-elide-warning@-1 {{Address of stack memory associated with temporary \
410 object of type 'Foo' is still referred to by the caller \
411 variable 'z' upon returning to the caller}}
412 }
413 
414 void test_copy_elision() {
415   Foo *z;
416   // If the copy elided, 'z' points to 'tmp', otherwise it's a dangling pointer.
417   Foo tmp = make1(&z);
418   (void)tmp;
419 }
420 
421 } // namespace address_vector_tests
422 
423 namespace arg_directly_from_return_in_loop {
424 
425 struct Result {
426   int value;
427 };
428 
429 Result create() {
430   return Result{10};
431 }
432 
433 int accessValue(Result r) {
434   return r.value;
435 }
436 
437 void test() {
438   for (int i = 0; i < 3; ++i) {
439     int v = accessValue(create());
440     if (i == 0) {
441       clang_analyzer_dump(v); // expected-warning {{10 S32b}}
442     } else {
443       clang_analyzer_dump(v); // expected-warning {{10 S32b}}
444                               // was {{reg_${{[0-9]+}}<int r.value> }} for C++11
445     }
446   }
447 }
448 
449 } // namespace arg_directly_from_return_in_loop
450