[analyzer] Disable constructor inlining when lifetime extending through a field.
authorArtem Dergachev <artem.dergachev@gmail.com>
Tue, 27 Feb 2018 20:14:06 +0000 (20:14 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Tue, 27 Feb 2018 20:14:06 +0000 (20:14 +0000)
Automatic destructors are missing in the CFG in situations like

  const int &x = C().x;

For now it's better to disable construction inlining, because inlining
constructors while doing nothing on destructors is very bad.

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

llvm-svn: 326240

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

index 0269953..f332132 100644 (file)
@@ -65,6 +65,10 @@ public:
     bool IsArrayCtorOrDtor = false;
     /// 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;
 
     EvalCallOptions() {}
   };
index 14b4569..6502af7 100644 (file)
@@ -168,6 +168,18 @@ ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE,
       break;
     }
     case ConstructionContext::TemporaryObjectKind: {
+      const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
+      // See if we're lifetime-extended via our field. If so, take a note.
+      // Because automatic destructors aren't quite working in this case.
+      if (const auto *MTE = TOCC->getMaterializedTemporaryExpr()) {
+        if (const ValueDecl *VD = MTE->getExtendingDecl()) {
+          assert(VD->getType()->isReferenceType());
+          if (VD->getType()->getPointeeType().getCanonicalType() !=
+              MTE->GetTemporaryExpr()->getType().getCanonicalType()) {
+            CallOpts.IsTemporaryLifetimeExtendedViaSubobject = true;
+          }
+        }
+      }
       // TODO: Support temporaries lifetime-extended via static references.
       // They'd need a getCXXStaticTempObjectRegion().
       CallOpts.IsTemporaryCtorOrDtor = true;
index 4838a8f..ede4816 100644 (file)
@@ -678,6 +678,11 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
       // the fake temporary target.
       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;
     }
 
     break;
index 93605fc..9b64280 100644 (file)
@@ -39,18 +39,10 @@ void f() {
   const int &y = A().j[1]; // no-crash
   const int &z = (A().j[1], A().j[0]); // no-crash
 
-  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
+  // 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}}
 }
 } // end namespace pr19539_crash_on_destroying_an_integer
 
@@ -144,12 +136,7 @@ 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);
-#ifdef TEMPORARIES
-  // expected-warning@-2{{The left operand of '==' is a garbage value}}
-#else
-  // expected-warning@-4{{UNKNOWN}}
-#endif
+  clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
 }
 } // end namespace maintain_original_object_address_on_lifetime_extension