[CFG] Add construction context for constructor initializers.

CFG elements for constructors of fields and base classes that are being
initialized before the body of the whole-class constructor starts can now be
queried to discover that they're indeed participating in initialization of their
respective fields or bases before the whole-class constructor kicks in.

CFG construction contexts are now capable of representing CXXCtorInitializer
triggers, which aren't considered to be statements in the Clang AST.

Differential Revision: https://reviews.llvm.org/D42700

llvm-svn: 324796
This commit is contained in:
Artem Dergachev 2018-02-10 02:18:04 +00:00
parent 0df8935c23
commit 5a281bba40
4 changed files with 85 additions and 29 deletions

View File

@ -145,19 +145,30 @@ protected:
// necessary to express what memory is being initialized by
// the construction.
class ConstructionContext {
public:
typedef llvm::PointerUnion<Stmt *, CXXCtorInitializer *> TriggerTy;
private:
// The construction site - the statement that triggered the construction
// for one of its parts. For instance, stack variable declaration statement
// triggers construction of itself or its elements if it's an array,
// new-expression triggers construction of the newly allocated object(s).
Stmt *Trigger = nullptr;
TriggerTy Trigger;
public:
ConstructionContext() = default;
ConstructionContext(Stmt *Trigger) : Trigger(Trigger) {}
ConstructionContext(TriggerTy Trigger)
: Trigger(Trigger) {}
bool isNull() const { return Trigger == nullptr; }
bool isNull() const { return Trigger.isNull(); }
const Stmt *getTriggerStmt() const { return Trigger; }
const Stmt *getTriggerStmt() const {
return Trigger.dyn_cast<Stmt *>();
}
const CXXCtorInitializer *getTriggerInit() const {
return Trigger.dyn_cast<CXXCtorInitializer *>();
}
const ConstructionContext *getPersistentCopy(BumpVectorContext &C) const {
ConstructionContext *CC = C.getAllocator().Allocate<ConstructionContext>();
@ -185,6 +196,10 @@ public:
return getConstructionContext()->getTriggerStmt();
}
const CXXCtorInitializer *getTriggerInit() const {
return getConstructionContext()->getTriggerInit();
}
private:
friend class CFGElement;

View File

@ -654,7 +654,8 @@ private:
// to the trigger statement. The construction context will be unset once
// it is consumed when the CFG building procedure processes the
// construct-expression and adds the respective CFGConstructor element.
void EnterConstructionContextIfNecessary(Stmt *Trigger, Stmt *Child);
void EnterConstructionContextIfNecessary(
ConstructionContext::TriggerTy Trigger, 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.
@ -1147,8 +1148,8 @@ static const VariableArrayType *FindVA(const Type *t) {
return nullptr;
}
void CFGBuilder::EnterConstructionContextIfNecessary(Stmt *Trigger,
Stmt *Child) {
void CFGBuilder::EnterConstructionContextIfNecessary(
ConstructionContext::TriggerTy Trigger, Stmt *Child) {
if (!BuildOpts.AddRichCXXConstructors)
return;
if (!Child)
@ -1294,6 +1295,8 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) {
appendInitializer(Block, I);
if (Init) {
EnterConstructionContextIfNecessary(I, Init);
if (HasTemporaries) {
// For expression with temporaries go directly to subexpression to omit
// generating destructors for the second time.
@ -4605,6 +4608,27 @@ public:
} // namespace
static void print_initializer(raw_ostream &OS, StmtPrinterHelper &Helper,
const CXXCtorInitializer *I) {
if (I->isBaseInitializer())
OS << I->getBaseClass()->getAsCXXRecordDecl()->getName();
else if (I->isDelegatingInitializer())
OS << I->getTypeSourceInfo()->getType()->getAsCXXRecordDecl()->getName();
else
OS << I->getAnyMember()->getName();
OS << "(";
if (Expr *IE = I->getInit())
IE->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts()));
OS << ")";
if (I->isBaseInitializer())
OS << " (Base initializer)";
else if (I->isDelegatingInitializer())
OS << " (Delegating initializer)";
else
OS << " (Member initializer)";
}
static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
const CFGElement &E) {
if (Optional<CFGStmt> CS = E.getAs<CFGStmt>()) {
@ -4643,6 +4667,8 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
if (Optional<CFGConstructor> CE = E.getAs<CFGConstructor>()) {
if (const Stmt *S = CE->getTriggerStmt())
Helper.handledStmt((const_cast<Stmt *>(S)), OS);
else if (const CXXCtorInitializer *I = CE->getTriggerInit())
print_initializer(OS, Helper, I);
else
llvm_unreachable("Unexpected trigger kind!");
OS << ", ";
@ -4659,23 +4685,8 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
if (isa<Expr>(S))
OS << '\n';
} else if (Optional<CFGInitializer> IE = E.getAs<CFGInitializer>()) {
const CXXCtorInitializer *I = IE->getInitializer();
if (I->isBaseInitializer())
OS << I->getBaseClass()->getAsCXXRecordDecl()->getName();
else if (I->isDelegatingInitializer())
OS << I->getTypeSourceInfo()->getType()->getAsCXXRecordDecl()->getName();
else OS << I->getAnyMember()->getName();
OS << "(";
if (Expr *IE = I->getInit())
IE->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts()));
OS << ")";
if (I->isBaseInitializer())
OS << " (Base initializer)\n";
else if (I->isDelegatingInitializer())
OS << " (Delegating initializer)\n";
else OS << " (Member initializer)\n";
print_initializer(OS, Helper, IE->getInitializer());
OS << '\n';
} else if (Optional<CFGAutomaticObjDtor> DE =
E.getAs<CFGAutomaticObjDtor>()) {
const VarDecl *VD = DE->getVarDecl();

View File

@ -126,3 +126,28 @@ void referenceVariableWithInitializer() {
}
} // end namespace decl_stmt
namespace ctor_initializers {
class D: public C {
C c1;
public:
// CHECK: D()
// CHECK: 1: (CXXConstructExpr, C() (Base initializer), class C)
// CHECK-NEXT: 2: C([B1.1]) (Base initializer)
// CHECK-NEXT: 3: CFGNewAllocator(C *)
// CHECK-NEXT: 4: (CXXConstructExpr, [B1.5], class C)
// CHECK-NEXT: 5: new C([B1.4])
// CHECK-NEXT: 6: [B1.5] (CXXConstructExpr, c1([B1.5]) (Member initializer), class C)
// CHECK-NEXT: 7: c1([B1.6]) (Member initializer)
D(): C(), c1(new C()) {}
// CHECK: D(int)
// CHECK: 1: (CXXConstructExpr, D() (Delegating initializer), class ctor_initializers::D)
// CHECK-NEXT: 2: D([B1.1]) (Delegating initializer)
D(int): D() {}
};
} // end namespace ctor_initializers

View File

@ -63,13 +63,17 @@ class TestDelegating {
// CHECK: [B2 (ENTRY)]
// CHECK: Succs (1): B1
// CHECK: [B1]
// CHECK: 1: (CXXConstructExpr, class A)
// WARNINGS: 1: (CXXConstructExpr, class A)
// ANALYZER: 1: (CXXConstructExpr, A() (Base initializer), class A)
// CHECK: 2: A([B1.1]) (Base initializer)
// CHECK: 3: (CXXConstructExpr, class C)
// WARNINGS: 3: (CXXConstructExpr, class C)
// ANALYZER: 3: (CXXConstructExpr, C() (Base initializer), class C)
// CHECK: 4: C([B1.3]) (Base initializer)
// CHECK: 5: (CXXConstructExpr, class B)
// WARNINGS: 5: (CXXConstructExpr, class B)
// ANALYZER: 5: (CXXConstructExpr, B() (Base initializer), class B)
// CHECK: 6: B([B1.5]) (Base initializer)
// CHECK: 7: (CXXConstructExpr, class A)
// WARNINGS: 7: (CXXConstructExpr, class A)
// ANALYZER: 7: (CXXConstructExpr, A() (Base initializer), class A)
// CHECK: 8: A([B1.7]) (Base initializer)
// CHECK: 9: /*implicit*/(int)0
// CHECK: 10: i([B1.9]) (Member initializer)
@ -118,7 +122,8 @@ class TestDelegating {
// CHECK: [B1]
// CHECK: 1: 2
// CHECK: 2: 3
// CHECK: 3: [B1.1], [B1.2] (CXXConstructExpr, class TestDelegating)
// WARNINGS: 3: [B1.1], [B1.2] (CXXConstructExpr, class TestDelegating)
// ANALYZER: 3: [B1.1], [B1.2] (CXXConstructExpr, TestDelegating([B1.1], [B1.2]) (Delegating initializer), class TestDelegating)
// CHECK: 4: TestDelegating([B1.3]) (Delegating initializer)
// CHECK: Preds (1): B2
// CHECK: Succs (1): B0