[PATCH] D44124: [analyzer] Support destruction and lifetime-extension of inlined function return values.

2018-03-12 Thread Phabricator via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rC327345: [analyzer] Destroy and lifetime-extend inlined 
function return values properly. (authored by dergachev, committed by ).

Repository:
  rC Clang

https://reviews.llvm.org/D44124

Files:
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  test/Analysis/lifetime-extension.cpp

Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -197,16 +197,19 @@
   return MRMgr.getCXXTempObjectRegion(CE, LCtx);
 }
 case ConstructionContext::ReturnedValueKind: {
-  // TODO: We should construct into a CXXBindTemporaryExpr or a
-  // MaterializeTemporaryExpr around the call-expression on the previous
-  // stack frame. Currently we re-bind the temporary to the correct region
-  // later, but that's not semantically correct. This of course does not
-  // apply when we're in the top frame. But if we are in an inlined
-  // function, we should be able to take the call-site CFG element,
-  // and it should contain (but right now it wouldn't) some sort of
-  // construction context that'd give us the right temporary expression.
+  // The temporary is to be managed by the parent stack frame.
+  // So build it in the parent stack frame if we're not in the
+  // top frame of the analysis.
+  // TODO: What exactly happens when we are? Does the temporary object live
+  // long enough in the region store in this case? Would checkers think
+  // that this object immediately goes out of scope?
+  const LocationContext *TempLCtx = LCtx;
+  if (const LocationContext *CallerLCtx =
+  LCtx->getCurrentStackFrame()->getParent()) {
+TempLCtx = CallerLCtx;
+  }
   CallOpts.IsTemporaryCtorOrDtor = true;
-  return MRMgr.getCXXTempObjectRegion(CE, LCtx);
+  return MRMgr.getCXXTempObjectRegion(CE, TempLCtx);
 }
 }
   }
@@ -262,32 +265,52 @@
   assert(C || getCurrentCFGElement().getAs());
   const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr;
 
