Index: clang/include/clang/Analysis/CFG.h =================================================================== --- clang/include/clang/Analysis/CFG.h +++ clang/include/clang/Analysis/CFG.h @@ -202,7 +202,8 @@ isa(C) || isa(C) || isa(C) || - isa(C))); + isa(C)) || + isa(C)); Data2.setPointer(const_cast(C)); } Index: clang/include/clang/Analysis/ConstructionContext.h =================================================================== --- clang/include/clang/Analysis/ConstructionContext.h +++ clang/include/clang/Analysis/ConstructionContext.h @@ -36,13 +36,14 @@ ElidedDestructorKind, ElidableConstructorKind, ArgumentKind, - STATEMENT_WITH_INDEX_KIND_BEGIN=ArgumentKind, - STATEMENT_WITH_INDEX_KIND_END=ArgumentKind, + LambdaKind, + STATEMENT_WITH_INDEX_KIND_BEGIN = ArgumentKind, + STATEMENT_WITH_INDEX_KIND_END = LambdaKind, STATEMENT_KIND_BEGIN = VariableKind, - STATEMENT_KIND_END = ArgumentKind, + STATEMENT_KIND_END = LambdaKind, InitializerKind, - INITIALIZER_KIND_BEGIN=InitializerKind, - INITIALIZER_KIND_END=InitializerKind + INITIALIZER_KIND_BEGIN = InitializerKind, + INITIALIZER_KIND_END = InitializerKind }; LLVM_DUMP_METHOD static StringRef getKindAsString(ItemKind K) { @@ -55,6 +56,8 @@ case ElidedDestructorKind: return "elide destructor"; case ElidableConstructorKind: return "elide constructor"; case ArgumentKind: return "construct into argument"; + case LambdaKind: + return "construct into lambda captured variable"; case InitializerKind: return "construct into member variable"; }; llvm_unreachable("Unknown ItemKind"); @@ -72,7 +75,7 @@ bool hasIndex() const { return Kind >= STATEMENT_WITH_INDEX_KIND_BEGIN && - Kind >= STATEMENT_WITH_INDEX_KIND_END; + Kind <= STATEMENT_WITH_INDEX_KIND_END; } bool hasInitializer() const { @@ -127,6 +130,9 @@ ConstructionContextItem(const CXXCtorInitializer *Init) : Data(Init), Kind(InitializerKind), Index(0) {} + ConstructionContextItem(const LambdaExpr *LE, unsigned Index) + : Data(LE), Kind(LambdaKind), Index(Index) {} + ItemKind getKind() const { return Kind; } LLVM_DUMP_METHOD StringRef getKindAsString() const { @@ -254,7 +260,8 @@ CXX17ElidedCopyReturnedValueKind, RETURNED_VALUE_BEGIN = SimpleReturnedValueKind, RETURNED_VALUE_END = CXX17ElidedCopyReturnedValueKind, - ArgumentKind + ArgumentKind, + LambdaKind }; protected: @@ -298,6 +305,11 @@ const ConstructionContextLayer *TopLayer); Kind getKind() const { return K; } + + virtual const ArrayInitLoopExpr *getArrayInitLoop() const { return nullptr; } + + // Only declared to silence -Wnon-virtual-dtor warnings. + virtual ~ConstructionContext() = default; }; /// An abstract base class for local variable constructors. @@ -314,6 +326,12 @@ public: const DeclStmt *getDeclStmt() const { return DS; } + const ArrayInitLoopExpr *getArrayInitLoop() const override { + const auto *Var = cast(DS->getSingleDecl()); + + return dyn_cast_or_null(Var->getInit()); + } + static bool classof(const ConstructionContext *CC) { return CC->getKind() >= VARIABLE_BEGIN && CC->getKind() <= VARIABLE_END; @@ -381,6 +399,10 @@ public: const CXXCtorInitializer *getCXXCtorInitializer() const { return I; } + const ArrayInitLoopExpr *getArrayInitLoop() const override { + return dyn_cast_or_null(I->getInit()); + } + static bool classof(const ConstructionContext *CC) { return CC->getKind() >= INITIALIZER_BEGIN && CC->getKind() <= INITIALIZER_END; @@ -659,6 +681,47 @@ } }; +class LambdaConstructionContext : public ConstructionContext { + // The lambda of which the initializer we capture. + const LambdaExpr *LE; + + // Index of the captured element in the captured list. + unsigned Index; + + friend class ConstructionContext; // Allows to create<>() itself. + + explicit LambdaConstructionContext(const LambdaExpr *LE, unsigned Index) + : ConstructionContext(LambdaKind), LE(LE), Index(Index) {} + +public: + const LambdaExpr *getLambdaExpr() const { return LE; } + unsigned getIndex() const { return Index; } + + std::pair + getCaptureFieldAndInitializer() const { + unsigned Idx = 0; + CXXRecordDecl::field_iterator CurField = + LE->getLambdaClass()->field_begin(); + for (LambdaExpr::const_capture_init_iterator i = LE->capture_init_begin(), + e = LE->capture_init_end(); + i != e; ++i, ++CurField, ++Idx) { + if (Idx == Index) + return {*CurField, *i}; + } + + llvm_unreachable("Lambda capture field not found!"); + } + + const ArrayInitLoopExpr *getArrayInitLoop() const override { + return dyn_cast_or_null( + getCaptureFieldAndInitializer().second); + } + + static bool classof(const ConstructionContext *CC) { + return CC->getKind() == LambdaKind; + } +}; + } // end namespace clang #endif // LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -622,6 +622,11 @@ getIndexOfElementToConstruct(ProgramStateRef State, const CXXConstructExpr *E, const LocationContext *LCtx); + /// Retreives the size of the array in the pending ArrayInitLoopExpr. + static Optional getPendingInitLoop(ProgramStateRef State, + const CXXConstructExpr *E, + const LocationContext *LCtx); + /// By looking at a certain item that may be potentially part of an object's /// ConstructionContext, retrieve such object's location. A particular /// statement can be transparently passed as \p Item in most cases. @@ -816,7 +821,9 @@ /// Checks whether our policies allow us to inline a non-POD type array /// construction. - bool shouldInlineArrayConstruction(const ArrayType *Type); + bool shouldInlineArrayConstruction(const ProgramStateRef State, + const CXXConstructExpr *CE, + const LocationContext *LCtx); /// Checks whether we construct an array of non-POD type, and decides if the /// constructor should be inkoved once again. @@ -916,6 +923,16 @@ const CXXConstructExpr *E, const LocationContext *LCtx); + /// Sets the size of the array in a pending ArrayInitLoopExpr. + static ProgramStateRef setPendingInitLoop(ProgramStateRef State, + const CXXConstructExpr *E, + const LocationContext *LCtx, + unsigned Idx); + + static ProgramStateRef removePendingInitLoop(ProgramStateRef State, + const CXXConstructExpr *E, + const LocationContext *LCtx); + /// Store the location of a C++ object corresponding to a statement /// until the statement is actually encountered. For example, if a DeclStmt /// has CXXConstructExpr as its initializer, the object would be considered Index: clang/lib/Analysis/CFG.cpp =================================================================== --- clang/lib/Analysis/CFG.cpp +++ clang/lib/Analysis/CFG.cpp @@ -1659,9 +1659,13 @@ appendInitializer(Block, I); if (Init) { + // If the initializer is an ArrayInitLoopExpr, we want to extract the + // initializer, that's used for each element. + const auto *AILE = dyn_cast_or_null(Init); + findConstructionContexts( ConstructionContextLayer::create(cfg->getBumpVectorContext(), I), - Init); + AILE ? AILE->getSubExpr() : Init); if (HasTemporaries) { // For expression with temporaries go directly to subexpression to omit @@ -2931,9 +2935,13 @@ autoCreateBlock(); appendStmt(Block, DS); + // If the initializer is an ArrayInitLoopExpr, we want to extract the + // initializer, that's used for each element. + const auto *AILE = dyn_cast_or_null(Init); + findConstructionContexts( ConstructionContextLayer::create(cfg->getBumpVectorContext(), DS), - Init); + AILE ? AILE->getSubExpr() : Init); // Keep track of the last non-null block, as 'Block' can be nulled out // if the initializer expression is something like a 'while' in a @@ -3340,13 +3348,25 @@ CFGBlock *CFGBuilder::VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc) { CFGBlock *LastBlock = VisitNoRecurse(E, asc); + + unsigned Idx = 0; for (LambdaExpr::capture_init_iterator it = E->capture_init_begin(), et = E->capture_init_end(); it != et; ++it) { if (Expr *Init = *it) { + // If the initializer is an ArrayInitLoopExpr, we want to extract the + // initializer, that's used for each element. + const auto *AILE = dyn_cast_or_null(Init); + + findConstructionContexts(ConstructionContextLayer::create( + cfg->getBumpVectorContext(), {E, Idx}), + AILE ? AILE->getSubExpr() : Init); + CFGBlock *Tmp = Visit(Init); if (Tmp) LastBlock = Tmp; } + + ++Idx; } return LastBlock; } @@ -5616,6 +5636,12 @@ Stmts.push_back(TOCC->getConstructorAfterElision()); break; } + case ConstructionContext::LambdaKind: { + const auto *LCC = cast(CC); + Helper.handledStmt(const_cast(LCC->getLambdaExpr()), OS); + OS << "+" << LCC->getIndex(); + return; + } case ConstructionContext::ArgumentKind: { const auto *ACC = cast(CC); if (const Stmt *BTE = ACC->getCXXBindTemporaryExpr()) { Index: clang/lib/Analysis/ConstructionContext.cpp =================================================================== --- clang/lib/Analysis/ConstructionContext.cpp +++ clang/lib/Analysis/ConstructionContext.cpp @@ -156,6 +156,11 @@ return create( C, I, BTE); } + case ConstructionContextItem::LambdaKind: { + assert(ParentLayer->isLast()); + const auto *E = cast(ParentItem.getStmt()); + return create(C, E, ParentItem.getIndex()); + } } // switch (ParentItem.getKind()) llvm_unreachable("Unexpected construction context with destructor!"); @@ -200,6 +205,11 @@ case ConstructionContextItem::ElidableConstructorKind: { llvm_unreachable("The argument needs to be materialized first!"); } + case ConstructionContextItem::LambdaKind: { + assert(TopLayer->isLast()); + const auto *E = cast(TopItem.getStmt()); + return create(C, E, TopItem.getIndex()); + } case ConstructionContextItem::InitializerKind: { assert(TopLayer->isLast()); const CXXCtorInitializer *I = TopItem.getCXXCtorInitializer(); Index: clang/lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -196,6 +196,14 @@ IndexOfElementToConstructMap; REGISTER_TRAIT_WITH_PROGRAMSTATE(IndexOfElementToConstruct, IndexOfElementToConstructMap) + +// This trait is responsible for holding our pending ArrayInitLoopExprs. +// It pairs the LocationContext and the initializer CXXConstructExpr with +// the size of the array that's being copy initialized. +typedef llvm::ImmutableMap< + std::pair, unsigned> + PendingInitLoopMap; +REGISTER_TRAIT_WITH_PROGRAMSTATE(PendingInitLoop, PendingInitLoopMap) //===----------------------------------------------------------------------===// // Engine construction and deletion. //===----------------------------------------------------------------------===// @@ -462,6 +470,34 @@ return State->set(Key, Idx); } +Optional ExprEngine::getPendingInitLoop(ProgramStateRef State, + const CXXConstructExpr *E, + const LocationContext *LCtx) { + + return Optional::create( + State->get({E, LCtx->getStackFrame()})); +} + +ProgramStateRef ExprEngine::removePendingInitLoop(ProgramStateRef State, + const CXXConstructExpr *E, + const LocationContext *LCtx) { + auto Key = std::make_pair(E, LCtx->getStackFrame()); + + assert(E && State->contains(Key)); + return State->remove(Key); +} + +ProgramStateRef ExprEngine::setPendingInitLoop(ProgramStateRef State, + const CXXConstructExpr *E, + const LocationContext *LCtx, + unsigned Size) { + auto Key = std::make_pair(E, LCtx->getStackFrame()); + + assert(!State->contains(Key) && Size > 0); + + return State->set(Key, Size); +} + Optional ExprEngine::getIndexOfElementToConstruct(ProgramStateRef State, const CXXConstructExpr *E, @@ -487,18 +523,36 @@ const LocationContext *LC, SVal V) { ConstructedObjectKey Key(Item, LC->getStackFrame()); - const CXXConstructExpr *E = nullptr; + const Expr *Init = nullptr; if (auto DS = dyn_cast_or_null(Item.getStmtOrNull())) { if (auto VD = dyn_cast_or_null(DS->getSingleDecl())) - E = dyn_cast(VD->getInit()); + Init = VD->getInit(); } - if (!E && !Item.getStmtOrNull()) { - auto CtorInit = Item.getCXXCtorInitializer(); - E = dyn_cast(CtorInit->getInit()); + if (!Init && !Item.getStmtOrNull()) + Init = Item.getCXXCtorInitializer()->getInit(); + + if (Item.getKind() == ConstructionContextItem::LambdaKind) { + const auto *LE = cast(Item.getStmt()); + unsigned Idx = Item.getIndex(); + + for (auto &&I : LE->capture_inits()) { + if (Idx == 0) { + Init = I; + break; + } + --Idx; + } } + // In an ArrayInitLoopExpr the real initializer is returned by + // getSubExpr(). + if (const auto *AILE = dyn_cast_or_null(Init)) + Init = AILE->getSubExpr(); + + const auto *E = dyn_cast_or_null(Init); + // FIXME: Currently the state might already contain the marker due to // incorrect handling of temporaries bound to default parameters. // The state will already contain the marker if we construct elements @@ -2797,6 +2851,11 @@ const Expr *Arr = Ex->getCommonExpr()->getSourceExpr(); for (auto *Node : CheckerPreStmt) { + + // The constructor visitior has already taken care of everything. + if (auto *CE = dyn_cast(Ex->getSubExpr())) + break; + const LocationContext *LCtx = Node->getLocationContext(); ProgramStateRef state = Node->getState(); Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -290,6 +290,33 @@ return loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx)); } + case ConstructionContext::LambdaKind: { + CallOpts.IsTemporaryCtorOrDtor = true; + + const auto *LCC = cast(CC); + + const auto *LE = LCC->getLambdaExpr(); + + // Get the region of the lambda itself. + const MemRegion *R = + svalBuilder.getRegionManager().getCXXTempObjectRegion(LE, LCtx); + SVal V = loc::MemRegionVal(R); + + const FieldDecl *FieldForCapture; + const Expr *FieldInitializer; + std::tie(FieldForCapture, FieldInitializer) = + LCC->getCaptureFieldAndInitializer(); + + SVal Base = State->getLValue(FieldForCapture, V); + if (getIndexOfElementToConstruct( + State, dyn_cast_or_null(E), LCtx)) { + CallOpts.IsArrayCtorOrDtor = true; + Base = State->getLValue(E->getType(), svalBuilder.makeArrayIndex(Idx), + Base); + } + + return Base; + } case ConstructionContext::ArgumentKind: { // Arguments are technically temporaries. CallOpts.IsTemporaryCtorOrDtor = true; @@ -450,6 +477,20 @@ return State; } + case ConstructionContext::LambdaKind: { + const auto *LCC = cast(CC); + + const FieldDecl *FieldForCapture; + const Expr *FieldInitializer; + std::tie(FieldForCapture, FieldInitializer) = + LCC->getCaptureFieldAndInitializer(); + + if (const auto *AILE = dyn_cast(FieldInitializer)) + FieldInitializer = AILE->getSubExpr(); + + return addObjectUnderConstruction( + State, {LCC->getLambdaExpr(), LCC->getIndex()}, LCtx, V); + } case ConstructionContext::ArgumentKind: { const auto *ACC = cast(CC); if (const auto *BTE = ACC->getCXXBindTemporaryExpr()) @@ -462,6 +503,56 @@ llvm_unreachable("Unhandled construction context!"); } +static ProgramStateRef +bindRequiredArrayElementToEnvironment(ProgramStateRef State, + const ArrayInitLoopExpr *AILE, + const LocationContext *LCtx, SVal Idx) { + // The ctor in this case is guaranteed to be a copy ctor, otherwise we hit a + // compile time error. + // + // -ArrayInitLoopExpr <-- we're here + // |-OpaqueValueExpr + // | `-DeclRefExpr <-- match this + // `-CXXConstructExpr + // `-ImplicitCastExpr + // `-ArraySubscriptExpr + // |-ImplicitCastExpr + // | `-OpaqueValueExpr + // | `-DeclRefExpr + // `-ArrayInitIndexExpr + // + // The resulting expression might look like the one below in an implicit + // copy/move ctor. + // + // ArrayInitLoopExpr <-- we're here + // |-OpaqueValueExpr + // | `-MemberExpr <-- match this + // | `-DeclRefExpr + // `-CXXConstructExpr + // `-ArraySubscriptExpr + // |-ImplicitCastExpr + // | `-OpaqueValueExpr + // | `-MemberExpr + // | `-DeclRefExpr + // `-ArrayInitIndexExpr + // + // HACK: There is no way we can put the index of the array element into the + // CFG unless we unroll the loop, so we manually select and bind the required + // parameter to the environment. + const auto *CE = cast(AILE->getSubExpr()); + const auto *OVESrc = AILE->getCommonExpr()->getSourceExpr(); + + SVal Base = UnknownVal(); + if (const auto *ME = dyn_cast(OVESrc)) + Base = State->getSVal(ME, LCtx); + else if (const auto *DRE = cast(OVESrc)) + Base = State->getLValue(cast(DRE->getDecl()), LCtx); + + SVal NthElem = State->getLValue(CE->getType(), Idx, Base); + + return State->BindExpr(CE->getArg(0), LCtx, NthElem); +} + void ExprEngine::handleConstructor(const Expr *E, ExplodedNode *Pred, ExplodedNodeSet &destNodes) { @@ -502,12 +593,26 @@ // Inherited constructors are always base class constructors. assert(CE && !CIE && "A complete constructor is inherited?!"); + // If the ctor is part of an ArrayInitLoopExpr, we want to handle it + // differently. + auto *AILE = CC ? CC->getArrayInitLoop() : nullptr; + unsigned Idx = 0; - if (CE->getType()->isArrayType()) { + if (CE->getType()->isArrayType() || AILE) { Idx = getIndexOfElementToConstruct(State, CE, LCtx).getValueOr(0u); State = setIndexOfElementToConstruct(State, CE, LCtx, Idx + 1); } + if (AILE) { + // Only set this once even though we loop through it multiple times. + if (!getPendingInitLoop(State, CE, LCtx)) + State = setPendingInitLoop(State, CE, LCtx, + AILE->getArraySize().getLimitedValue()); + + State = bindRequiredArrayElementToEnvironment( + State, AILE, LCtx, svalBuilder.makeArrayIndex(Idx)); + } + // The target region is found from construction context. std::tie(State, Target) = handleConstructionContext(CE, State, LCtx, CC, CallOpts, Idx); @@ -1038,6 +1143,7 @@ // If we created a new MemRegion for the lambda, we should explicitly bind // the captures. + unsigned Idx = 0; CXXRecordDecl::field_iterator CurField = LE->getLambdaClass()->field_begin(); for (LambdaExpr::const_capture_init_iterator i = LE->capture_init_begin(), e = LE->capture_init_end(); @@ -1048,6 +1154,20 @@ SVal InitVal; if (!FieldForCapture->hasCapturedVLAType()) { Expr *InitExpr = *i; + + if (const auto AILE = dyn_cast(InitExpr)) { + // If the AILE initializes a non-POD array, we need to keep it as the + // InitExpr. + if (dyn_cast(AILE->getSubExpr())) + InitExpr = AILE->getSubExpr(); + } + + if (dyn_cast(InitExpr)) { + State = finishObjectConstruction(State, {LE, Idx}, LocCtxt); + ++Idx; + continue; + } + assert(InitExpr && "Capture missing initialization expression"); InitVal = State->getSVal(InitExpr, LocCtxt); } else { @@ -1058,6 +1178,7 @@ InitVal = State->getSVal(SizeExpr, LocCtxt); } + ++Idx; State = State->bindLoc(FieldLoc, InitVal, LocCtxt); } Index: clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -265,9 +265,13 @@ ShouldRepeatCall = shouldRepeatCtorCall(state, CCE, callerCtx); - if (!ShouldRepeatCall && - getIndexOfElementToConstruct(state, CCE, callerCtx)) - state = removeIndexOfElementToConstruct(state, CCE, callerCtx); + if (!ShouldRepeatCall) { + if (getIndexOfElementToConstruct(state, CCE, callerCtx)) + state = removeIndexOfElementToConstruct(state, CCE, callerCtx); + + if (getPendingInitLoop(state, CCE, callerCtx)) + state = removePendingInitLoop(state, CCE, callerCtx); + } } if (const auto *CNE = dyn_cast(CE)) { @@ -815,8 +819,7 @@ // We still allow construction into ElementRegion targets when they don't // represent array elements. if (CallOpts.IsArrayCtorOrDtor) { - if (!shouldInlineArrayConstruction( - dyn_cast(CtorExpr->getType()))) + if (!shouldInlineArrayConstruction(Pred->getState(), CtorExpr, CurLC)) return CIP_DisallowedOnce; } @@ -1082,10 +1085,14 @@ return true; } -bool ExprEngine::shouldInlineArrayConstruction(const ArrayType *Type) { - if (!Type) +bool ExprEngine::shouldInlineArrayConstruction(const ProgramStateRef State, + const CXXConstructExpr *CE, + const LocationContext *LCtx) { + if (!CE) return false; + auto Type = CE->getType(); + // FIXME: Handle other arrays types. if (const auto *CAT = dyn_cast(Type)) { unsigned Size = getContext().getConstantArrayElementCount(CAT); @@ -1093,6 +1100,10 @@ return Size <= AMgr.options.maxBlockVisitOnPath; } + // Check if we're inside an ArrayInitLoopExpr, and it's sufficiently small. + if (auto Size = getPendingInitLoop(State, CE, LCtx)) + return *Size <= AMgr.options.maxBlockVisitOnPath; + return false; } @@ -1111,6 +1122,9 @@ return Size > getIndexOfElementToConstruct(State, E, LCtx); } + if (auto Size = getPendingInitLoop(State, E, LCtx)) + return Size > getIndexOfElementToConstruct(State, E, LCtx); + return false; } Index: clang/test/Analysis/array-init-loop.cpp =================================================================== --- clang/test/Analysis/array-init-loop.cpp +++ clang/test/Analysis/array-init-loop.cpp @@ -125,3 +125,92 @@ clang_analyzer_eval(moved.arr[3]); // expected-warning{{UNKNOWN}} clang_analyzer_eval(moved.arr[4]); // expected-warning{{UNKNOWN}} } + +struct S2 { + inline static int c = 0; + int i; + + S2() : i(++c) {} + + S2(const S2 ©) { + i = copy.i + 1; + } + + S2(S2 &&move) { + i = move.i + 2; + } +}; + +void array_init_non_pod() { + S2::c = 0; + S2 arr[4]; + + auto [a, b, c, d] = arr; + + clang_analyzer_eval(a.i == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(b.i == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(c.i == 4); // expected-warning{{TRUE}} + clang_analyzer_eval(d.i == 5); // expected-warning{{TRUE}} +} + +struct S3 { + int i; +}; + +void array_uninit_non_pod() { + // FIXME: core.uninitialized.Assign in an implicit copy ctor is reported only once, + // we prefer to catch it in lambda_uninit_non_pod() + // S3 arr[1]; + + // auto [a] = arr; +} + +void lambda_init_non_pod() { + S2::c = 0; + S2 arr[4]; + + auto l = [arr] { return arr[0].i; }(); + clang_analyzer_eval(l == 2); // expected-warning{{TRUE}} + + l = [arr] { return arr[1].i; }(); + clang_analyzer_eval(l == 3); // expected-warning{{TRUE}} + + l = [arr] { return arr[2].i; }(); + clang_analyzer_eval(l == 4); // expected-warning{{TRUE}} + + l = [arr] { return arr[3].i; }(); + clang_analyzer_eval(l == 5); // expected-warning{{TRUE}} +} + +void lambda_uninit_non_pod() { + S3 arr[4]; + + int l = [arr] { return arr[3].i; }(); // expected-warning@156{{ in implicit constructor is garbage or undefined }} +} + +struct S5 { + S2 arr[4]; +}; + +void copy_ctor_init_non_pod() { + S2::c = 0; + S5 orig; + + S5 copy = orig; + clang_analyzer_eval(copy.arr[0].i == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(copy.arr[1].i == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(copy.arr[2].i == 4); // expected-warning{{TRUE}} + clang_analyzer_eval(copy.arr[3].i == 5); // expected-warning{{TRUE}} +} + +void move_ctor_init_non_pod() { + S2::c = 0; + S5 orig; + + S5 moved = (S5 &&) orig; + + clang_analyzer_eval(moved.arr[0].i == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(moved.arr[1].i == 4); // expected-warning{{TRUE}} + clang_analyzer_eval(moved.arr[2].i == 5); // expected-warning{{TRUE}} + clang_analyzer_eval(moved.arr[3].i == 6); // expected-warning{{TRUE}} +} Index: clang/test/Analysis/ctor.mm =================================================================== --- clang/test/Analysis/ctor.mm +++ clang/test/Analysis/ctor.mm @@ -439,16 +439,16 @@ NonPOD b = a; - clang_analyzer_eval(b.values[0].x == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(b.values[1].x == 2); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(b.values[2].x == 3); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(b.values[0].x == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(b.values[1].x == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(b.values[2].x == 3); // expected-warning{{TRUE}} NonPOD c; c = b; - clang_analyzer_eval(c.values[0].x == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(c.values[1].x == 2); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(c.values[2].x == 3); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(c.values[0].x == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(c.values[1].x == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(c.values[2].x == 3); // expected-warning{{TRUE}} } struct NestedNonPOD { @@ -502,16 +502,16 @@ NonPODDefaulted b = a; - clang_analyzer_eval(b.values[0].x == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(b.values[1].x == 2); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(b.values[2].x == 3); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(b.values[0].x == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(b.values[1].x == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(b.values[2].x == 3); // expected-warning{{TRUE}} NonPODDefaulted c; c = b; - clang_analyzer_eval(c.values[0].x == 1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(c.values[1].x == 2); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(c.values[2].x == 3); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(c.values[0].x == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(c.values[1].x == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(c.values[2].x == 3); // expected-warning{{TRUE}} } }; Index: clang/test/Analysis/lambdas.cpp =================================================================== --- clang/test/Analysis/lambdas.cpp +++ clang/test/Analysis/lambdas.cpp @@ -400,7 +400,7 @@ // CHECK: [B1] // CHECK: 1: x // CHECK: 2: [B1.1] (ImplicitCastExpr, NoOp, const struct X) -// CHECK: 3: [B1.2] (CXXConstructExpr, struct X) +// CHECK: 3: [B1.2] (CXXConstructExpr[B1.4]+0, struct X) // CHECK: 4: [x] { // CHECK: } // CHECK: 5: (void)[B1.4] (CStyleCastExpr, ToVoid, void) @@ -408,4 +408,3 @@ // CHECK: Succs (1): B0 // CHECK: [B0 (EXIT)] // CHECK: Preds (1): B1 - Index: clang/test/Analysis/uninit-structured-binding-array.cpp =================================================================== --- clang/test/Analysis/uninit-structured-binding-array.cpp +++ clang/test/Analysis/uninit-structured-binding-array.cpp @@ -292,3 +292,68 @@ clang_analyzer_eval(e == 5); // expected-warning{{UNKNOWN}} clang_analyzer_eval(f == 6); // expected-warning{{UNKNOWN}} } + +struct S { + int a = 1; + int b = 2; +}; + +void non_pod_val(void) { + S arr[2]; + + auto [x, y] = arr; + + clang_analyzer_eval(x.a == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(x.b == 2); // expected-warning{{TRUE}} + + clang_analyzer_eval(y.a == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(y.b == 2); // expected-warning{{TRUE}} +} + +void non_pod_lref(void) { + S arr[2]; + + auto &[x, y] = arr; + + clang_analyzer_eval(x.a == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(x.b == 2); // expected-warning{{TRUE}} + + clang_analyzer_eval(y.a == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(y.b == 2); // expected-warning{{TRUE}} +} + +void non_pod_rref(void) { + S arr[2]; + + auto &&[x, y] = arr; + + clang_analyzer_eval(x.a == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(x.b == 2); // expected-warning{{TRUE}} + + clang_analyzer_eval(y.a == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(y.b == 2); // expected-warning{{TRUE}} +} + +struct SUD { + int a = 1; + int b = 2; + + SUD() = default; + + SUD(const SUD ©) { + a = copy.a + 1; + b = copy.b + 1; + } +}; + +void non_pod_user_defined_val(void) { + SUD arr[2]; + + auto [x, y] = arr; + + clang_analyzer_eval(x.a == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(x.b == 3); // expected-warning{{TRUE}} + + clang_analyzer_eval(y.a == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(y.b == 3); // expected-warning{{TRUE}} +}