<div dir="ltr">Filed <a href="https://bugs.llvm.org/show_bug.cgi?id=38072">https://bugs.llvm.org/show_bug.cgi?id=38072</a> with an isolated test case.</div><br><div class="gmail_quote"><div dir="ltr">On Wed, Jul 4, 2018 at 4:18 PM Alexander Kornienko <<a href="mailto:alexfh@google.com">alexfh@google.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">We've started seeing assertion failures after this commit.<div>assert.h assertion failed at llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp:485 in static clang::ento::ProgramStateRef clang::ento::ExprEngine::elideDestructor(clang::ento::ProgramStateRef, const clang::CXXBindTemporaryExpr *, const clang::LocationContext *): !State->contains<ElidedDestructors>(I)<br></div><div><br></div><div><br></div><div>The stack trace is<div><div>clang::ento::ExprEngine::elideDestructor</div><div>clang::ento::ExprEngine::prepareForObjectConstruction</div><div>clang::ento::ExprEngine::prepareForObjectConstruction</div><div>clang::ento::ExprEngine::VisitCXXConstructExpr</div><div>clang::ento::ExprEngine::Visit</div><div>clang::ento::ExprEngine::ProcessStmt</div><div>clang::ento::ExprEngine::processCFGElement</div><div>clang::ento::CoreEngine::HandlePostStmt</div><div>clang::ento::CoreEngine::dispatchWorkItem</div><div>clang::ento::CoreEngine::ExecuteWorkList</div><div>clang::ento::ExprEngine::ExecuteWorkList</div><div>::AnalysisConsumer::ActionExprEngine</div><div>::AnalysisConsumer::HandleCode</div><div>::AnalysisConsumer::HandleDeclsCallGraph</div><div>::AnalysisConsumer::runAnalysisOnTranslationUnit</div><div>::AnalysisConsumer::HandleTranslationUnit</div></div></div><div><br></div><div>I haven't come up with a test case yet.</div></div><br><div class="gmail_quote"><div dir="ltr">On Thu, Jun 28, 2018 at 2:34 AM Artem Dergachev via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: dergachev<br>
Date: Wed Jun 27 17:30:18 2018<br>
New Revision: 335800<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=335800&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=335800&view=rev</a><br>
Log:<br>
[analyzer] Add support for pre-C++17 copy elision.<br>
<br>
r335795 adds copy elision information to CFG. This commit allows static analyzer<br>
to elide elidable copy constructors by constructing the objects that were<br>
previously subject to elidable copy directly in the target region of the copy.<br>
<br>
The chain of elided constructors may potentially be indefinitely long. This<br>
only happens when the object is being returned from a function which in turn is<br>
returned from another function, etc.<br>
<br>
NRVO is not supported yet.<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D47671" rel="noreferrer" target="_blank">https://reviews.llvm.org/D47671</a><br>
<br>
Modified:<br>
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h<br>
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp<br>
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp<br>
    cfe/trunk/test/Analysis/cxx17-mandatory-elision.cpp<br>
    cfe/trunk/test/Analysis/gtest.cpp<br>
    cfe/trunk/test/Analysis/inlining/temp-dtors-path-notes.cpp<br>
    cfe/trunk/test/Analysis/lifetime-extension.cpp<br>
    cfe/trunk/test/Analysis/temporaries.cpp<br>
<br>
Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h (original)<br>
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h Wed Jun 27 17:30:18 2018<br>
@@ -780,9 +780,30 @@ private:<br>
       llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P,<br>
       const LocationContext *LC);<br>
<br>
+  /// If the given expression corresponds to a temporary that was used for<br>
+  /// passing into an elidable copy/move constructor and that constructor<br>
+  /// was actually elided, track that we also need to elide the destructor.<br>
+  static ProgramStateRef elideDestructor(ProgramStateRef State,<br>
+                                         const CXXBindTemporaryExpr *BTE,<br>
+                                         const LocationContext *LC);<br>
+<br>
+  /// Stop tracking the destructor that corresponds to an elided constructor.<br>
+  static ProgramStateRef<br>
+  cleanupElidedDestructor(ProgramStateRef State,<br>
+                          const CXXBindTemporaryExpr *BTE,<br>
+                          const LocationContext *LC);<br>
+<br>
+  /// Returns true if the given expression corresponds to a temporary that<br>
+  /// was constructed for passing into an elidable copy/move constructor<br>
+  /// and that constructor was actually elided.<br>
+  static bool isDestructorElided(ProgramStateRef State,<br>
+                                 const CXXBindTemporaryExpr *BTE,<br>
+                                 const LocationContext *LC);<br>
+<br>
   /// Check if all objects under construction have been fully constructed<br>
   /// for the given context range (including FromLC, not including ToLC).<br>
-  /// This is useful for assertions.<br>
+  /// This is useful for assertions. Also checks if elided destructors<br>
+  /// were cleaned up.<br>
   static bool areAllObjectsFullyConstructed(ProgramStateRef State,<br>
                                             const LocationContext *FromLC,<br>
                                             const LocationContext *ToLC);<br>
<br>
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp (original)<br>
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp Wed Jun 27 17:30:18 2018<br>
@@ -139,7 +139,8 @@ public:<br>
     // encountered. This list may be expanded when new actions are implemented.<br>
     assert(getCXXCtorInitializer() || isa<DeclStmt>(getStmt()) ||<br>
            isa<CXXNewExpr>(getStmt()) || isa<CXXBindTemporaryExpr>(getStmt()) ||<br>
-           isa<MaterializeTemporaryExpr>(getStmt()));<br>
+           isa<MaterializeTemporaryExpr>(getStmt()) ||<br>
+           isa<CXXConstructExpr>(getStmt()));<br>
   }<br>
<br>
   const Stmt *getStmt() const {<br>
@@ -183,6 +184,14 @@ typedef llvm::ImmutableMap<ConstructedOb<br>
 REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction,<br>
                                  ObjectsUnderConstructionMap)<br>
<br>
+// Additionally, track a set of destructors that correspond to elided<br>
+// constructors when copy elision occurs.<br>
+typedef std::pair<const CXXBindTemporaryExpr *, const LocationContext *><br>
+    ElidedDestructorItem;<br>
+typedef llvm::ImmutableSet<ElidedDestructorItem><br>
+    ElidedDestructorSet;<br>
+REGISTER_TRAIT_WITH_PROGRAMSTATE(ElidedDestructors,<br>
+                                 ElidedDestructorSet);<br>
<br>
 //===----------------------------------------------------------------------===//<br>
 // Engine construction and deletion.<br>
