<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>