Index: include/clang/Analysis/CFG.h =================================================================== --- include/clang/Analysis/CFG.h +++ include/clang/Analysis/CFG.h @@ -39,6 +39,7 @@ class BinaryOperator; class CFG; class ConstructionContext; +class TemporaryObjectConstructionContext; class CXXBaseSpecifier; class CXXBindTemporaryExpr; class CXXCtorInitializer; @@ -65,8 +66,9 @@ // stmt kind Statement, Constructor, + CXXRecordTypedCall, STMT_BEGIN = Statement, - STMT_END = Constructor, + STMT_END = CXXRecordTypedCall, // dtor kind AutomaticObjectDtor, DeleteDtor, @@ -158,10 +160,6 @@ return static_cast(Data2.getPointer()); } - QualType getType() const { - return cast(getStmt())->getType(); - } - private: friend class CFGElement; @@ -172,6 +170,46 @@ } }; +/// CFGCXXRecordTypedCall - Represents a function call that returns a C++ object +/// by value. This, like constructor, requires a construction context, which +/// will always be that of a temporary object - usually consumed by an elidable +/// constructor. For such value-typed calls the ReturnedValueConstructionContext +/// of their return value is naturally complemented by the +/// TemporaryObjectConstructionContext at the call site (here). In C such +/// tracking is not necessary because no additional effort is required for +/// destroying the object or modeling copy elision. Like CFGConstructor, this is +/// for now only used by the analyzer's CFG. +class CFGCXXRecordTypedCall : public CFGStmt { +public: + /// Returns true when call expression \p CE needs to be represented + /// by CFGCXXRecordTypedCall, as opposed to a regular CFGStmt. + static bool isCXXRecordTypedCall(CallExpr *CE) { + return CE->getType().getCanonicalType()->getAsCXXRecordDecl(); + } + + explicit CFGCXXRecordTypedCall(CallExpr *CE, + const TemporaryObjectConstructionContext *C) + : CFGStmt(CE, CXXRecordTypedCall) { + assert(isCXXRecordTypedCall(CE)); + assert(C); + Data2.setPointer(const_cast(C)); + } + + const TemporaryObjectConstructionContext *getConstructionContext() const { + return static_cast( + Data2.getPointer()); + } + +private: + friend class CFGElement; + + CFGCXXRecordTypedCall() = default; + + static bool isKind(const CFGElement &E) { + return E.getKind() == CXXRecordTypedCall; + } +}; + /// CFGInitializer - Represents C++ base or member initializer from /// constructor's initialization list. class CFGInitializer : public CFGElement { @@ -840,6 +878,12 @@ Elements.push_back(CFGConstructor(CE, CC), C); } + void appendCXXRecordTypedCall(CallExpr *CE, + const TemporaryObjectConstructionContext *CC, + BumpVectorContext &C) { + Elements.push_back(CFGCXXRecordTypedCall(CE, CC), C); + } + void appendInitializer(CXXCtorInitializer *initializer, BumpVectorContext &C) { Elements.push_back(CFGInitializer(initializer), C); Index: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ lib/Analysis/CFG.cpp @@ -483,8 +483,8 @@ // Information about the currently visited C++ object construction site. // This is set in the construction trigger and read when the constructor - // itself is being visited. - llvm::DenseMap + // or a function that returns an object by value is being visited. + llvm::DenseMap ConstructionContextMap; using DeclsWithEndedScopeSetTy = llvm::SmallSetVector; @@ -673,7 +673,7 @@ // Remember to apply the construction context based on the current \p Layer // when constructing the CFG element for \p CE. void consumeConstructionContext(const ConstructionContextLayer *Layer, - CXXConstructExpr *CE); + Expr *E); // Scan \p Child statement to find constructors in it, while keeping in mind // that its parent statement is providing a partial construction context @@ -684,9 +684,9 @@ Stmt *Child); // Unset the construction context after consuming it. This is done immediately - // after adding the CFGConstructor element, so there's no need to - // do this manually in every Visit... function. - void cleanupConstructionContext(CXXConstructExpr *CE); + // after adding the CFGConstructor or CFGCXXRecordTypedCall element, so + // there's no need to do this manually in every Visit... function. + void cleanupConstructionContext(Expr *E); void autoCreateBlock() { if (!Block) Block = createBlock(); } CFGBlock *createBlock(bool add_successor = true); @@ -749,6 +749,27 @@ B->appendStmt(CE, cfg->getBumpVectorContext()); } + void appendCall(CFGBlock *B, CallExpr *CE) { + if (BuildOpts.AddRichCXXConstructors) { + if (CFGCXXRecordTypedCall::isCXXRecordTypedCall(CE)) { + if (const ConstructionContextLayer *Layer = + ConstructionContextMap.lookup(CE)) { + const ConstructionContext *CC = + ConstructionContext::createFromLayers(cfg->getBumpVectorContext(), + Layer); + B->appendCXXRecordTypedCall( + CE, cast(CC), + cfg->getBumpVectorContext()); + cleanupConstructionContext(CE); + return; + } + } + } + + // No valid construction context found. Fall back to statement. + B->appendStmt(CE, cfg->getBumpVectorContext()); + } + void appendInitializer(CFGBlock *B, CXXCtorInitializer *I) { B->appendInitializer(I, cfg->getBumpVectorContext()); } @@ -1209,16 +1230,16 @@ } void CFGBuilder::consumeConstructionContext( - const ConstructionContextLayer *Layer, CXXConstructExpr *CE) { + const ConstructionContextLayer *Layer, Expr *E) { if (const ConstructionContextLayer *PreviouslyStoredLayer = - ConstructionContextMap.lookup(CE)) { + ConstructionContextMap.lookup(E)) { (void)PreviouslyStoredLayer; // We might have visited this child when we were finding construction // contexts within its parents. assert(PreviouslyStoredLayer->isStrictlyMoreSpecificThan(Layer) && "Already within a different construction context!"); } else { - ConstructionContextMap[CE] = Layer; + ConstructionContextMap[E] = Layer; } } @@ -1236,6 +1257,18 @@ consumeConstructionContext(Layer, cast(Child)); break; } + // FIXME: This, like the main visit, doesn't support CUDAKernelCallExpr. + // FIXME: An isa<> would look much better but this whole switch is a + // workaround for an internal compiler error in MSVC 2015 (see r326021). + case Stmt::CallExprClass: + case Stmt::CXXMemberCallExprClass: + case Stmt::CXXOperatorCallExprClass: + case Stmt::UserDefinedLiteralClass: { + auto *CE = cast(Child); + if (CFGCXXRecordTypedCall::isCXXRecordTypedCall(CE)) + consumeConstructionContext(Layer, CE); + break; + } case Stmt::ExprWithCleanupsClass: { auto *Cleanups = cast(Child); findConstructionContexts(Layer, Cleanups->getSubExpr()); @@ -1277,12 +1310,12 @@ } } -void CFGBuilder::cleanupConstructionContext(CXXConstructExpr *CE) { +void CFGBuilder::cleanupConstructionContext(Expr *E) { assert(BuildOpts.AddRichCXXConstructors && "We should not be managing construction contexts!"); - assert(ConstructionContextMap.count(CE) && + assert(ConstructionContextMap.count(E) && "Cannot exit construction context without the context!"); - ConstructionContextMap.erase(CE); + ConstructionContextMap.erase(E); } @@ -2360,7 +2393,10 @@ } if (!NoReturn && !AddEHEdge) { - return VisitStmt(C, asc.withAlwaysAdd(true)); + autoCreateBlock(); + appendCall(Block, C); + + return VisitChildren(C); } if (Block) { @@ -2374,7 +2410,7 @@ else Block = createBlock(); - appendStmt(Block, C); + appendCall(Block, C); if (AddEHEdge) { // Add exceptional edges. @@ -4516,6 +4552,7 @@ case CFGElement::LifetimeEnds: case CFGElement::Statement: case CFGElement::Constructor: + case CFGElement::CXXRecordTypedCall: case CFGElement::ScopeBegin: case CFGElement::ScopeEnd: llvm_unreachable("getDestructorDecl should only be used with " @@ -4868,6 +4905,49 @@ OS << " (Member initializer)"; } +static void print_construction_context(raw_ostream &OS, + StmtPrinterHelper &Helper, + const ConstructionContext *CC) { + const Stmt *S1 = nullptr, *S2 = nullptr; + switch (CC->getKind()) { + case ConstructionContext::ConstructorInitializerKind: { + OS << ", "; + const auto *ICC = cast(CC); + print_initializer(OS, Helper, ICC->getCXXCtorInitializer()); + break; + } + case ConstructionContext::SimpleVariableKind: { + const auto *DSCC = cast(CC); + S1 = DSCC->getDeclStmt(); + break; + } + case ConstructionContext::NewAllocatedObjectKind: { + const auto *NECC = cast(CC); + S1 = NECC->getCXXNewExpr(); + break; + } + case ConstructionContext::ReturnedValueKind: { + const auto *RSCC = cast(CC); + S1 = RSCC->getReturnStmt(); + break; + } + case ConstructionContext::TemporaryObjectKind: { + const auto *TOCC = cast(CC); + S1 = TOCC->getCXXBindTemporaryExpr(); + S2 = TOCC->getMaterializedTemporaryExpr(); + break; + } + } + if (S1) { + OS << ", "; + Helper.handledStmt(const_cast(S1), OS); + } + if (S2) { + OS << ", "; + Helper.handledStmt(const_cast(S2), OS); + } +} + static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, const CFGElement &E) { if (Optional CS = E.getAs()) { @@ -4897,54 +4977,22 @@ } S->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts())); - if (isa(S)) { + if (auto VTC = E.getAs()) { + if (isa(S)) + OS << " (OperatorCall)"; + OS << " (CXXRecordTypedCall"; + print_construction_context(OS, Helper, VTC->getConstructionContext()); + OS << ")"; + } else if (isa(S)) { OS << " (OperatorCall)"; } else if (isa(S)) { OS << " (BindTemporary)"; } else if (const CXXConstructExpr *CCE = dyn_cast(S)) { - OS << " (CXXConstructExpr, "; + OS << " (CXXConstructExpr"; if (Optional CE = E.getAs()) { - const ConstructionContext *CC = CE->getConstructionContext(); - const Stmt *S1 = nullptr, *S2 = nullptr; - switch (CC->getKind()) { - case ConstructionContext::ConstructorInitializerKind: { - const auto *ICC = cast(CC); - print_initializer(OS, Helper, ICC->getCXXCtorInitializer()); - OS << ", "; - break; - } - case ConstructionContext::SimpleVariableKind: { - const auto *DSCC = cast(CC); - S1 = DSCC->getDeclStmt(); - break; - } - case ConstructionContext::NewAllocatedObjectKind: { - const auto *NECC = cast(CC); - S1 = NECC->getCXXNewExpr(); - break; - } - case ConstructionContext::ReturnedValueKind: { - const auto *RSCC = cast(CC); - S1 = RSCC->getReturnStmt(); - break; - } - case ConstructionContext::TemporaryObjectKind: { - const auto *TOCC = cast(CC); - S1 = TOCC->getCXXBindTemporaryExpr(); - S2 = TOCC->getMaterializedTemporaryExpr(); - break; - } - } - if (S1) { - Helper.handledStmt(const_cast(S1), OS); - OS << ", "; - } - if (S2) { - Helper.handledStmt(const_cast(S2), OS); - OS << ", "; - } + print_construction_context(OS, Helper, CE->getConstructionContext()); } - OS << CCE->getType().getAsString() << ")"; + OS << ", " << CCE->getType().getAsString() << ")"; } else if (const CastExpr *CE = dyn_cast(S)) { OS << " (" << CE->getStmtClassName() << ", " << CE->getCastKindName() Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -612,6 +612,7 @@ switch (E.getKind()) { case CFGElement::Statement: case CFGElement::Constructor: + case CFGElement::CXXRecordTypedCall: ProcessStmt(E.castAs().getStmt(), Pred); return; case CFGElement::Initializer: Index: lib/StaticAnalyzer/Core/PathDiagnostic.cpp =================================================================== --- lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -579,6 +579,7 @@ switch (Source.getKind()) { case CFGElement::Statement: case CFGElement::Constructor: + case CFGElement::CXXRecordTypedCall: return PathDiagnosticLocation(Source.castAs().getStmt(), SM, CallerCtx); case CFGElement::Initializer: { Index: test/Analysis/cfg-rich-constructors.cpp =================================================================== --- test/Analysis/cfg-rich-constructors.cpp +++ test/Analysis/cfg-rich-constructors.cpp @@ -97,7 +97,7 @@ // CHECK: void simpleVariableInitializedByValue() // CHECK: 1: C::get // CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void)) -// CHECK-NEXT: 3: [B1.2]() +// CHECK-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.4]) // CHECK-NEXT: 4: [B1.3] // CHECK-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.6], class C) // CHECK-NEXT: 6: C c = C::get(); @@ -114,7 +114,7 @@ // CHECK: [B2] // CHECK-NEXT: 1: C::get // CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void)) -// CHECK-NEXT: 3: [B2.2]() +// CHECK-NEXT: 3: [B2.2]() (CXXRecordTypedCall, [B2.4]) // CHECK-NEXT: 4: [B2.3] // CHECK-NEXT: 5: [B2.4] (CXXConstructExpr, [B1.2], class C) // CHECK: [B3] @@ -172,7 +172,7 @@ // CHECK: [B2] // CHECK-NEXT: 1: C::get // CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void)) -// CHECK-NEXT: 3: [B2.2]() +// CHECK-NEXT: 3: [B2.2]() (CXXRecordTypedCall, [B2.4]) // CHECK-NEXT: 4: [B2.3] // CHECK-NEXT: 5: [B2.4] (CXXConstructExpr, [B1.3], class C) // CHECK: [B3] @@ -217,14 +217,14 @@ // CHECK: D(double) // CHECK: 1: C::get // CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void)) -// CHECK-NEXT: 3: [B1.2]() +// CHECK-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.4]) // CHECK-NEXT: 4: [B1.3] // CHECK-NEXT: 5: [B1.4] (CXXConstructExpr, C([B1.4]) (Base initializer), class C) // CHECK-NEXT: 6: C([B1.5]) (Base initializer) // CHECK-NEXT: 7: CFGNewAllocator(C *) // CHECK-NEXT: 8: C::get // CHECK-NEXT: 9: [B1.8] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void)) -// CHECK-NEXT: 10: [B1.9]() +// CHECK-NEXT: 10: [B1.9]() (CXXRecordTypedCall, [B1.11]) // CHECK-NEXT: 11: [B1.10] // CHECK-NEXT: 12: [B1.11] (CXXConstructExpr, [B1.13], class C) // CHECK-NEXT: 13: new C([B1.12]) @@ -299,7 +299,7 @@ // CHECK: C returnTemporaryConstructedByFunction() // CHECK: 1: C::get // CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void)) -// CHECK-NEXT: 3: [B1.2]() +// CHECK-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.4]) // CHECK-NEXT: 4: [B1.3] // CHECK-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.6], class C) // CHECK-NEXT: 6: return [B1.5]; @@ -310,7 +310,7 @@ // CHECK: C returnChainOfCopies() // CHECK: 1: C::get // CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class C (*)(void)) -// CHECK-NEXT: 3: [B1.2]() +// CHECK-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.4]) // CHECK-NEXT: 4: [B1.3] // CHECK-NEXT: 5: [B1.4] (CXXConstructExpr, [B1.7], class C) // CHECK-NEXT: 6: C([B1.5]) (CXXFunctionalCastExpr, ConstructorConversion, class C) @@ -435,7 +435,7 @@ // CHECK: [B5] // CHECK-NEXT: 1: D::get // CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, FunctionToPointerDecay, class temporary_object_expr_with_dtors::D (*)(void)) -// CHECK-NEXT: 3: [B5.2]() +// CHECK-NEXT: 3: [B5.2]() (CXXRecordTypedCall, [B5.4], [B5.6]) // CHECK-NEXT: 4: [B5.3] (BindTemporary) // CHECK-NEXT: 5: [B5.4] (ImplicitCastExpr, NoOp, const class temporary_object_expr_with_dtors::D) // CHECK-NEXT: 6: [B5.5] @@ -518,7 +518,7 @@ // CHECK: void implicitConstructionConversionFromFunctionValue() // CHECK: 1: get // CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class implicit_constructor_conversion::A (*)(void)) -// CHECK-NEXT: 3: [B1.2]() +// CHECK-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.5]) // CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A) // CHECK-NEXT: 5: [B1.4] // CHECK-NEXT: 6: [B1.5] (CXXConstructExpr, [B1.8], [B1.10], class implicit_constructor_conversion::B) @@ -552,7 +552,7 @@ // CHECK: void implicitConstructionConversionFromFunctionValueWithLifetimeExtension() // CHECK: 1: get // CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class implicit_constructor_conver -// CHECK-NEXT: 3: [B1.2]() +// CHECK-NEXT: 3: [B1.2]() (CXXRecordTypedCall, [B1.5]) // CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, NoOp, const class implicit_constructor_conversion::A) // CHECK-NEXT: 5: [B1.4] // CHECK-NEXT: 6: [B1.5] (CXXConstructExpr, [B1.9], class implicit_constructor_conversion::B) Index: test/Analysis/temp-obj-dtors-cfg-output.cpp =================================================================== --- test/Analysis/temp-obj-dtors-cfg-output.cpp +++ test/Analysis/temp-obj-dtors-cfg-output.cpp @@ -1111,7 +1111,8 @@ // CHECK: [B1] // CHECK: 1: A::make // CHECK: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class A (*)(void)) -// CHECK: 3: [B1.2]() +// WARNINGS: 3: [B1.2]() +// ANALYZER: 3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6]) // CHECK: 4: [B1.3] (BindTemporary) // CHECK: 5: [B1.4] (ImplicitCastExpr, NoOp, const class A) // CHECK: 6: [B1.5] @@ -1130,7 +1131,8 @@ // CHECK: [B1] // CHECK: 1: A::make // CHECK: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class A (*)(void)) -// CHECK: 3: [B1.2]() +// WARNINGS: 3: [B1.2]() +// ANALYZER: 3: [B1.2]() (CXXRecordTypedCall, [B1.4], [B1.6]) // CHECK: 4: [B1.3] (BindTemporary) // CHECK: 5: [B1.4] (ImplicitCastExpr, NoOp, const class A) // CHECK: 6: [B1.5] @@ -1139,7 +1141,8 @@ // CHECK: 9: [B1.8] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &)) // CHECK: 10: A::make // CHECK: 11: [B1.10] (ImplicitCastExpr, FunctionToPointerDecay, class A (*)(void)) -// CHECK: 12: [B1.11]() +// WARNINGS: 12: [B1.11]() +// ANALYZER: 12: [B1.11]() (CXXRecordTypedCall, [B1.13], [B1.15]) // CHECK: 13: [B1.12] (BindTemporary) // CHECK: 14: [B1.13] (ImplicitCastExpr, NoOp, const class A) // CHECK: 15: [B1.14]