@@ -358,14 +367,12 @@ ExprEngine::createTemporaryRegionIfNeede<br>
   // a new temporary region out of thin air and copy the contents of the object<br>
   // (which are currently present in the Environment, because Init is an rvalue)<br>
   // into that region. This is not correct, but it is better than nothing.<br>
-  bool FoundOriginalMaterializationRegion = false;<br>
   const TypedValueRegion *TR = nullptr;<br>
   if (const auto *MT = dyn_cast<MaterializeTemporaryExpr>(Result)) {<br>
     if (Optional<SVal> V = getObjectUnderConstruction(State, MT, LC)) {<br>
-      FoundOriginalMaterializationRegion = true;<br>
-      TR = cast<CXXTempObjectRegion>(V->getAsRegion());<br>
-      assert(TR);<br>
       State = finishObjectConstruction(State, MT, LC);<br>
+      State = State->BindExpr(Result, LC, *V);<br>
+      return State;<br>
     } else {<br>
       StorageDuration SD = MT->getStorageDuration();<br>
       // If this object is bound to a reference with static storage duration, we<br>
@@ -402,35 +409,33 @@ ExprEngine::createTemporaryRegionIfNeede<br>
     }<br>
   }<br>
<br>
-  if (!FoundOriginalMaterializationRegion) {<br>
-    // What remains is to copy the value of the object to the new region.<br>
-    // FIXME: In other words, what we should always do is copy value of the<br>
-    // Init expression (which corresponds to the bigger object) to the whole<br>
-    // temporary region TR. However, this value is often no longer present<br>
-    // in the Environment. If it has disappeared, we instead invalidate TR.<br>
-    // Still, what we can do is assign the value of expression Ex (which<br>
-    // corresponds to the sub-object) to the TR's sub-region Reg. At least,<br>
-    // values inside Reg would be correct.<br>
-    SVal InitVal = State->getSVal(Init, LC);<br>
-    if (InitVal.isUnknown()) {<br>
-      InitVal = getSValBuilder().conjureSymbolVal(Result, LC, Init->getType(),<br>
-                                                  currBldrCtx->blockCount());<br>
-      State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false);<br>
-<br>
-      // Then we'd need to take the value that certainly exists and bind it<br>
-      // over.<br>
-      if (InitValWithAdjustments.isUnknown()) {<br>
-        // Try to recover some path sensitivity in case we couldn't<br>
-        // compute the value.<br>
-        InitValWithAdjustments = getSValBuilder().conjureSymbolVal(<br>
-            Result, LC, InitWithAdjustments->getType(),<br>
-            currBldrCtx->blockCount());<br>
-      }<br>
-      State =<br>
-          State->bindLoc(Reg.castAs<Loc>(), InitValWithAdjustments, LC, false);<br>
-    } else {<br>
-      State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false);<br>
+  // What remains is to copy the value of the object to the new region.<br>
+  // FIXME: In other words, what we should always do is copy value of the<br>
+  // Init expression (which corresponds to the bigger object) to the whole<br>
+  // temporary region TR. However, this value is often no longer present<br>
+  // in the Environment. If it has disappeared, we instead invalidate TR.<br>
+  // Still, what we can do is assign the value of expression Ex (which<br>
+  // corresponds to the sub-object) to the TR's sub-region Reg. At least,<br>
+  // values inside Reg would be correct.<br>
+  SVal InitVal = State->getSVal(Init, LC);<br>
+  if (InitVal.isUnknown()) {<br>
+    InitVal = getSValBuilder().conjureSymbolVal(Result, LC, Init->getType(),<br>
+                                                currBldrCtx->blockCount());<br>
+    State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false);<br>
+<br>
+    // Then we'd need to take the value that certainly exists and bind it<br>
+    // over.<br>
+    if (InitValWithAdjustments.isUnknown()) {<br>
+      // Try to recover some path sensitivity in case we couldn't<br>
+      // compute the value.<br>
+      InitValWithAdjustments = getSValBuilder().conjureSymbolVal(<br>
+          Result, LC, InitWithAdjustments->getType(),<br>
+          currBldrCtx->blockCount());<br>
     }<br>
+    State =<br>
+        State->bindLoc(Reg.castAs<Loc>(), InitValWithAdjustments, LC, false);<br>
+  } else {<br>
+    State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false);<br>
   }<br>
<br>
   // The result expression would now point to the correct sub-region of the<br>
@@ -438,10 +443,8 @@ ExprEngine::createTemporaryRegionIfNeede<br>
   // correctly in case (Result == Init).<br>
   State = State->BindExpr(Result, LC, Reg);<br>
<br>
-  if (!FoundOriginalMaterializationRegion) {<br>
-    // Notify checkers once for two bindLoc()s.<br>
-    State = processRegionChange(State, TR, LC);<br>
-  }<br>
+  // Notify checkers once for two bindLoc()s.<br>
+  State = processRegionChange(State, TR, LC);<br>
<br>
   return State;<br>
 }<br>
@@ -475,6 +478,30 @@ ProgramStateRef ExprEngine::finishObject<br>
   return State->remove<ObjectsUnderConstruction>(Key);<br>
 }<br>
