Skip to content

Commit

Permalink
[CFG] Provide construction contexts for lifetime-extended temporaries.
Browse files Browse the repository at this point in the history
When constructing a temporary that is going to be lifetime-extended through a
MaterializeTemporaryExpr later, CFG elements for the respective constructor
can now be queried to obtain the reference to that MaterializeTemporaryExpr
and therefore gain information about lifetime extension.

This may produce multi-layered construction contexts when information about
both temporary destruction and lifetime extension is available.

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

llvm-svn: 326014
  • Loading branch information
haoNoQ committed Feb 24, 2018
1 parent c9f0b65 commit f43ac4c
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 11 deletions.
15 changes: 15 additions & 0 deletions clang/include/clang/Analysis/CFG.h
Expand Up @@ -193,6 +193,17 @@ class ConstructionContext {
return Trigger.dyn_cast<CXXCtorInitializer *>();
}

const MaterializeTemporaryExpr *getMaterializedTemporary() const {
// TODO: Be more careful to ensure that there's only one MTE around.
for (const ConstructionContext *CC = this; CC; CC = CC->getParent()) {
if (const auto *MTE = dyn_cast_or_null<MaterializeTemporaryExpr>(
CC->getTriggerStmt())) {
return MTE;
}
}
return nullptr;
}

bool isSameAsPartialContext(const ConstructionContext *Other) const {
assert(Other);
return (Trigger == Other->Trigger);
Expand Down Expand Up @@ -248,6 +259,10 @@ class CFGConstructor : public CFGStmt {
return getConstructionContext()->getTriggerInit();
}

const MaterializeTemporaryExpr *getMaterializedTemporary() const {
return getConstructionContext()->getMaterializedTemporary();
}

private:
friend class CFGElement;

Expand Down
25 changes: 24 additions & 1 deletion clang/lib/Analysis/CFG.cpp
Expand Up @@ -548,6 +548,8 @@ class CFGBuilder {
Stmt *Term,
CFGBlock *TrueBlock,
CFGBlock *FalseBlock);
CFGBlock *VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *MTE,
AddStmtChoice asc);
CFGBlock *VisitMemberExpr(MemberExpr *M, AddStmtChoice asc);
CFGBlock *VisitObjCAtCatchStmt(ObjCAtCatchStmt *S);
CFGBlock *VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *S);
Expand Down Expand Up @@ -1840,6 +1842,10 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
case Stmt::LambdaExprClass:
return VisitLambdaExpr(cast<LambdaExpr>(S), asc);

case Stmt::MaterializeTemporaryExprClass:
return VisitMaterializeTemporaryExpr(cast<MaterializeTemporaryExpr>(S),
asc);

case Stmt::MemberExprClass:
return VisitMemberExpr(cast<MemberExpr>(S), asc);

Expand Down Expand Up @@ -2975,6 +2981,16 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) {
return EntryConditionBlock;
}

CFGBlock *
CFGBuilder::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *MTE,
AddStmtChoice asc) {
findConstructionContexts(
ConstructionContext::create(cfg->getBumpVectorContext(), MTE),
MTE->getTemporary());

return VisitStmt(MTE, asc);
}

