1 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify %s
2 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true -DTEMPORARIES -verify %s
3 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++17 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true -DTEMPORARIES %s
4 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -DMOVES -verify %s
5 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true -DTEMPORARIES -DMOVES -verify %s
6 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++17 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true -DTEMPORARIES -DMOVES %s
7 
8 // Note: The C++17 run-lines don't -verify yet - it is a no-crash test.
9 
10 void clang_analyzer_eval(bool);
11 void clang_analyzer_checkInlined(bool);
12 
13 namespace pr17001_call_wrong_destructor {
14 bool x;
15 struct A {
16   int *a;
17   A() {}
18   ~A() {}
19 };
20 struct B : public A {
21   B() {}
22   ~B() { x = true; }
23 };
24 
25 void f() {
26   {
27     const A &a = B();
28   }
29   clang_analyzer_eval(x); // expected-warning{{TRUE}}
30 }
31 } // end namespace pr17001_call_wrong_destructor
32 
33 namespace pr19539_crash_on_destroying_an_integer {
34 struct A {
35   int i;
36   int j[2];
37   A() : i(1) {
38     j[0] = 2;
39     j[1] = 3;
40   }
41   ~A() {}
42 };
43 
44 void f() {
45   const int &x = A().i; // no-crash
46   const int &y = A().j[1]; // no-crash
47   const int &z = (A().j[1], A().j[0]); // no-crash
48 
49   clang_analyzer_eval(x == 1);
50   clang_analyzer_eval(y == 3);
51   clang_analyzer_eval(z == 2);
52 #ifdef TEMPORARIES
53   // expected-warning@-4{{TRUE}}
54   // expected-warning@-4{{TRUE}}
55   // expected-warning@-4{{TRUE}}
56 #else
57   // expected-warning@-8{{UNKNOWN}}
58   // expected-warning@-8{{UNKNOWN}}
59   // expected-warning@-8{{UNKNOWN}}
60 #endif
61 }
62 } // end namespace pr19539_crash_on_destroying_an_integer
63 
64 namespace maintain_original_object_address_on_lifetime_extension {
65 class C {
66   C **after, **before;
67 
68 public:
69   bool x;
70 
71   C(bool x, C **after, C **before) : x(x), after(after), before(before) {
72     *before = this;
73   }
74 
75   // Don't track copies in our tests.
76   C(const C &c) : x(c.x), after(nullptr), before(nullptr) {}
77 
78   ~C() { if (after) *after = this; }
79 
80   operator bool() const { return x; }
81 
82   static C make(C **after, C **before) { return C(false, after, before); }
83 };
84 
85 void f1() {
86   C *after, *before;
87   {
88     const C &c = C(true, &after, &before);
89   }
90   clang_analyzer_eval(after == before);
91 #ifdef TEMPORARIES
92   // expected-warning@-2{{TRUE}}
93 #else
94   // expected-warning@-4{{UNKNOWN}}
95 #endif
96 }
97 
98 void f2() {
99   C *after, *before;
100   C c = C(1, &after, &before);
101   clang_analyzer_eval(after == before);
102 #ifdef TEMPORARIES
103   // expected-warning@-2{{TRUE}}
104 #else
105   // expected-warning@-4{{UNKNOWN}}
106 #endif
107 }
108 
109 void f3(bool coin) {
110   C *after, *before;
111   {
112     const C &c = coin ? C(true, &after, &before) : C(false, &after, &before);
113   }
114   clang_analyzer_eval(after == before);
115 #ifdef TEMPORARIES
116   // expected-warning@-2{{TRUE}}
117 #else
118   // expected-warning@-4{{UNKNOWN}}
119 #endif
120 }
121 
122 void f4(bool coin) {
123   C *after, *before;
124   {
125     // no-crash
126     const C &c = C(coin, &after, &before) ?: C(false, &after, &before);
127   }
128   // FIXME: Add support for lifetime extension through binary conditional
129   // operator. Ideally also add support for the binary conditional operator in
130   // C++. Because for now it calls the constructor for the condition twice.
131   if (coin) {
132     clang_analyzer_eval(after == before);
133 #ifdef TEMPORARIES
134   // expected-warning@-2{{The left operand of '==' is a garbage value}}
135 #else
136   // expected-warning@-4{{UNKNOWN}}
137 #endif
138   } else {
139     clang_analyzer_eval(after == before);
140 #ifdef TEMPORARIES
141     // Seems to work at the moment, but also seems accidental.
142     // Feel free to break.
143   // expected-warning@-4{{TRUE}}
144 #else
145   // expected-warning@-6{{UNKNOWN}}
146 #endif
147   }
148 }
149 
150 void f5() {
151   C *after, *before;
152   {
153     const bool &x = C(true, &after, &before).x; // no-crash
154   }
155   clang_analyzer_eval(after == before);
156 #ifdef TEMPORARIES
157   // expected-warning@-2{{TRUE}}
158 #else
159   // expected-warning@-4{{UNKNOWN}}
160 #endif
161 }
162 
163 struct A { // A is an aggregate.
164   const C &c;
165 };
166 
167 void f6() {
168   C *after, *before;
169   {
170     A a{C(true, &after, &before)};
171   }
172   // FIXME: Should be TRUE. Should not warn about garbage value.
173   clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
174 }
175 
176 void f7() {
177   C *after, *before;
178   {
179     A a = {C(true, &after, &before)};
180   }
181   // FIXME: Should be TRUE. Should not warn about garbage value.
182   clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
183 }
184 
185 void f8() {
186   C *after, *before;
187   {
188     A a[2] = {C(false, nullptr, nullptr), C(true, &after, &before)};
189   }
190   // FIXME: Should be TRUE. Should not warn about garbage value.
191   clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
192 }
193 } // end namespace maintain_original_object_address_on_lifetime_extension
194 
195 namespace maintain_original_object_address_on_move {
196 class C {
197   int *x;
198 
199 public:
200   C() : x(nullptr) {}
201   C(int *x) : x(x) {}
202   C(const C &c) = delete;
203   C(C &&c) : x(c.x) { c.x = nullptr; }
204   C &operator=(C &&c) {
205     x = c.x;
206     c.x = nullptr;
207     return *this;
208   }
209   ~C() {
210     // This was triggering the division by zero warning in f1() and f2():
211     // Because move-elision materialization was incorrectly causing the object
212     // to be relocated from one address to another before move, but destructor
213     // was operating on the old address, it was still thinking that 'x' is set.
214     if (x)
215       *x = 0;
216   }
217 };
218 
219 void f1() {
220   int x = 1;
221   // &x is replaced with nullptr in move-constructor before the temporary dies.
222   C c = C(&x);
223   // Hence x was not set to 0 yet.
224   1 / x; // no-warning
225 }
226 void f2() {
227   int x = 1;
228   C c;
229   // &x is replaced with nullptr in move-assignment before the temporary dies.
230   c = C(&x);
231   // Hence x was not set to 0 yet.
232   1 / x; // no-warning
233 }
234 } // end namespace maintain_original_object_address_on_move
235 
236 namespace maintain_address_of_copies {
237 class C;
238 
239 struct AddressVector {
240   C *buf[10];
241   int len;
242 
243   AddressVector() : len(0) {}
244 
245   void push(C *c) {
246     buf[len] = c;
247     ++len;
248   }
249 };
250 
251 class C {
252   AddressVector &v;
253 
254 public:
255   C(AddressVector &v) : v(v) { v.push(this); }
256   ~C() { v.push(this); }
257 
258 #ifdef MOVES
259   C(C &&c) : v(c.v) { v.push(this); }
260 #endif
261 
262   // Note how return-statements prefer move-constructors when available.
263   C(const C &c) : v(c.v) {
264 #ifdef MOVES
265     clang_analyzer_checkInlined(false); // no-warning
266 #else
267     v.push(this);
268 #endif
269   } // no-warning
270 
271   static C make(AddressVector &v) { return C(v); }
272 };
273 
274 void f1() {
275   AddressVector v;
276   {
277     C c = C(v);
278   }
279   // 0. Create the original temporary and lifetime-extend it into variable 'c'
280   //    construction argument.
281   // 1. Construct variable 'c' (elidable copy/move).
282   // 2. Destroy the temporary.
283   // 3. Destroy variable 'c'.
284   clang_analyzer_eval(v.len == 4);
285   clang_analyzer_eval(v.buf[0] == v.buf[2]);
286   clang_analyzer_eval(v.buf[1] == v.buf[3]);
287 #ifdef TEMPORARIES
288   // expected-warning@-4{{TRUE}}
289   // expected-warning@-4{{TRUE}}
290   // expected-warning@-4{{TRUE}}
291 #else
292   // expected-warning@-8{{UNKNOWN}}
293   // expected-warning@-8{{UNKNOWN}}
294   // expected-warning@-8{{UNKNOWN}}
295 #endif
296 }
297 
298 void f2() {
299   AddressVector v;
300   {
301     const C &c = C::make(v);
302   }
303   // 0. Construct the original temporary within make(),
304   // 1. Construct the return value of make() (elidable copy/move) and
305   //    lifetime-extend it via reference 'c',
306   // 2. Destroy the temporary within make(),
307   // 3. Destroy the temporary lifetime-extended by 'c'.
308   clang_analyzer_eval(v.len == 4);
309   clang_analyzer_eval(v.buf[0] == v.buf[2]);
310   clang_analyzer_eval(v.buf[1] == v.buf[3]);
311 #ifdef TEMPORARIES
312   // expected-warning@-4{{TRUE}}
313   // expected-warning@-4{{TRUE}}
314   // expected-warning@-4{{TRUE}}
315 #else
316   // expected-warning@-8{{UNKNOWN}}
317   // expected-warning@-8{{UNKNOWN}}
318   // expected-warning@-8{{UNKNOWN}}
319 #endif
320 }
321 
322 void f3() {
323   AddressVector v;
324   {
325     C &&c = C::make(v);
326   }
327   // 0. Construct the original temporary within make(),
328   // 1. Construct the return value of make() (elidable copy/move) and
329   //    lifetime-extend it via reference 'c',
330   // 2. Destroy the temporary within make(),
331   // 3. Destroy the temporary lifetime-extended by 'c'.
332   clang_analyzer_eval(v.len == 4);
333   clang_analyzer_eval(v.buf[0] == v.buf[2]);
334   clang_analyzer_eval(v.buf[1] == v.buf[3]);
335 #ifdef TEMPORARIES
336   // expected-warning@-4{{TRUE}}
337   // expected-warning@-4{{TRUE}}
338   // expected-warning@-4{{TRUE}}
339 #else
340   // expected-warning@-8{{UNKNOWN}}
341   // expected-warning@-8{{UNKNOWN}}
342   // expected-warning@-8{{UNKNOWN}}
343 #endif
344 }
345 
346 C doubleMake(AddressVector &v) {
347   return C::make(v);
348 }
349 
350 void f4() {
351   AddressVector v;
352   {
353     C c = doubleMake(v);
354   }
355   // 0. Construct the original temporary within make(),
356   // 1. Construct the return value of make() (elidable copy/move) and
357   //    lifetime-extend it into the return value constructor argument within
358   //    doubleMake(),
359   // 2. Destroy the temporary within make(),
360   // 3. Construct the return value of doubleMake() (elidable copy/move) and
361   //    lifetime-extend it into the variable 'c' constructor argument,
362   // 4. Destroy the return value of make(),
363   // 5. Construct variable 'c' (elidable copy/move),
364   // 6. Destroy the return value of doubleMake(),
365   // 7. Destroy variable 'c'.
366   clang_analyzer_eval(v.len == 8);
367   clang_analyzer_eval(v.buf[0] == v.buf[2]);
368   clang_analyzer_eval(v.buf[1] == v.buf[4]);
369   clang_analyzer_eval(v.buf[3] == v.buf[6]);
370   clang_analyzer_eval(v.buf[5] == v.buf[7]);
371 #ifdef TEMPORARIES
372   // expected-warning@-6{{TRUE}}
373   // expected-warning@-6{{TRUE}}
374   // expected-warning@-6{{TRUE}}
375   // expected-warning@-6{{TRUE}}
376   // expected-warning@-6{{TRUE}}
377 #else
378   // expected-warning@-12{{UNKNOWN}}
379   // expected-warning@-12{{UNKNOWN}}
380   // expected-warning@-12{{UNKNOWN}}
381   // expected-warning@-12{{UNKNOWN}}
382   // expected-warning@-12{{UNKNOWN}}
383 #endif
384 }
385 } // end namespace maintain_address_of_copies
386