<br>
+ProgramStateRef ExprEngine::elideDestructor(ProgramStateRef State,<br>
+                                            const CXXBindTemporaryExpr *BTE,<br>
+                                            const LocationContext *LC) {<br>
+  ElidedDestructorItem I(BTE, LC);<br>
+  assert(!State->contains<ElidedDestructors>(I));<br>
+  return State->add<ElidedDestructors>(I);<br>
+}<br>
+<br>
+ProgramStateRef<br>
+ExprEngine::cleanupElidedDestructor(ProgramStateRef State,<br>
+                                    const CXXBindTemporaryExpr *BTE,<br>
+                                    const LocationContext *LC) {<br>
+  ElidedDestructorItem I(BTE, LC);<br>
+  assert(State->contains<ElidedDestructors>(I));<br>
+  return State->remove<ElidedDestructors>(I);<br>
+}<br>
+<br>
+bool ExprEngine::isDestructorElided(ProgramStateRef State,<br>
+                                    const CXXBindTemporaryExpr *BTE,<br>
+                                    const LocationContext *LC) {<br>
+  ElidedDestructorItem I(BTE, LC);<br>
+  return State->contains<ElidedDestructors>(I);<br>
+}<br>
+<br>
 bool ExprEngine::areAllObjectsFullyConstructed(ProgramStateRef State,<br>
                                                const LocationContext *FromLC,<br>
                                                const LocationContext *ToLC) {<br>
@@ -485,6 +512,10 @@ bool ExprEngine::areAllObjectsFullyConst<br>
       if (I.first.getLocationContext() == LC)<br>
         return false;<br>
<br>
+    for (auto I: State->get<ElidedDestructors>())<br>
+      if (I.second == LC)<br>
+        return false;<br>
+<br>
     LC = LC->getParent();<br>
   }<br>
   return true;<br>
@@ -529,6 +560,14 @@ static void printObjectsUnderConstructio<br>
     Key.print(Out, nullptr, PP);<br>
     Out << " : " << Value << NL;<br>
   }<br>
+<br>
+  for (auto I : State->get<ElidedDestructors>()) {<br>
+    if (I.second != LC)<br>
+      continue;<br>
+    Out << '(' << I.second << ',' << (const void *)I.first << ") ";<br>
+    I.first->printPretty(Out, nullptr, PP);<br>
+    Out << " : (constructor elided)" << NL;<br>
+  }<br>
 }<br>
<br>
 void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State,<br>
@@ -1003,10 +1042,11 @@ void ExprEngine::ProcessMemberDtor(const<br>
 void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,<br>
                                       ExplodedNode *Pred,<br>
                                       ExplodedNodeSet &Dst) {<br>
-  ExplodedNodeSet CleanDtorState;<br>
-  StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx);<br>
+  const CXXBindTemporaryExpr *BTE = D.getBindTemporaryExpr();<br>
   ProgramStateRef State = Pred->getState();<br>
+  const LocationContext *LC = Pred->getLocationContext();<br>
   const MemRegion *MR = nullptr;<br>
+<br>
   if (Optional<SVal> V =<br>
           getObjectUnderConstruction(State, D.getBindTemporaryExpr(),<br>
                                      Pred->getLocationContext())) {<br>
@@ -1017,6 +1057,21 @@ void ExprEngine::ProcessTemporaryDtor(co<br>
                                      Pred->getLocationContext());<br>
     MR = V->getAsRegion();<br>
   }<br>
+<br>
+  // If copy elision has occured, and the constructor corresponding to the<br>
+  // destructor was elided, we need to skip the destructor as well.<br>
+  if (isDestructorElided(State, BTE, LC)) {<br>
+    State = cleanupElidedDestructor(State, BTE, LC);<br>
+    NodeBuilder Bldr(Pred, Dst, *currBldrCtx);<br>
+    PostImplicitCall PP(D.getDestructorDecl(getContext()),<br>
+                        D.getBindTemporaryExpr()->getLocStart(),<br>
+                        Pred->getLocationContext());<br>
+    Bldr.generateNode(PP, State, Pred);<br>
+    return;<br>
+  }<br>
+<br>
+  ExplodedNodeSet CleanDtorState;<br>
+  StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx);<br>
   StmtBldr.generateNode(D.getBindTemporaryExpr(), Pred, State);<br>
<br>
   QualType T = D.getBindTemporaryExpr()->getSubExpr()->getType();<br>
@@ -2201,8 +2256,21 @@ void ExprEngine::processEndOfFunction(No<br>
           // it must be a separate problem.<br>
           assert(isa<CXXBindTemporaryExpr>(I.first.getStmt()));<br>
           State = State->remove<ObjectsUnderConstruction>(I.first);<br>
+          // Also cleanup the elided destructor info.<br>
+          ElidedDestructorItem Item(<br>
+              cast<CXXBindTemporaryExpr>(I.first.getStmt()),<br>
+              I.first.getLocationContext());<br>
+          State = State->remove<ElidedDestructors>(Item);<br>
         }<br>
<br>
+      // Also suppress the assertion for elided destructors when temporary<br>
+      // destructors are not provided at all by the CFG, because there's no<br>
+      // good place to clean them up.<br>
+      if (!AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG())<br>
+        for (auto I : State->get<ElidedDestructors>())<br>
+          if (I.second == LC)<br>
+            State = State->remove<ElidedDestructors>(I);<br>
+<br>
       LC = LC->getParent();<br>
     }<br>
     if (State != Pred->getState()) {<br>
<br>
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp (original)<br>
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp Wed Jun 27 17:30:18 2018<br>
@@ -209,12 +209,37 @@ std::pair<ProgramStateRef, SVal> ExprEng<br>
       }<br>
       llvm_unreachable("Unhandled return value construction context!");<br>
     }<br>
-    case ConstructionContext::ElidedTemporaryObjectKind:<br>
+    case ConstructionContext::ElidedTemporaryObjectKind: {<br>
       assert(AMgr.getAnalyzerOptions().shouldElideConstructors());<br>
-      // FALL-THROUGH<br>
+      const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC);<br>
+      const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();<br>
+      const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();<br>
+      const CXXConstructExpr *CE = TCC->getConstructorAfterElision();<br>
+<br>
+      // Support pre-C++17 copy elision. We'll have the elidable copy<br>
+      // constructor in the AST and in the CFG, but we'll skip it<br>
+      // and construct directly into the final object. This call<br>
+      // also sets the CallOpts flags for us.<br>
+      SVal V;<br>
+      std::tie(State, V) = prepareForObjectConstruction(<br>
+          CE, State, LCtx, TCC->getConstructionContextAfterElision(), CallOpts);<br>
+<br>
+      // Remember that we've elided the constructor.<br>
+      State = addObjectUnderConstruction(State, CE, LCtx, V);<br>
+<br>
+      // Remember that we've elided the destructor.<br>
+      if (BTE)<br>
+        State = elideDestructor(State, BTE, LCtx);<br>
+<br>
+      // Instead of materialization, shamelessly return<br>
+      // the final object destination.<br>
+      if (MTE)<br>
+        State = addObjectUnderConstruction(State, MTE, LCtx, V);<br>
+<br>
+      return std::make_pair(State, V);<br>
+    }<br>
     case ConstructionContext::SimpleTemporaryObjectKind: {<br>
-      // TODO: Copy elision implementation goes here.<br>
-      const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);<br>
+      const auto *TCC = cast<SimpleTemporaryObjectConstructionContext>(CC);<br>
       const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();<br>
       const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();<br>
       SVal V = UnknownVal();<br>
