class BinaryOperator;
class CFG;
class ConstructionContext;
+class TemporaryObjectConstructionContext;
class CXXBaseSpecifier;
class CXXBindTemporaryExpr;
class CXXCtorInitializer;
// stmt kind
Statement,
Constructor,
+ CXXRecordTypedCall,
STMT_BEGIN = Statement,
- STMT_END = Constructor,
+ STMT_END = CXXRecordTypedCall,
// dtor kind
AutomaticObjectDtor,
DeleteDtor,
return static_cast<ConstructionContext *>(Data2.getPointer());
}
- QualType getType() const {
- return cast<CXXConstructExpr>(getStmt())->getType();
- }
-
private:
friend class CFGElement;
}
};
+/// 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<TemporaryObjectConstructionContext *>(C));
+ }
+
+ const TemporaryObjectConstructionContext *getConstructionContext() const {
+ return static_cast<TemporaryObjectConstructionContext *>(
+ 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 {
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);
// 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<CXXConstructExpr *, const ConstructionContextLayer *>
+ // or a function that returns an object by value is being visited.
+ llvm::DenseMap<Expr *, const ConstructionContextLayer *>
ConstructionContextMap;
using DeclsWithEndedScopeSetTy = llvm::SmallSetVector<VarDecl *, 16>;
// 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
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);
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<TemporaryObjectConstructionContext>(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());
}
}
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;
}
}
consumeConstructionContext(Layer, cast<CXXConstructExpr>(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<CallExpr>(Child);
+ if (CFGCXXRecordTypedCall::isCXXRecordTypedCall(CE))
+ consumeConstructionContext(Layer, CE);
+ break;
+ }
case Stmt::ExprWithCleanupsClass: {
auto *Cleanups = cast<ExprWithCleanups>(Child);
findConstructionContexts(Layer, Cleanups->getSubExpr());
}
}
-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);
}
}
if (!NoReturn && !AddEHEdge) {
- return VisitStmt(C, asc.withAlwaysAdd(true));
+ autoCreateBlock();
+ appendCall(Block, C);
+
+ return VisitChildren(C);
}
if (Block) {
else
Block = createBlock();
- appendStmt(Block, C);
+ appendCall(Block, C);
if (AddEHEdge) {
// Add exceptional edges.
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 "
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<ConstructorInitializerConstructionContext>(CC);
+ print_initializer(OS, Helper, ICC->getCXXCtorInitializer());
+ break;
+ }
+ case ConstructionContext::SimpleVariableKind: {
+ const auto *DSCC = cast<SimpleVariableConstructionContext>(CC);
+ S1 = DSCC->getDeclStmt();
+ break;
+ }
+ case ConstructionContext::NewAllocatedObjectKind: {
+ const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
+ S1 = NECC->getCXXNewExpr();
+ break;
+ }
+ case ConstructionContext::ReturnedValueKind: {
+ const auto *RSCC = cast<ReturnedValueConstructionContext>(CC);
+ S1 = RSCC->getReturnStmt();
+ break;
+ }
+ case ConstructionContext::TemporaryObjectKind: {
+ const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
+ S1 = TOCC->getCXXBindTemporaryExpr();
+ S2 = TOCC->getMaterializedTemporaryExpr();
+ break;
+ }
+ }
+ if (S1) {
+ OS << ", ";
+ Helper.handledStmt(const_cast<Stmt *>(S1), OS);
+ }
+ if (S2) {
+ OS << ", ";
+ Helper.handledStmt(const_cast<Stmt *>(S2), OS);
+ }
+}
+
static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
const CFGElement &E) {
if (Optional<CFGStmt> CS = E.getAs<CFGStmt>()) {
}
S->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts()));
- if (isa<CXXOperatorCallExpr>(S)) {
+ if (auto VTC = E.getAs<CFGCXXRecordTypedCall>()) {
+ if (isa<CXXOperatorCallExpr>(S))
+ OS << " (OperatorCall)";
+ OS << " (CXXRecordTypedCall";
+ print_construction_context(OS, Helper, VTC->getConstructionContext());
+ OS << ")";
+ } else if (isa<CXXOperatorCallExpr>(S)) {
OS << " (OperatorCall)";
} else if (isa<CXXBindTemporaryExpr>(S)) {
OS << " (BindTemporary)";
} else if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(S)) {
- OS << " (CXXConstructExpr, ";
+ OS << " (CXXConstructExpr";
if (Optional<CFGConstructor> CE = E.getAs<CFGConstructor>()) {
- const ConstructionContext *CC = CE->getConstructionContext();
- const Stmt *S1 = nullptr, *S2 = nullptr;
- switch (CC->getKind()) {
- case ConstructionContext::ConstructorInitializerKind: {
- const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
- print_initializer(OS, Helper, ICC->getCXXCtorInitializer());
- OS << ", ";
- break;
- }
- case ConstructionContext::SimpleVariableKind: {
- const auto *DSCC = cast<SimpleVariableConstructionContext>(CC);
- S1 = DSCC->getDeclStmt();
- break;
- }
- case ConstructionContext::NewAllocatedObjectKind: {
- const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
- S1 = NECC->getCXXNewExpr();
- break;
- }
- case ConstructionContext::ReturnedValueKind: {
- const auto *RSCC = cast<ReturnedValueConstructionContext>(CC);
- S1 = RSCC->getReturnStmt();
- break;
- }
- case ConstructionContext::TemporaryObjectKind: {
- const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
- S1 = TOCC->getCXXBindTemporaryExpr();
- S2 = TOCC->getMaterializedTemporaryExpr();
- break;
- }
- }
- if (S1) {
- Helper.handledStmt(const_cast<Stmt *>(S1), OS);
- OS << ", ";
- }
- if (S2) {
- Helper.handledStmt(const_cast<Stmt *>(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<CastExpr>(S)) {
OS << " (" << CE->getStmtClassName() << ", "
<< CE->getCastKindName()
// 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();
// 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]
// 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]
// 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])
// 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];
// 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)
// 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]
// 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)
// 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)