[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:
parent
0df8935c23
commit
5a281bba40
|
@ -145,19 +145,30 @@ protected:
|
||||||
// necessary to express what memory is being initialized by
|
// necessary to express what memory is being initialized by
|
||||||
// the construction.
|
// the construction.
|
||||||
class ConstructionContext {
|
class ConstructionContext {
|
||||||
|
public:
|
||||||
|
typedef llvm::PointerUnion<Stmt *, CXXCtorInitializer *> TriggerTy;
|
||||||
|
|
||||||
|
private:
|
||||||
// The construction site - the statement that triggered the construction
|
// The construction site - the statement that triggered the construction
|
||||||
// for one of its parts. For instance, stack variable declaration statement
|
// for one of its parts. For instance, stack variable declaration statement
|
||||||
// triggers construction of itself or its elements if it's an array,
|
// triggers construction of itself or its elements if it's an array,
|
||||||
// new-expression triggers construction of the newly allocated object(s).
|
// new-expression triggers construction of the newly allocated object(s).
|
||||||
Stmt *Trigger = nullptr;
|
TriggerTy Trigger;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ConstructionContext() = default;
|
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 {
|
const ConstructionContext *getPersistentCopy(BumpVectorContext &C) const {
|
||||||
ConstructionContext *CC = C.getAllocator().Allocate<ConstructionContext>();
|
ConstructionContext *CC = C.getAllocator().Allocate<ConstructionContext>();
|
||||||
|
@ -185,6 +196,10 @@ public:
|
||||||
return getConstructionContext()->getTriggerStmt();
|
return getConstructionContext()->getTriggerStmt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CXXCtorInitializer *getTriggerInit() const {
|
||||||
|
return getConstructionContext()->getTriggerInit();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class CFGElement;
|
friend class CFGElement;
|
||||||
|
|
||||||
|
|
|
@ -654,7 +654,8 @@ private:
|
||||||
// to the trigger statement. The construction context will be unset once
|
// to the trigger statement. The construction context will be unset once
|
||||||
// it is consumed when the CFG building procedure processes the
|
// it is consumed when the CFG building procedure processes the
|
||||||
// construct-expression and adds the respective CFGConstructor element.
|
// 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
|
// Unset the construction context after consuming it. This is done immediately
|
||||||
// after adding the CFGConstructor element, so there's no need to
|
// after adding the CFGConstructor element, so there's no need to
|
||||||
// do this manually in every Visit... function.
|
// do this manually in every Visit... function.
|
||||||
|
@ -1147,8 +1148,8 @@ static const VariableArrayType *FindVA(const Type *t) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFGBuilder::EnterConstructionContextIfNecessary(Stmt *Trigger,
|
void CFGBuilder::EnterConstructionContextIfNecessary(
|
||||||
Stmt *Child) {
|
ConstructionContext::TriggerTy Trigger, Stmt *Child) {
|
||||||
if (!BuildOpts.AddRichCXXConstructors)
|
if (!BuildOpts.AddRichCXXConstructors)
|
||||||
return;
|
return;
|
||||||
if (!Child)
|
if (!Child)
|
||||||
|
@ -1294,6 +1295,8 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) {
|
||||||
appendInitializer(Block, I);
|
appendInitializer(Block, I);
|
||||||
|
|
||||||
if (Init) {
|
if (Init) {
|
||||||
|
EnterConstructionContextIfNecessary(I, Init);
|
||||||
|
|
||||||
if (HasTemporaries) {
|
if (HasTemporaries) {
|
||||||
// For expression with temporaries go directly to subexpression to omit
|
// For expression with temporaries go directly to subexpression to omit
|
||||||
// generating destructors for the second time.
|
// generating destructors for the second time.
|
||||||
|
@ -4605,6 +4608,27 @@ public:
|
||||||
|
|
||||||
} // namespace
|
} // 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,
|
static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
|
||||||
const CFGElement &E) {
|
const CFGElement &E) {
|
||||||
if (Optional<CFGStmt> CS = E.getAs<CFGStmt>()) {
|
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 (Optional<CFGConstructor> CE = E.getAs<CFGConstructor>()) {
|
||||||
if (const Stmt *S = CE->getTriggerStmt())
|
if (const Stmt *S = CE->getTriggerStmt())
|
||||||
Helper.handledStmt((const_cast<Stmt *>(S)), OS);
|
Helper.handledStmt((const_cast<Stmt *>(S)), OS);
|
||||||
|
else if (const CXXCtorInitializer *I = CE->getTriggerInit())
|
||||||
|
print_initializer(OS, Helper, I);
|
||||||
else
|
else
|
||||||
llvm_unreachable("Unexpected trigger kind!");
|
llvm_unreachable("Unexpected trigger kind!");
|
||||||
OS << ", ";
|
OS << ", ";
|
||||||
|
@ -4659,23 +4685,8 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
|
||||||
if (isa<Expr>(S))
|
if (isa<Expr>(S))
|
||||||
OS << '\n';
|
OS << '\n';
|
||||||
} else if (Optional<CFGInitializer> IE = E.getAs<CFGInitializer>()) {
|
} else if (Optional<CFGInitializer> IE = E.getAs<CFGInitializer>()) {
|
||||||
const CXXCtorInitializer *I = IE->getInitializer();
|
print_initializer(OS, Helper, IE->getInitializer());
|
||||||
if (I->isBaseInitializer())
|
OS << '\n';
|
||||||
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";
|
|
||||||
} else if (Optional<CFGAutomaticObjDtor> DE =
|
} else if (Optional<CFGAutomaticObjDtor> DE =
|
||||||
E.getAs<CFGAutomaticObjDtor>()) {
|
E.getAs<CFGAutomaticObjDtor>()) {
|
||||||
const VarDecl *VD = DE->getVarDecl();
|
const VarDecl *VD = DE->getVarDecl();
|
||||||
|
|
|
@ -126,3 +126,28 @@ void referenceVariableWithInitializer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace decl_stmt
|
} // 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
|
||||||
|
|
|
@ -63,13 +63,17 @@ class TestDelegating {
|
||||||
// CHECK: [B2 (ENTRY)]
|
// CHECK: [B2 (ENTRY)]
|
||||||
// CHECK: Succs (1): B1
|
// CHECK: Succs (1): B1
|
||||||
// CHECK: [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: 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: 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: 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: 8: A([B1.7]) (Base initializer)
|
||||||
// CHECK: 9: /*implicit*/(int)0
|
// CHECK: 9: /*implicit*/(int)0
|
||||||
// CHECK: 10: i([B1.9]) (Member initializer)
|
// CHECK: 10: i([B1.9]) (Member initializer)
|
||||||
|
@ -118,7 +122,8 @@ class TestDelegating {
|
||||||
// CHECK: [B1]
|
// CHECK: [B1]
|
||||||
// CHECK: 1: 2
|
// CHECK: 1: 2
|
||||||
// CHECK: 2: 3
|
// 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: 4: TestDelegating([B1.3]) (Delegating initializer)
|
||||||
// CHECK: Preds (1): B2
|
// CHECK: Preds (1): B2
|
||||||
// CHECK: Succs (1): B0
|
// CHECK: Succs (1): B0
|
||||||
|
|
Loading…
Reference in New Issue