@@ -266,6 +291,20 @@ void ExprEngine::VisitCXXConstructExpr(c<br>
<br>
   SVal Target = UnknownVal();<br>
<br>
+  if (Optional<SVal> ElidedTarget =<br>
+          getObjectUnderConstruction(State, CE, LCtx)) {<br>
+    // We've previously modeled an elidable constructor by pretending that it in<br>
+    // fact constructs into the correct target. This constructor can therefore<br>
+    // be skipped.<br>
+    Target = *ElidedTarget;<br>
+    StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx);<br>
+    State = finishObjectConstruction(State, CE, LCtx);<br>
+    if (auto L = Target.getAs<Loc>())<br>
+      State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType()));<br>
+    Bldr.generateNode(CE, Pred, State);<br>
+    return;<br>
+  }<br>
+<br>
   // FIXME: Handle arrays, which run the same constructor for every element.<br>
   // For now, we just run the first constructor (which should still invalidate<br>
   // the entire array).<br>
<br>
Modified: cfe/trunk/test/Analysis/cxx17-mandatory-elision.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/cxx17-mandatory-elision.cpp?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/cxx17-mandatory-elision.cpp?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/cxx17-mandatory-elision.cpp (original)<br>
+++ cfe/trunk/test/Analysis/cxx17-mandatory-elision.cpp Wed Jun 27 17:30:18 2018<br>
@@ -1,5 +1,17 @@<br>
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 -verify %s<br>
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s<br>
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++11 -analyzer-config elide-constructors=false -DNO_ELIDE_FLAG -verify %s<br>
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -std=c++17 -analyzer-config elide-constructors=false -DNO_ELIDE_FLAG -verify %s<br>
+<br>
+// Copy elision always occurs in C++17, otherwise it's under<br>
+// an on-by-default flag.<br>
+#if __cplusplus >= 201703L<br>
+  #define ELIDE 1<br>
+#else<br>
+  #ifndef NO_ELIDE_FLAG<br>
+    #define ELIDE 1<br>
+  #endif<br>
+#endif<br>
<br>
 void clang_analyzer_eval(bool);<br>
<br>
@@ -39,9 +51,9 @@ public:<br>
   C() : t(T(4)) {<br>
     S s = {1, 2, 3};<br>
     t.s = s;<br>
-    // FIXME: Should be TRUE in C++11 as well.<br>
+    // FIXME: Should be TRUE regardless of copy elision.<br>
     clang_analyzer_eval(t.w == 4);<br>
-#if __cplusplus >= 201703L<br>
+#ifdef ELIDE<br>
     // expected-warning@-2{{TRUE}}<br>
 #else<br>
     // expected-warning@-4{{UNKNOWN}}<br>
@@ -149,7 +161,7 @@ void testMultipleReturns() {<br>
   AddressVector<ClassWithoutDestructor> v;<br>
   ClassWithoutDestructor c = make3(v);<br>
<br>
-#if __cplusplus >= 201703L<br>
+#if ELIDE<br>
   clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}<br>
   clang_analyzer_eval(v.buf[0] == &c); // expected-warning{{TRUE}}<br>
 #else<br>
@@ -184,13 +196,13 @@ void testVariable() {<br>
     ClassWithDestructor c = ClassWithDestructor(v);<br>
     // Check if the last destructor is an automatic destructor.<br>
     // A temporary destructor would have fired by now.<br>
-#if __cplusplus >= 201703L<br>
+#if ELIDE<br>
     clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}<br>
 #else<br>
     clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}<br>
 #endif<br>
   }<br>
-#if __cplusplus >= 201703L<br>
+#if ELIDE<br>
   // 0. Construct the variable.<br>
   // 1. Destroy the variable.<br>
   clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}<br>
@@ -218,13 +230,13 @@ void testCtorInitializer() {<br>
     TestCtorInitializer t(v);<br>
     // Check if the last destructor is an automatic destructor.<br>
     // A temporary destructor would have fired by now.<br>
-#if __cplusplus >= 201703L<br>
+#if ELIDE<br>
     clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}<br>
 #else<br>
     clang_analyzer_eval(v.len == 3); // expected-warning{{TRUE}}<br>
 #endif<br>
   }<br>
-#if __cplusplus >= 201703L<br>
+#if ELIDE<br>
   // 0. Construct the member variable.<br>
   // 1. Destroy the member variable.<br>
   clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}<br>
@@ -257,14 +269,14 @@ void testMultipleReturnsWithDestructors(<br>
     ClassWithDestructor c = make3(v);<br>
     // Check if the last destructor is an automatic destructor.<br>
     // A temporary destructor would have fired by now.<br>
-#if __cplusplus >= 201703L<br>
+#if ELIDE<br>
     clang_analyzer_eval(v.len == 1); // expected-warning{{TRUE}}<br>
 #else<br>
     clang_analyzer_eval(v.len == 9); // expected-warning{{TRUE}}<br>
 #endif<br>
   }<br>
<br>
-#if __cplusplus >= 201703L<br>
+#if ELIDE<br>
   // 0. Construct the variable. Yes, constructor in make1() constructs<br>
   //    the variable 'c'.<br>
   // 1. Destroy the variable.<br>