+  bool IsReturnedIntoParentStackFrame = false;
   const CXXBindTemporaryExpr *BTE = nullptr;
   const MaterializeTemporaryExpr *MTE = nullptr;
 
   switch (CE->getConstructionKind()) {
   case CXXConstructExpr::CK_Complete: {
 Target = getRegionForConstructedObject(CE, Pred, CC, CallOpts);
 
-// In case of temporary object construction, extract data necessary for
-// destruction and lifetime extension.
-if (const auto *TCC =
-dyn_cast_or_null(CC)) {
-  assert(CallOpts.IsTemporaryCtorOrDtor);
-  assert(!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion);
-  if (AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG()) {
-BTE = TCC->getCXXBindTemporaryExpr();
-MTE = TCC->getMaterializedTemporaryExpr();
-if (!BTE) {
-  // FIXME: lifetime extension for temporaries without destructors
-  // is not implemented yet.
-  MTE = nullptr;
+if (CC) {
+  // In case of temporary object construction, extract data necessary for
+  // destruction and lifetime extension.
+  const auto *TCC = dyn_cast(CC);
+
+  // If the temporary is being returned from the function, it will be
+  // destroyed or lifetime-extended in the caller stack frame.
+  if (const auto *RCC = dyn_cast(CC)) {
+const StackFrameContext *SFC = LCtx->getCurrentStackFrame();
+assert(SFC);
+if (SFC->getParent()) {
+  IsReturnedIntoParentStackFrame = true;
+  const CFGElement &CallElem =
+  (*SFC->getCallSiteBlock())[SFC->getIndex()];
+  if (auto RTCElem = CallElem.getAs()) {
+TCC = cast(
+RTCElem->getConstructionContext());
+  }
 }
-if (MTE && MTE->getStorageDuration() != SD_FullExpression) {
-  // If the temporary is lifetime-extended, don't save the BTE,
-  // because we don't need a temporary destructor, but an automatic
-  // destructor.
-  BTE = nullptr;
+  }
+
+  if (TCC) {
+assert(CallOpts.IsTemporaryCtorOrDtor);
+assert(!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion);
+if (AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG()) {
+  BTE = TCC->getCXXBindTemporaryExpr();
+  MTE = TCC->getMaterializedTemporaryExpr();
+  if (!BTE) {
+// FIXME: lifetime extension for temporaries without destructors
+// is not implemented yet.
+MTE = nullptr;
+  }
+  if (MTE && MTE->getStorageDuration() != SD_FullExpression) {
+// If the temporary is lifetime-extended, don't save the BTE,
+// because we don't need a temporary destructor, but an automatic
+// destructor.
+ 

[PATCH] D44124: [analyzer] Support destruction and lifetime-extension of inlined function return values.

2018-03-05 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ updated this revision to Diff 137117.
NoQ added a comment.

Fix test comments and make them start from `0` :) Also rebase.


https://reviews.llvm.org/D44124

Files:
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  test/Analysis/lifetime-extension.cpp

Index: test/Analysis/lifetime-extension.cpp
===
--- test/Analysis/lifetime-extension.cpp
+++ test/Analysis/lifetime-extension.cpp
@@ -1,7 +1,10 @@
 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify %s
 // 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
+// RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -DMOVES -verify %s
+// 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
 
 void clang_analyzer_eval(bool);
+void clang_analyzer_checkInlined(bool);
 
 namespace pr17001_call_wrong_destructor {
 bool x;
@@ -63,6 +66,8 @@
   ~C() { if (after) *after = this; }
 
   operator bool() const { return x; }
+
+  static C make(C **after, C **before) { return C(false, after, before); }
 };
 
 void f1() {
@@ -180,3 +185,154 @@
   1 / x; // no-warning
 }
 } // end namespace maintain_original_object_address_on_move
+
+namespace maintain_address_of_copies {
+class C;
+
+struct AddressVector {
+  C *buf[10];
+  int len;
+
+  AddressVector() : len(0) {}
+
+  void push(C *c) {
+buf[len] = c;
+++len;
+  }
+};
+
+class C {
+  AddressVector &v;
+
+public:
+  C(AddressVector &v) : v(v) { v.push(this); }
+  ~C() { v.push(this); }
+
+#ifdef MOVES
+  C(C &&c) : v(c.v) { v.push(this); }
+#endif
+
+  // Note how return-statements prefer move-constructors when available.
+  C(const C &c) : v(c.v) {
+#ifdef MOVES
+clang_analyzer_checkInlined(false); // no-warning
+#else
+v.push(this);
+#endif
+  } // no-warning
+
+  static C make(AddressVector &v) { return C(v); }
+};
+
+void f1() {
+  AddressVector v;
+  {
+C c = C(v);
+  }
+  // 0. Create the original temporary and lifetime-extend it into variable 'c'
+  //construction argument.
+  // 1. Construct variable 'c' (elidable copy/move).
+  // 2. Destroy the temporary.
+  // 3. Destroy variable 'c'.
+  clang_analyzer_eval(v.len == 4);
+  clang_analyzer_eval(v.buf[0] == v.buf[2]);
+  clang_analyzer_eval(v.buf[1] == v.buf[3]);
+#ifdef TEMPORARIES
+  // expected-warning@-4{{TRUE}}
+  // expected-warning@-4{{TRUE}}
+  // expected-warning@-4{{TRUE}}
+#else
+  // expected-warning@-8{{UNKNOWN}}
+  // expected-warning@-8{{UNKNOWN}}
+  // expected-warning@-8{{UNKNOWN}}
+#endif
+}
+
+void f2() {
+  AddressVector v;
+  {
+const C &c = C::make(v);
+  }
+  // 0. Construct the original temporary within make(),
+  // 1. Construct the return value of make() (elidable copy/move) and
+  //lifetime-extend it via reference 'c',
+  // 2. Destroy the temporary within make(),
+  // 3. Destroy the temporary lifetime-extended by 'c'.
+  clang_analyzer_eval(v.len == 4);
+  clang_analyzer_eval(v.buf[0] == v.buf[2]);
+  clang_analyzer_eval(v.buf[1] == v.buf[3]);
+#ifdef TEMPORARIES
+  // expected-warning@-4{{TRUE}}
+  // expected-warning@-4{{TRUE}}
+  // expected-warning@-4{{TRUE}}
+#else
+  // expected-warning@-8{{UNKNOWN}}
+  // expected-warning@-8{{UNKNOWN}}
+  // expected-warning@-8{{UNKNOWN}}
+#endif
+}
+
+void f3() {
+  AddressVector v;
+  {
+C &&c = C::make(v);
+  }
+  // 0. Construct the original temporary within make(),
+  // 1. Construct the return value of make() (elidable copy/move) and
+  //lifetime-extend it via reference 'c',
+  // 2. Destroy the temporary within make(),
+  // 3. Destroy the temporary lifetime-extended by 'c'.
+  clang_analyzer_eval(v.len == 4);
+  clang_analyzer_eval(v.buf[0] == v.buf[2]);
+  clang_analyzer_eval(v.buf[1] == v.buf[3]);
+#ifdef TEMPORARIES
+  // expected-warning@-4{{TRUE}}
+  // expected-warning@-4{{TRUE}}
+  // expected-warning@-4{{TRUE}}
+#else
+  // expected-warning@-8{{UNKNOWN}}
+  // expected-warning@-8{{UNKNOWN}}
+  // expected-warning@-8{{UNKNOWN}}
+#endif
+}
+
+C doubleMake(AddressVector &v) {
+  return C::make(v);
+}
+
+void f4() {
+  AddressVector v;
+  {
+C c = doubleMake(v);
+  }
+  // 0. Construct the original temporary within make(),
+  // 1. Construct the return value of make() (elidable copy/move) and
+  //lifetime-extend it into the return value constructor argument within
+  //doubleMake(),
+  // 2. Destroy the temporary within make(),
+  // 3. Construct the return value of doubleMake() (elidable copy/move) and
+  //lifetime-extend it into the variable 'c' constructor argument,
+  // 4. Destroy the return value of make(),
+  // 5. C

[PATCH] D44124: [analyzer] Support destruction and lifetime-extension of inlined function return values.

2018-03-05 Thread Devin Coughlin via Phabricator via cfe-commits
dcoughlin accepted this revision.
dcoughlin added a comment.
This revision is now accepted and ready to land.

LGTM!




Comment at: test/Analysis/lifetime-extension.cpp:301
+  }
+  // 1. Construct the original temporary within make(),
+  // 2. Construct return value of  make() (elidable move) and lifetime-extend

It might be easier to follow if you started from '0' so that steps here match 
offsets into `buf`.


Repository:
  rC Clang

https://reviews.llvm.org/D44124



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D44124: [analyzer] Support destruction and lifetime-extension of inlined function return values.

2018-03-05 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added inline comments.



Comment at: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp:316
   }
 }
 break;

george.karpenkov wrote:
> 5 closing braces is a lot. What about moving the entire block under 
> `CK_Complete` into a static function?
I'm already on it for the next patch^^


Repository:
  rC Clang

https://reviews.llvm.org/D44124



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D44124: [analyzer] Support destruction and lifetime-extension of inlined function return values.

2018-03-05 Thread George Karpenkov via Phabricator via cfe-commits
george.karpenkov added inline comments.



Comment at: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp:316
   }
 }
 break;

5 closing braces is a lot. What about moving the entire block under 
`CK_Complete` into a static function?


Repository:
  rC Clang

https://reviews.llvm.org/D44124



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D44124: [analyzer] Support destruction and lifetime-extension of inlined function return values.

2018-03-05 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet.
Herald added subscribers: cfe-commits, rnkovacs.

With https://reviews.llvm.org/D44120 we can find the `CXXBindTemporaryExpr` and 
`MaterializeTemporaryExpr` that correspond to the temporary object that is 
being returned from a function into its caller. These expressions are on the 
caller side because it is the caller that's responsible for managing the 
lifetime of such temporary.

In order to find those expressions, we look at the call site's CFG element, 
notice that it is a `CFGValueTypedCall` that was added in 
https://reviews.llvm.org/D44120, and take the relevant construction context 
information from there.

This patch does not yet affect temporaries that were returned from 
conservatively evaluated functions.

The idea was initially described in the last part of 
http://lists.llvm.org/pipermail/cfe-dev/2018-February/056898.html and addresses 
most of the concerns from https://reviews.llvm.org/D42876.


Repository:
  rC Clang

https://reviews.llvm.org/D44124

Files:
  lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  test/Analysis/lifetime-extension.cpp

Index: test/Analysis/lifetime-extension.cpp
===
--- test/Analysis/lifetime-extension.cpp
+++ test/Analysis/lifetime-extension.cpp
@@ -1,7 +1,10 @@
 // RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify %s
 // 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
+// RUN: %clang_analyze_cc1 -Wno-unused -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -DMOVES -verify %s
+// 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
 
 void clang_analyzer_eval(bool);
+void clang_analyzer_checkInlined(bool);
 
 namespace pr17001_call_wrong_destructor {
 bool x;
@@ -63,6 +66,8 @@
   ~C() { if (after) *after = this; }
 
   operator bool() const { return x; }
+
+  static C make(C **after, C **before) { return C(false, after, before); }
 };
 
 void f1() {
@@ -180,3 +185,146 @@
   1 / x; // no-warning
 }
 } // end namespace maintain_original_object_address_on_move
+
+namespace maintain_address_of_copies {
+class C;
+
+struct AddressVector {
+  C *buf[10];
+  int len;
+
+  AddressVector() : len(0) {}
+
+  void push(C *c) {
+buf[len] = c;
+++len;
+  }
+};
+
+class C {
+  AddressVector &v;
+
+public:
+  C(AddressVector &v) : v(v) { v.push(this); }
+  ~C() { v.push(this); }
+
+#ifdef MOVES
+  C(C &&c) : v(c.v) { v.push(this); }
+#endif
+
+  // Note how return-statements prefer move-constructors when available.
+  C(const C &c) : v(c.v) {
+#ifdef MOVES
+clang_analyzer_checkInlined(false); // no-warning
+#else
+v.push(this);
+#endif
+  } // no-warning
+
+  static C make(AddressVector &v) { return C(v); }
+};
+
+void f1() {
+  AddressVector v;
+  {
+C c = C(v);
+  }
+  // Create temporary, perform elidable move into variable,
+  // destroy the temporary, destroy the variable.
+  clang_analyzer_eval(v.len == 4);
+  clang_analyzer_eval(v.buf[0] == v.buf[2]);
+  clang_analyzer_eval(v.buf[1] == v.buf[3]);
+#ifdef TEMPORARIES
+  // expected-warning@-4{{TRUE}}
+  // expected-warning@-4{{TRUE}}
+  // expected-warning@-4{{TRUE}}
+#else
+  // expected-warning@-8{{UNKNOWN}}
+  // expected-warning@-8{{UNKNOWN}}
+  // expected-warning@-8{{UNKNOWN}}
+#endif
+}
+
+void f2() {
+  AddressVector v;
+  {
+const C &c = C::make(v);
+  }
+  // Create temporary within make(), perform elidable move within make()
+  // and lifetime-extend it into 'c', destroy the temporary, destroy the
+  // moved-from object.
+  clang_analyzer_eval(v.len == 4);
+  clang_analyzer_eval(v.buf[0] == v.buf[2]);
+  clang_analyzer_eval(v.buf[1] == v.buf[3]);
+#ifdef TEMPORARIES
+  // expected-warning@-4{{TRUE}}
+  // expected-warning@-4{{TRUE}}
+  // expected-warning@-4{{TRUE}}
+#else
+  // expected-warning@-8{{UNKNOWN}}
+  // expected-warning@-8{{UNKNOWN}}
+  // expected-warning@-8{{UNKNOWN}}
+#endif
+}
+
+void f3() {
+  AddressVector v;
+  {
+C &&c = C::make(v);
+  }
+  // Create temporary within make(), perform elidable move within make()
+  // and lifetime-extend it into 'c', destroy the temporary, destroy the
+  // moved-from object.
+  clang_analyzer_eval(v.len == 4);
+  clang_analyzer_eval(v.buf[0] == v.buf[2]);
+  clang_analyzer_eval(v.buf[1] == v.buf[3]);
+#ifdef TEMPORARIES
+  // expected-warning@-4{{TRUE}}
+  // expected-warning@-4{{TRUE}}
+  // expected-warning@-4{{TRUE}}
+#else
+  // expected-warning@-8{{UNKNOWN}}
+  // expected-warning@-8{{UNKNOWN}}
+  // expected-warning@-8{{UNKNOWN}}
+#endif
+}
+
+C doub