Clang Project

clang_source_code/test/Analysis/lifetime-extension.cpp
1// RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -analyzer-config eagerly-assume=false %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 -analyzer-config eagerly-assume=false %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 -analyzer-config eagerly-assume=false %s
4// RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -DMOVES -verify -analyzer-config eagerly-assume=false %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 -analyzer-config eagerly-assume=false %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 -analyzer-config eagerly-assume=false %s
7
8// Note: The C++17 run-lines don't -verify yet - it is a no-crash test.
9
10void clang_analyzer_eval(bool);
11void clang_analyzer_checkInlined(bool);
12
13namespace pr17001_call_wrong_destructor {
14bool x;
15struct A {
16  int *a;
17  A() {}
18  ~A() {}
19};
20struct B : public A {
21  B() {}
22  ~B() { x = true; }
23};
24
25void 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
33namespace pr19539_crash_on_destroying_an_integer {
34struct 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
44void 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
64namespace maintain_original_object_address_on_lifetime_extension {
65class C {
66  C **after, **before;
67
68public:
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
85void 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
98void f2() {
99  C *after, *before;
100  {
101    C c = C(1, &after, &before);
102  }
103  clang_analyzer_eval(after == before); // expected-warning{{TRUE}}
104}
105
106void f3(bool coin) {
107  C *after, *before;
108  {
109    const C &c = coin ? C(true, &after, &before) : C(false, &after, &before);
110  }
111  clang_analyzer_eval(after == before);
112#ifdef TEMPORARIES
113  // expected-warning@-2{{TRUE}}
114#else
115  // expected-warning@-4{{UNKNOWN}}
116#endif
117}
118
119void f4(bool coin) {
120  C *after, *before;
121  {
122    // no-crash
123    const C &c = C(coin, &after, &before) ?: C(false, &after, &before);
124  }
125  // FIXME: Add support for lifetime extension through binary conditional
126  // operator. Ideally also add support for the binary conditional operator in
127  // C++. Because for now it calls the constructor for the condition twice.
128  if (coin) {
129    // FIXME: Should not warn.
130    clang_analyzer_eval(after == before);
131#ifdef TEMPORARIES
132  // expected-warning@-2{{The left operand of '==' is a garbage value}}
133#else
134  // expected-warning@-4{{UNKNOWN}}
135#endif
136  } else {
137    // FIXME: Should be TRUE.
138    clang_analyzer_eval(after == before);
139#ifdef TEMPORARIES
140  // expected-warning@-2{{FALSE}}
141#else
142  // expected-warning@-4{{UNKNOWN}}
143#endif
144  }
145}
146
147void f5() {
148  C *after, *before;
149  {
150    const bool &x = C(true, &after, &before).x; // no-crash
151  }
152  clang_analyzer_eval(after == before);
153#ifdef TEMPORARIES
154  // expected-warning@-2{{TRUE}}
155#else
156  // expected-warning@-4{{UNKNOWN}}
157#endif
158}
159
160struct A { // A is an aggregate.
161  const C &c;
162};
163
164void f6() {
165  C *after, *before;
166  {
167    A a{C(true, &after, &before)};
168  }
169  // FIXME: Should be TRUE. Should not warn about garbage value.
170  clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
171}
172
173void f7() {
174  C *after, *before;
175  {
176    A a = {C(true, &after, &before)};
177  }
178  // FIXME: Should be TRUE. Should not warn about garbage value.
179  clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
180}
181
182void f8() {
183  C *after, *before;
184  {
185    A a[2] = {C(false, nullptr, nullptr), C(true, &after, &before)};
186  }
187  // FIXME: Should be TRUE. Should not warn about garbage value.
188  clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
189}
190} // end namespace maintain_original_object_address_on_lifetime_extension
191
192namespace maintain_original_object_address_on_move {
193class C {
194  int *x;
195
196public:
197  C() : x(nullptr) {}
198  C(int *x) : x(x) {}
199  C(const C &c) = delete;
200  C(C &&c) : x(c.x) { c.x = nullptr; }
201  C &operator=(C &&c) {
202    x = c.x;
203    c.x = nullptr;
204    return *this;
205  }
206  ~C() {
207    // This was triggering the division by zero warning in f1() and f2():
208    // Because move-elision materialization was incorrectly causing the object
209    // to be relocated from one address to another before move, but destructor
210    // was operating on the old address, it was still thinking that 'x' is set.
211    if (x)
212      *x = 0;
213  }
214};
215
216void f1() {
217  int x = 1;
218  // &x is replaced with nullptr in move-constructor before the temporary dies.
219  C c = C(&x);
220  // Hence x was not set to 0 yet.
221  1 / x; // no-warning
222}
223void f2() {
224  int x = 1;
225  C c;
226  // &x is replaced with nullptr in move-assignment before the temporary dies.
227  c = C(&x);
228  // Hence x was not set to 0 yet.
229  1 / x; // no-warning
230}
231} // end namespace maintain_original_object_address_on_move
232
233namespace maintain_address_of_copies {
234class C;
235
236struct AddressVector {
237  C *buf[10];
238  int len;
239
240  AddressVector() : len(0) {}
241
242  void push(C *c) {
243    buf[len] = c;
244    ++len;
245  }
246};
247
248class C {
249  AddressVector &v;
250
251public:
252  C(AddressVector &v) : v(v) { v.push(this); }
253  ~C() { v.push(this); }
254
255#ifdef MOVES
256  C(C &&c) : v(c.v) { v.push(this); }
257#endif
258
259  // Note how return-statements prefer move-constructors when available.
260  C(const C &c) : v(c.v) {
261#ifdef MOVES
262    clang_analyzer_checkInlined(false); // no-warning
263#else
264    v.push(this);
265#endif
266  } // no-warning
267
268  static C make(AddressVector &v) { return C(v); }
269};
270
271void f1() {
272  AddressVector v;
273  {
274    C c = C(v);
275  }
276  // 0. Construct variable 'c' (copy/move elided).
277  // 1. Destroy variable 'c'.
278  clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
279  clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
280}
281
282void f2() {
283  AddressVector v;
284  {
285    const C &c = C::make(v);
286  }
287  // 0. Construct the return value of make() (copy/move elided) and
288  //    lifetime-extend it directly via reference 'c',
289  // 1. Destroy the temporary lifetime-extended by 'c'.
290  clang_analyzer_eval(v.len == 2);
291  clang_analyzer_eval(v.buf[0] == v.buf[1]);
292#ifdef TEMPORARIES
293  // expected-warning@-3{{TRUE}}
294  // expected-warning@-3{{TRUE}}
295#else
296  // expected-warning@-6{{UNKNOWN}}
297  // expected-warning@-6{{UNKNOWN}}
298#endif
299}
300
301void f3() {
302  AddressVector v;
303  {
304    C &&c = C::make(v);
305  }
306  // 0. Construct the return value of make() (copy/move elided) and
307  //    lifetime-extend it directly via reference 'c',
308  // 1. Destroy the temporary lifetime-extended by 'c'.
309  clang_analyzer_eval(v.len == 2);
310  clang_analyzer_eval(v.buf[0] == v.buf[1]);
311#ifdef TEMPORARIES
312  // expected-warning@-3{{TRUE}}
313  // expected-warning@-3{{TRUE}}
314#else
315  // expected-warning@-6{{UNKNOWN}}
316  // expected-warning@-6{{UNKNOWN}}
317#endif
318}
319
320C doubleMake(AddressVector &v) {
321  return C::make(v);
322}
323
324void f4() {
325  AddressVector v;
326  {
327    C c = doubleMake(v);
328  }
329  // 0. Construct variable 'c' (all copies/moves elided),
330  // 1. Destroy variable 'c'.
331  clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}
332  clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}
333}
334} // end namespace maintain_address_of_copies
335