<br>
Modified: cfe/trunk/test/Analysis/gtest.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/gtest.cpp?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/gtest.cpp?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/gtest.cpp (original)<br>
+++ cfe/trunk/test/Analysis/gtest.cpp Wed Jun 27 17:30:18 2018<br>
@@ -154,13 +154,11 @@ void testConstrainState(int p) {<br>
 void testAssertSymbolicPtr(const bool *b) {<br>
   ASSERT_TRUE(*b); // no-crash<br>
<br>
-  // FIXME: Our solver doesn't handle this well yet.<br>
-  clang_analyzer_eval(*b); // expected-warning{{UNKNOWN}}<br>
+  clang_analyzer_eval(*b); // expected-warning{{TRUE}}<br>
 }<br>
<br>
 void testAssertSymbolicRef(const bool &b) {<br>
   ASSERT_TRUE(b); // no-crash<br>
<br>
-  // FIXME: Our solver doesn't handle this well yet.<br>
-  clang_analyzer_eval(b); // expected-warning{{UNKNOWN}}<br>
+  clang_analyzer_eval(b); // expected-warning{{TRUE}}<br>
 }<br>
<br>
Modified: cfe/trunk/test/Analysis/inlining/temp-dtors-path-notes.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/inlining/temp-dtors-path-notes.cpp?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/inlining/temp-dtors-path-notes.cpp?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/inlining/temp-dtors-path-notes.cpp (original)<br>
+++ cfe/trunk/test/Analysis/inlining/temp-dtors-path-notes.cpp Wed Jun 27 17:30:18 2018<br>
@@ -30,19 +30,14 @@ public:<br>
 };<br>
<br>
 void test(int coin) {<br>
-  // We'd divide by zero in the temporary destructor that goes after the<br>
-  // elidable copy-constructor from C(0) to the lifetime-extended temporary.<br>
-  // So in fact this example has nothing to do with lifetime extension.<br>
-  // Actually, it would probably be better to elide the constructor, and<br>
-  // put the note for the destructor call at the closing brace after nop.<br>
+  // We'd divide by zero in the automatic destructor for variable 'c'.<br>
   const C &c = coin ? C(1) : C(0); // expected-note   {{Assuming 'coin' is 0}}<br>
                                    // expected-note@-1{{'?' condition is false}}<br>
                                    // expected-note@-2{{Passing the value 0 via 1st parameter 'x'}}<br>
                                    // expected-note@-3{{Calling constructor for 'C'}}<br>
                                    // expected-note@-4{{Returning from constructor for 'C'}}<br>
-                                   // expected-note@-5{{Calling '~C'}}<br>
   c.nop();<br>
-}<br>
+} // expected-note{{Calling '~C'}}<br>
 } // end namespace test_lifetime_extended_temporary<br>
