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