Clang Project

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