<br>
 namespace test_bug_after_dtor {<br>
<br>
Modified: cfe/trunk/test/Analysis/lifetime-extension.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/lifetime-extension.cpp?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/lifetime-extension.cpp?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/lifetime-extension.cpp (original)<br>
+++ cfe/trunk/test/Analysis/lifetime-extension.cpp Wed Jun 27 17:30:18 2018<br>
@@ -97,13 +97,10 @@ void f1() {<br>
<br>
 void f2() {<br>
   C *after, *before;<br>
-  C c = C(1, &after, &before);<br>
-  clang_analyzer_eval(after == before);<br>
-#ifdef TEMPORARIES<br>
-  // expected-warning@-2{{TRUE}}<br>
-#else<br>
-  // expected-warning@-4{{UNKNOWN}}<br>
-#endif<br>
+  {<br>
+    C c = C(1, &after, &before);<br>
+  }<br>
+  clang_analyzer_eval(after == before); // expected-warning{{TRUE}}<br>
 }<br>
<br>
 void f3(bool coin) {<br>
@@ -129,6 +126,7 @@ void f4(bool coin) {<br>
   // operator. Ideally also add support for the binary conditional operator in<br>
   // C++. Because for now it calls the constructor for the condition twice.<br>
   if (coin) {<br>
+    // FIXME: Should not warn.<br>
     clang_analyzer_eval(after == before);<br>
 #ifdef TEMPORARIES<br>
   // expected-warning@-2{{The left operand of '==' is a garbage value}}<br>
@@ -136,13 +134,12 @@ void f4(bool coin) {<br>
   // expected-warning@-4{{UNKNOWN}}<br>
 #endif<br>
   } else {<br>
+    // FIXME: Should be TRUE.<br>
     clang_analyzer_eval(after == before);<br>
 #ifdef TEMPORARIES<br>
-    // Seems to work at the moment, but also seems accidental.<br>
-    // Feel free to break.<br>
-  // expected-warning@-4{{TRUE}}<br>
+  // expected-warning@-2{{FALSE}}<br>
 #else<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
+  // expected-warning@-4{{UNKNOWN}}<br>
 #endif<br>
   }<br>
 }<br>
@@ -234,24 +231,25 @@ void f2() {<br>
 } // end namespace maintain_original_object_address_on_move<br>
<br>
 namespace maintain_address_of_copies {<br>
+class C;<br>
<br>
-template <typename T> struct AddressVector {<br>
-  const T *buf[10];<br>
+struct AddressVector {<br>
+  C *buf[10];<br>
   int len;<br>
<br>
   AddressVector() : len(0) {}<br>
<br>
-  void push(const T *t) {<br>
-    buf[len] = t;<br>
+  void push(C *c) {<br>
+    buf[len] = c;<br>
     ++len;<br>
   }<br>
 };<br>
<br>
 class C {<br>
-  AddressVector<C> &v;<br>
+  AddressVector &v;<br>
<br>
 public:<br>
-  C(AddressVector<C> &v) : v(v) { v.push(this); }<br>
+  C(AddressVector &v) : v(v) { v.push(this); }<br>
   ~C() { v.push(this); }<br>
<br>
 #ifdef MOVES<br>
@@ -267,132 +265,70 @@ public:<br>
 #endif<br>
   } // no-warning<br>
<br>
-  static C make(AddressVector<C> &v) { return C(v); }<br>
+  static C make(AddressVector &v) { return C(v); }<br>
 };<br>
<br>
 void f1() {<br>
-  AddressVector<C> v;<br>
+  AddressVector v;<br>
   {<br>
     C c = C(v);<br>
   }<br>
-  // 0. Create the original temporary and lifetime-extend it into variable 'c'<br>
-  //    construction argument.<br>
-  // 1. Construct variable 'c' (elidable copy/move).<br>
-  // 2. Destroy the temporary.<br>
-  // 3. Destroy variable 'c'.<br>
-  clang_analyzer_eval(v.len == 4);<br>
-  clang_analyzer_eval(v.buf[0] == v.buf[2]);<br>
-  clang_analyzer_eval(v.buf[1] == v.buf[3]);<br>
-#ifdef TEMPORARIES<br>
-  // expected-warning@-4{{TRUE}}<br>
-  // expected-warning@-4{{TRUE}}<br>
-  // expected-warning@-4{{TRUE}}<br>
-#else<br>
-  // expected-warning@-8{{UNKNOWN}}<br>
-  // expected-warning@-8{{UNKNOWN}}<br>
-  // expected-warning@-8{{UNKNOWN}}<br>
-#endif<br>
+  // 0. Construct variable 'c' (copy/move elided).<br>
+  // 1. Destroy variable 'c'.<br>
+  clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}<br>
+  clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}<br>
 }<br>
<br>
 void f2() {<br>
-  AddressVector<C> v;<br>
+  AddressVector v;<br>
   {<br>
     const C &c = C::make(v);<br>
   }<br>
-  // 0. Construct the original temporary within make(),<br>
-  // 1. Construct the return value of make() (elidable copy/move) and<br>
-  //    lifetime-extend it via reference 'c',<br>
-  // 2. Destroy the temporary within make(),<br>
-  // 3. Destroy the temporary lifetime-extended by 'c'.<br>
-  clang_analyzer_eval(v.len == 4);<br>
-  clang_analyzer_eval(v.buf[0] == v.buf[2]);<br>
-  clang_analyzer_eval(v.buf[1] == v.buf[3]);<br>
+  // 0. Construct the return value of make() (copy/move elided) and<br>
+  //    lifetime-extend it directly via reference 'c',<br>
+  // 1. Destroy the temporary lifetime-extended by 'c'.<br>
+  clang_analyzer_eval(v.len == 2);<br>
+  clang_analyzer_eval(v.buf[0] == v.buf[1]);<br>
 #ifdef TEMPORARIES<br>
-  // expected-warning@-4{{TRUE}}<br>
-  // expected-warning@-4{{TRUE}}<br>
-  // expected-warning@-4{{TRUE}}<br>
+  // expected-warning@-3{{TRUE}}<br>
+  // expected-warning@-3{{TRUE}}<br>
 #else<br>
-  // expected-warning@-8{{UNKNOWN}}<br>
-  // expected-warning@-8{{UNKNOWN}}<br>
-  // expected-warning@-8{{UNKNOWN}}<br>
+  // expected-warning@-6{{UNKNOWN}}<br>
+  // expected-warning@-6{{UNKNOWN}}<br>
 #endif<br>
 }<br>
<br>
 void f3() {<br>
-  AddressVector<C> v;<br>
+  AddressVector v;<br>
   {<br>
     C &&c = C::make(v);<br>
   }<br>
-  // 0. Construct the original temporary within make(),<br>
-  // 1. Construct the return value of make() (elidable copy/move) and<br>
-  //    lifetime-extend it via reference 'c',<br>
-  // 2. Destroy the temporary within make(),<br>
-  // 3. Destroy the temporary lifetime-extended by 'c'.<br>
-  clang_analyzer_eval(v.len == 4);<br>
-  clang_analyzer_eval(v.buf[0] == v.buf[2]);<br>
-  clang_analyzer_eval(v.buf[1] == v.buf[3]);<br>
+  // 0. Construct the return value of make() (copy/move elided) and<br>
+  //    lifetime-extend it directly via reference 'c',<br>
+  // 1. Destroy the temporary lifetime-extended by 'c'.<br>
+  clang_analyzer_eval(v.len == 2);<br>
+  clang_analyzer_eval(v.buf[0] == v.buf[1]);<br>
 #ifdef TEMPORARIES<br>
-  // expected-warning@-4{{TRUE}}<br>
-  // expected-warning@-4{{TRUE}}<br>
-  // expected-warning@-4{{TRUE}}<br>
+  // expected-warning@-3{{TRUE}}<br>
+  // expected-warning@-3{{TRUE}}<br>
 #else<br>
-  // expected-warning@-8{{UNKNOWN}}<br>
-  // expected-warning@-8{{UNKNOWN}}<br>
-  // expected-warning@-8{{UNKNOWN}}<br>
+  // expected-warning@-6{{UNKNOWN}}<br>
+  // expected-warning@-6{{UNKNOWN}}<br>
 #endif<br>
 }<br>
<br>
-C doubleMake(AddressVector<C> &v) {<br>
+C doubleMake(AddressVector &v) {<br>
   return C::make(v);<br>
 }<br>
<br>
 void f4() {<br>
-  AddressVector<C> v;<br>
+  AddressVector v;<br>
   {<br>
     C c = doubleMake(v);<br>
   }<br>
-  // 0. Construct the original temporary within make(),<br>
-  // 1. Construct the return value of make() (elidable copy/move) and<br>
-  //    lifetime-extend it into the return value constructor argument within<br>
-  //    doubleMake(),<br>
-  // 2. Destroy the temporary within make(),<br>
-  // 3. Construct the return value of doubleMake() (elidable copy/move) and<br>
-  //    lifetime-extend it into the variable 'c' constructor argument,<br>
-  // 4. Destroy the return value of make(),<br>
-  // 5. Construct variable 'c' (elidable copy/move),<br>
-  // 6. Destroy the return value of doubleMake(),<br>
-  // 7. Destroy variable 'c'.<br>
-  clang_analyzer_eval(v.len == 8);<br>
-  clang_analyzer_eval(v.buf[0] == v.buf[2]);<br>
-  clang_analyzer_eval(v.buf[1] == v.buf[4]);<br>
-  clang_analyzer_eval(v.buf[3] == v.buf[6]);<br>
-  clang_analyzer_eval(v.buf[5] == v.buf[7]);<br>
-#ifdef TEMPORARIES<br>
-  // expected-warning@-6{{TRUE}}<br>
-  // expected-warning@-6{{TRUE}}<br>
-  // expected-warning@-6{{TRUE}}<br>
-  // expected-warning@-6{{TRUE}}<br>
-  // expected-warning@-6{{TRUE}}<br>
-#else<br>
-  // expected-warning@-12{{UNKNOWN}}<br>
-  // expected-warning@-12{{UNKNOWN}}<br>
-  // expected-warning@-12{{UNKNOWN}}<br>
-  // expected-warning@-12{{UNKNOWN}}<br>
-  // expected-warning@-12{{UNKNOWN}}<br>
-#endif<br>
-}<br>
-<br>
-class NoDtor {<br>
-  AddressVector<NoDtor> &v;<br>
-<br>
-public:<br>
-  NoDtor(AddressVector<NoDtor> &v) : v(v) { v.push(this); }<br>
-};<br>
-<br>
-void f5() {<br>
-  AddressVector<NoDtor> v;<br>
-  const NoDtor &N = NoDtor(v);<br>
-  clang_analyzer_eval(v.buf[0] == &N); // expected-warning{{TRUE}}<br>
+  // 0. Construct variable 'c' (all copies/moves elided),<br>
+  // 1. Destroy variable 'c'.<br>
+  clang_analyzer_eval(v.len == 2); // expected-warning{{TRUE}}<br>
+  clang_analyzer_eval(v.buf[0] == v.buf[1]); // expected-warning{{TRUE}}<br>
 }<br>
-<br>
 } // end namespace maintain_address_of_copies<br>
<br>
Modified: cfe/trunk/test/Analysis/temporaries.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/temporaries.cpp?rev=335800&r1=335799&r2=335800&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/temporaries.cpp?rev=335800&r1=335799&r2=335800&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/temporaries.cpp (original)<br>
+++ cfe/trunk/test/Analysis/temporaries.cpp Wed Jun 27 17:30:18 2018<br>
@@ -612,107 +612,44 @@ void test() {<br>
   clang_analyzer_eval(c3.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
   C c4 = returnTemporaryWithConstruction();<br>
-  clang_analyzer_eval(c4.getX() == 1);<br>
-  clang_analyzer_eval(c4.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-  // expected-warning@-3{{TRUE}}<br>
-  // expected-warning@-3{{TRUE}}<br>
-#else<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+  clang_analyzer_eval(c4.getX() == 1); // expected-warning{{TRUE}}<br>
+  clang_analyzer_eval(c4.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
   C c5 = returnTemporaryWithAnotherFunctionWithConstruction();<br>
-  clang_analyzer_eval(c5.getX() == 1);<br>
-  clang_analyzer_eval(c5.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-  // expected-warning@-3{{TRUE}}<br>
-  // expected-warning@-3{{TRUE}}<br>
-#else<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+  clang_analyzer_eval(c5.getX() == 1); // expected-warning{{TRUE}}<br>
+  clang_analyzer_eval(c5.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
   C c6 = returnTemporaryWithCopyConstructionWithConstruction();<br>
-  clang_analyzer_eval(c5.getX() == 1);<br>
-  clang_analyzer_eval(c5.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-  // expected-warning@-3{{TRUE}}<br>
-  // expected-warning@-3{{TRUE}}<br>
-#else<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+  clang_analyzer_eval(c5.getX() == 1); // expected-warning{{TRUE}}<br>
+  clang_analyzer_eval(c5.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
 #if __cplusplus >= 201103L<br>
<br>
   C c7 = returnTemporaryWithBraces();<br>
-  clang_analyzer_eval(c7.getX() == 1);<br>
-  clang_analyzer_eval(c7.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-  // expected-warning@-3{{TRUE}}<br>
-  // expected-warning@-3{{TRUE}}<br>
-#else<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+  clang_analyzer_eval(c7.getX() == 1); // expected-warning{{TRUE}}<br>
+  clang_analyzer_eval(c7.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
   C c8 = returnTemporaryWithAnotherFunctionWithBraces();<br>
-  clang_analyzer_eval(c8.getX() == 1);<br>
-  clang_analyzer_eval(c8.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-  // expected-warning@-3{{TRUE}}<br>
-  // expected-warning@-3{{TRUE}}<br>
-#else<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+  clang_analyzer_eval(c8.getX() == 1); // expected-warning{{TRUE}}<br>
+  clang_analyzer_eval(c8.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
   C c9 = returnTemporaryWithCopyConstructionWithBraces();<br>
-  clang_analyzer_eval(c9.getX() == 1);<br>
-  clang_analyzer_eval(c9.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-  // expected-warning@-3{{TRUE}}<br>
-  // expected-warning@-3{{TRUE}}<br>
-#else<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+  clang_analyzer_eval(c9.getX() == 1); // expected-warning{{TRUE}}<br>
+  clang_analyzer_eval(c9.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
 #endif // C++11<br>
<br>
   D d1 = returnTemporaryWithVariableAndNonTrivialCopy();<br>
-  clang_analyzer_eval(d1.getX() == 1);<br>
-  clang_analyzer_eval(d1.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-  // expected-warning@-3{{TRUE}}<br>
-  // expected-warning@-3{{TRUE}}<br>
-#else<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+  clang_analyzer_eval(d1.getX() == 1); // expected-warning{{TRUE}}<br>
+  clang_analyzer_eval(d1.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
   D d2 = returnTemporaryWithAnotherFunctionWithVariableAndNonTrivialCopy();<br>
-  clang_analyzer_eval(d2.getX() == 1);<br>
-  clang_analyzer_eval(d2.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-  // expected-warning@-3{{TRUE}}<br>
-  // expected-warning@-3{{TRUE}}<br>
-#else<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+  clang_analyzer_eval(d2.getX() == 1); // expected-warning{{TRUE}}<br>
+  clang_analyzer_eval(d2.getY() == 2); // expected-warning{{TRUE}}<br>
<br>
   D d3 = returnTemporaryWithCopyConstructionWithVariableAndNonTrivialCopy();<br>
-  clang_analyzer_eval(d3.getX() == 1);<br>
-  clang_analyzer_eval(d3.getY() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-  // expected-warning@-3{{TRUE}}<br>
-  // expected-warning@-3{{TRUE}}<br>
-#else<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+  clang_analyzer_eval(d3.getX() == 1); // expected-warning{{TRUE}}<br>
+  clang_analyzer_eval(d3.getY() == 2); // expected-warning{{TRUE}}<br>
 }<br>
 } // namespace test_return_temporary<br>
<br>
@@ -767,19 +704,9 @@ void test(int coin) {<br>
<br>
   C c2 = coin ? C(1) : C(2);<br>
   if (coin) {<br>
-    clang_analyzer_eval(c2.getX() == 1);<br>
-#ifdef TEMPORARY_DTORS<br>
-  // expected-warning@-2{{TRUE}}<br>
-#else<br>
-  // expected-warning@-4{{UNKNOWN}}<br>
-#endif<br>
+    clang_analyzer_eval(c2.getX() == 1); // expected-warning{{TRUE}}<br>
   } else {<br>
-    clang_analyzer_eval(c2.getX() == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-  // expected-warning@-2{{TRUE}}<br>
-#else<br>
-  // expected-warning@-4{{UNKNOWN}}<br>
-#endif<br>
+    clang_analyzer_eval(c2.getX() == 2); // expected-warning{{TRUE}}<br>
   }<br>
 }<br>
<br>
@@ -816,16 +743,9 @@ void test_simple_temporary_with_copy() {<br>
   {<br>
     C c = C(x, y);<br>
   }<br>
-  // Two constructors (temporary object expr and copy) and two destructors.<br>
-  clang_analyzer_eval(x == 2);<br>
-  clang_analyzer_eval(y == 2);<br>
-#ifdef TEMPORARY_DTORS<br>
-  // expected-warning@-3{{TRUE}}<br>
-  // expected-warning@-3{{TRUE}}<br>
-#else<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-  // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+  // Only one constructor directly into the variable, and one destructor.<br>
+  clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}<br>
+  clang_analyzer_eval(y == 1); // expected-warning{{TRUE}}<br>
 }<br>
<br>
 void test_ternary_temporary(int coin) {<br>
@@ -833,10 +753,10 @@ void test_ternary_temporary(int coin) {<br>
   {<br>
     const C &c = coin ? C(x, y) : C(z, w);<br>
   }<br>
-  // This time each branch contains an additional elidable copy constructor.<br>
+  // Only one constructor on every branch, and one automatic destructor.<br>
   if (coin) {<br>
-    clang_analyzer_eval(x == 2);<br>
-    clang_analyzer_eval(y == 2);<br>
+    clang_analyzer_eval(x == 1);<br>
+    clang_analyzer_eval(y == 1);<br>
 #ifdef TEMPORARY_DTORS<br>
     // expected-warning@-3{{TRUE}}<br>
     // expected-warning@-3{{TRUE}}<br>
@@ -850,8 +770,8 @@ void test_ternary_temporary(int coin) {<br>
   } else {<br>
     clang_analyzer_eval(x == 0); // expected-warning{{TRUE}}<br>
     clang_analyzer_eval(y == 0); // expected-warning{{TRUE}}<br>
-    clang_analyzer_eval(z == 2);<br>
-    clang_analyzer_eval(w == 2);<br>
+    clang_analyzer_eval(z == 1);<br>
+    clang_analyzer_eval(w == 1);<br>
 #ifdef TEMPORARY_DTORS<br>
     // expected-warning@-3{{TRUE}}<br>
     // expected-warning@-3{{TRUE}}<br>
@@ -867,33 +787,18 @@ void test_ternary_temporary_with_copy(in<br>
   {<br>
     C c = coin ? C(x, y) : C(z, w);<br>
   }<br>
-  // Temporary expression, elidable copy within branch,<br>
-  // constructor for variable - 3 total.<br>
+  // On each branch the variable is constructed directly.<br>
   if (coin) {<br>
-    clang_analyzer_eval(x == 3);<br>
-    clang_analyzer_eval(y == 3);<br>
-#ifdef TEMPORARY_DTORS<br>
-    // expected-warning@-3{{TRUE}}<br>
-    // expected-warning@-3{{TRUE}}<br>
-#else<br>
-    // expected-warning@-6{{UNKNOWN}}<br>
-    // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+    clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}<br>
+    clang_analyzer_eval(y == 1); // expected-warning{{TRUE}}<br>
     clang_analyzer_eval(z == 0); // expected-warning{{TRUE}}<br>
     clang_analyzer_eval(w == 0); // expected-warning{{TRUE}}<br>
<br>
   } else {<br>
     clang_analyzer_eval(x == 0); // expected-warning{{TRUE}}<br>
     clang_analyzer_eval(y == 0); // expected-warning{{TRUE}}<br>
-    clang_analyzer_eval(z == 3);<br>
-    clang_analyzer_eval(w == 3);<br>
-#ifdef TEMPORARY_DTORS<br>
-    // expected-warning@-3{{TRUE}}<br>
-    // expected-warning@-3{{TRUE}}<br>
-#else<br>
-    // expected-warning@-6{{UNKNOWN}}<br>
-    // expected-warning@-6{{UNKNOWN}}<br>
-#endif<br>
+    clang_analyzer_eval(z == 1); // expected-warning{{TRUE}}<br>
+    clang_analyzer_eval(w == 1); // expected-warning{{TRUE}}<br>
   }<br>
 }<br>
 } // namespace test_match_constructors_and_destructors<br>
@@ -928,12 +833,13 @@ void testLifetimeExtendedCall() {<br>
 }<br>
<br>
 void testCopiedCall() {<br>
-  C c = make();<br>
-  // Should have divided by zero in the temporary destructor.<br>
-  clang_analyzer_warnIfReached();<br>
-#ifndef TEMPORARY_DTORS<br>
-    // expected-warning@-2{{REACHABLE}}<br>
-#endif<br>
+  {<br>
+    C c = make();<br>
+    // Should have elided the constructor/destructor for the temporary<br>
+    clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}<br>
+  }<br>
+  // Should have divided by zero in the destructor.<br>
+  clang_analyzer_warnIfReached(); // no-warning<br>
 }<br>
 } // namespace destructors_for_return_values<br>
<br>
@@ -1018,20 +924,10 @@ void test() {<br>
 #endif<br>
<br>
   S s = 20;<br>
-  clang_analyzer_eval(s.x == 20);<br>
-#ifdef TEMPORARY_DTORS<br>
-  // expected-warning@-2{{TRUE}}<br>
-#else<br>
-  // expected-warning@-4{{UNKNOWN}}<br>
-#endif<br>
+  clang_analyzer_eval(s.x == 20); // expected-warning{{TRUE}}<br>
<br>
   C c2 = s;<br>
-  clang_analyzer_eval(c2.getX() == 20);<br>
-#ifdef TEMPORARY_DTORS<br>
-  // expected-warning@-2{{TRUE}}<br>
-#else<br>
-  // expected-warning@-4{{UNKNOWN}}<br>
-#endif<br>
+  clang_analyzer_eval(c2.getX() == 20); // expected-warning{{TRUE}}<br>
 }<br>
 } // end namespace implicit_constructor_conversion<br>
<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div>
</blockquote></div>