]> granicus.if.org Git - clang/commitdiff
[analyzer] Re-enable constructors when lifetime extension through fields occurs.
authorArtem Dergachev <artem.dergachev@gmail.com>
Mon, 4 Jun 2018 20:18:37 +0000 (20:18 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Mon, 4 Jun 2018 20:18:37 +0000 (20:18 +0000)
Temporary object constructor inlining was disabled in r326240 for code like

    const int &x = A().x;

because automatic destructor for the lifetime-extended object A() was not
working correctly in CFG.

CFG was fixed in r333941, so inlining can be re-enabled. CFG for lifetime
extension through aggregates still needs to be fixed.

Differential Revision: https://reviews.llvm.org/D44239

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@333946 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
test/Analysis/lifetime-extension.cpp

index 5f7b7e1a969c47bee859818300cee752c5f937a1..1f2fef0b2dd2a3e097ceaf02b01a8e973f4a1f1f 100644 (file)
@@ -105,11 +105,6 @@ public:
     /// This call is a constructor or a destructor of a temporary value.
     bool IsTemporaryCtorOrDtor = false;
 
-    /// This call is a constructor for a temporary that is lifetime-extended
-    /// by binding a smaller object within it to a reference, for example
-    /// 'const int &x = C().x;'.
-    bool IsTemporaryLifetimeExtendedViaSubobject = false;
-
     /// This call is a constructor for a temporary that is lifetime-extended
     /// by binding it to a reference-type field within an aggregate,
     /// for example 'A { const C &c; }; A a = { C() };'
index 997ff6a5a261ac2834ab3c60bf3c0ab8a49fa0e0..4fa05cb1b8f4d7d8d20ab3834d746ede979ea6eb 100644 (file)
@@ -181,22 +181,13 @@ SVal ExprEngine::getLocationForConstructedObject(const CXXConstructExpr *CE,
       const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
       if (const auto *MTE = TOCC->getMaterializedTemporaryExpr()) {
         if (const ValueDecl *VD = MTE->getExtendingDecl()) {
-          // Pattern-match various forms of lifetime extension that aren't
-          // currently supported by the CFG.
-          // FIXME: Is there a better way to retrieve this information from
-          // the MaterializeTemporaryExpr?
           assert(MTE->getStorageDuration() != SD_FullExpression);
-          if (VD->getType()->isReferenceType()) {
-            assert(VD->getType()->isReferenceType());
-            if (VD->getType()->getPointeeType().getCanonicalType() !=
-                MTE->GetTemporaryExpr()->getType().getCanonicalType()) {
-              // We're lifetime-extended via our field. Automatic destructors
-              // aren't quite working in this case.
-              CallOpts.IsTemporaryLifetimeExtendedViaSubobject = true;
-            }
-          } else {
+          if (!VD->getType()->isReferenceType()) {
             // We're lifetime-extended by a surrounding aggregate.
-            // Automatic destructors aren't quite working in this case.
+            // Automatic destructors aren't quite working in this case
+            // on the CFG side. We should warn the caller about that.
+            // FIXME: Is there a better way to retrieve this information from
+            // the MaterializeTemporaryExpr?
             CallOpts.IsTemporaryLifetimeExtendedViaAggregate = true;
           }
         }
index 1a5dabfc6712ebab6a12949303294ba0bf2975d3..44f8eebe2bd239edf4b3383fa79adaa980840680 100644 (file)
@@ -696,12 +696,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
       if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion)
         return CIP_DisallowedOnce;
 
-      // If the temporary is lifetime-extended by binding a smaller object
-      // within it to a reference, automatic destructors don't work properly.
-      if (CallOpts.IsTemporaryLifetimeExtendedViaSubobject)
-        return CIP_DisallowedOnce;
-
-      // If the temporary is lifetime-extended by binding it to a reference-typ
+      // If the temporary is lifetime-extended by binding it to a reference-type
       // field within an aggregate, automatic destructors don't work properly.
       if (CallOpts.IsTemporaryLifetimeExtendedViaAggregate)
         return CIP_DisallowedOnce;
index 9173129ac14eb8cf30de994a0adbdf9baf8e7aae..1bad166d2d8d53158cddde93d400ee2d4712ed21 100644 (file)
@@ -46,10 +46,18 @@ void f() {
   const int &y = A().j[1]; // no-crash
   const int &z = (A().j[1], A().j[0]); // no-crash
 
-  // FIXME: All of these should be TRUE, but constructors aren't inlined.
-  clang_analyzer_eval(x == 1); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(y == 3); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(z == 2); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(x == 1);
+  clang_analyzer_eval(y == 3);
+  clang_analyzer_eval(z == 2);
+#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
 }
 } // end namespace pr19539_crash_on_destroying_an_integer
 
@@ -144,8 +152,12 @@ void f5() {
   {
     const bool &x = C(true, &after, &before).x; // no-crash
   }
-  // FIXME: Should be TRUE. Should not warn about garbage value.
-  clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(after == before);
+#ifdef TEMPORARIES
+  // expected-warning@-2{{TRUE}}
+#else
+  // expected-warning@-4{{UNKNOWN}}
+#endif
 }
 
 struct A { // A is an aggregate.