CFGBlock *CFGBuilder::VisitMemberExpr(MemberExpr *M, AddStmtChoice asc) {
if (asc.alwaysAdd(*this, M)) {
autoCreateBlock();
Expand Down Expand Up @@ -4706,13 +4722,20 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
} else if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(S)) {
OS << " (CXXConstructExpr, ";
if (Optional<CFGConstructor> CE = E.getAs<CFGConstructor>()) {
// TODO: Refactor into ConstructionContext::print().
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
llvm_unreachable("Unexpected trigger kind!");
OS << ", ";
if (const Stmt *S = CE->getMaterializedTemporary()) {
if (S != CE->getTriggerStmt()) {
Helper.handledStmt(const_cast<Stmt *>(S), OS);
OS << ", ";
}
}
}
OS << CCE->getType().getAsString() << ")";
} else if (const CastExpr *CE = dyn_cast<CastExpr>(S)) {
Expand Down
8 changes: 3 additions & 5 deletions clang/test/Analysis/cfg-rich-constructors.cpp
Expand Up @@ -133,11 +133,10 @@ void simpleVariableWithTernaryOperator(bool coin) {
C c = coin ? C::get() : C(0);
}

// TODO: Should find construction target here.
// CHECK: void referenceVariableWithConstructor()
// CHECK: 1: 0
// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, NullToPointer, class C *)
// CHECK-NEXT: 3: [B1.2] (CXXConstructExpr, const class C)
// CHECK-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.4], const class C)
// CHECK-NEXT: 4: [B1.3]
// CHECK-NEXT: 5: const C &c(0);
void referenceVariableWithConstructor() {
Expand Down Expand Up @@ -267,9 +266,8 @@ C returnBracesWithMultipleItems() {
return {123, 456};
}

// TODO: Should find construction targets for the first constructor as well.
// CHECK: C returnTemporary()
// CHECK: 1: C() (CXXConstructExpr, class C)
// CHECK: 1: C() (CXXConstructExpr, [B1.2], class C)
// CHECK-NEXT: 2: [B1.1]
// CHECK-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.4], class C)
// CHECK-NEXT: 4: return [B1.3];
Expand Down Expand Up @@ -400,7 +398,7 @@ void temporaryInCondition() {

// CHECK: void referenceVariableWithConstructor()
// CHECK: 1: 0
// CHECK-NEXT: 2: [B1.1] (CXXConstructExpr, [B1.3], const class temporary_object_expr_with_dtors::D)
// CHECK-NEXT: 2: [B1.1] (CXXConstructExpr, [B1.3], [B1.4], const class temporary_object_expr_with_dtors::D)
// CHECK-NEXT: 3: [B1.2] (BindTemporary)
// CHECK-NEXT: 4: [B1.3]
// CHECK-NEXT: 5: const temporary_object_expr_with_dtors::D &d(0);
Expand Down
16 changes: 11 additions & 5 deletions clang/test/Analysis/temp-obj-dtors-cfg-output.cpp
Expand Up @@ -714,7 +714,9 @@ int testConsistencyNestedNormalReturn(bool value) {
// CHECK: Preds (1): B3
// CHECK: Succs (1): B0
// CHECK: [B3]
// CHECK: 1: D() (CXXConstructExpr, struct D)
// WARNINGS: 1: D() (CXXConstructExpr, struct D)
// CXX98-ANALYZER: 1: D() (CXXConstructExpr, struct D)
// CXX11-ANALYZER: 1: D() (CXXConstructExpr, [B3.2], struct D)
// CXX98: 2: [B3.1] (ImplicitCastExpr, NoOp, const struct D)
// CXX98: 3: [B3.2]
// CXX98-WARNINGS: 4: [B3.3] (CXXConstructExpr, struct D)
Expand Down Expand Up @@ -925,7 +927,8 @@ int testConsistencyNestedNormalReturn(bool value) {
// CHECK: Succs (1): B4
// CHECK: [B7]
// WARNINGS: 1: A() (CXXConstructExpr, class A)
// ANALYZER: 1: A() (CXXConstructExpr, [B7.2], class A)
// ANALYZER-CXX98: 1: A() (CXXConstructExpr, [B7.2], [B7.3], class A)
// ANALYZER-CXX11: 1: A() (CXXConstructExpr, [B7.2], class A)
// CHECK: 2: [B7.1] (BindTemporary)
// CXX98: 3: [B7.2].operator bool
// CXX98: 4: [B7.2]
Expand Down Expand Up @@ -992,7 +995,8 @@ int testConsistencyNestedNormalReturn(bool value) {
// CHECK: 1: foo
// CHECK: 2: [B7.1] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &))
// WARNINGS: 3: A() (CXXConstructExpr, class A)
// ANALYZER: 3: A() (CXXConstructExpr, [B7.4], class A)
// ANALYZER-CXX98: 3: A() (CXXConstructExpr, [B7.4], class A)
// ANALYZER-CXX11: 3: A() (CXXConstructExpr, class A)
// CHECK: 4: [B7.3] (BindTemporary)
// CXX98: 5: [B7.4].operator bool
// CXX98: 6: [B7.4]
Expand Down Expand Up @@ -1043,7 +1047,8 @@ int testConsistencyNestedNormalReturn(bool value) {
// CHECK: Succs (1): B9
// CHECK: [B12]
// WARNINGS: 1: A() (CXXConstructExpr, class A)
// ANALYZER: 1: A() (CXXConstructExpr, [B12.2], class A)
// ANALYZER-CXX98: 1: A() (CXXConstructExpr, [B12.2], [B12.3], class A)
// ANALYZER-CXX11: 1: A() (CXXConstructExpr, [B12.2], class A)
// CHECK: 2: [B12.1] (BindTemporary)
// CXX98: 3: [B12.2].operator bool
// CXX98: 4: [B12.2]
Expand Down Expand Up @@ -1199,7 +1204,8 @@ int testConsistencyNestedNormalReturn(bool value) {
// CHECK: [B2 (NORETURN)]
// CHECK: 1: int a;
// WARNINGS: 2: NoReturn() (CXXConstructExpr, class NoReturn)
// ANALYZER: 2: NoReturn() (CXXConstructExpr, [B2.3], class NoReturn)
// ANALYZER-CXX98: 2: NoReturn() (CXXConstructExpr, [B2.3], [B2.4], class NoReturn)
// ANALYZER-CXX11: 2: NoReturn() (CXXConstructExpr, [B2.3], class NoReturn)
// CHECK: 3: [B2.2] (BindTemporary)
// CHECK: [[MEMBER:[45]]]: [B2.{{[34]}}].f
// CHECK: {{[56]}}: [B2.[[MEMBER]]]()
Expand Down

0 comments on commit f43ac4c

Please sign in to comment.