Skip to content

Commit 905b402

Browse files
authored
[Analyzer][CFG] Correctly handle rebuilt default arg and default init expression (#91879)
Depends on #92527 Clang now support the following: - Extending lifetime of object bound to reference members of aggregates, that are created from default member initializer. - Rebuild `CXXDefaultArgExpr` and `CXXDefaultInitExpr` as needed where called or constructed. But CFG and ExprEngine need to be updated to address this change. This PR add `CXXDefaultArgExpr` and `CXXDefaultInitExpr` into CFG, and correct handle these expressions in ExprEngine --------- Signed-off-by: yronglin <[email protected]>
1 parent 5064ff9 commit 905b402

File tree

6 files changed

+103
-42
lines changed

6 files changed

+103
-42
lines changed

clang/lib/AST/ParentMap.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,22 @@ static void BuildParentMap(MapTy& M, Stmt* S,
9797
BuildParentMap(M, SubStmt, OVMode);
9898
}
9999
break;
100+
case Stmt::CXXDefaultArgExprClass:
101+
if (auto *Arg = dyn_cast<CXXDefaultArgExpr>(S)) {
102+
if (Arg->hasRewrittenInit()) {
103+
M[Arg->getExpr()] = S;
104+
BuildParentMap(M, Arg->getExpr(), OVMode);
105+
}
106+
}
107+
break;
108+
case Stmt::CXXDefaultInitExprClass:
109+
if (auto *Init = dyn_cast<CXXDefaultInitExpr>(S)) {
110+
if (Init->hasRewrittenInit()) {
111+
M[Init->getExpr()] = S;
112+
BuildParentMap(M, Init->getExpr(), OVMode);
113+
}
114+
}
115+
break;
100116
default:
101117
for (Stmt *SubStmt : S->children()) {
102118
if (SubStmt) {

clang/lib/Analysis/CFG.cpp

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,10 @@ class CFGBuilder {
556556

557557
private:
558558
// Visitors to walk an AST and construct the CFG.
559+
CFGBlock *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *Default,
560+
AddStmtChoice asc);
561+
CFGBlock *VisitCXXDefaultInitExpr(CXXDefaultInitExpr *Default,
562+
AddStmtChoice asc);
559563
CFGBlock *VisitInitListExpr(InitListExpr *ILE, AddStmtChoice asc);
560564
CFGBlock *VisitAddrLabelExpr(AddrLabelExpr *A, AddStmtChoice asc);
561565
CFGBlock *VisitAttributedStmt(AttributedStmt *A, AddStmtChoice asc);
@@ -2254,16 +2258,10 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc,
22542258
asc, ExternallyDestructed);
22552259

22562260
case Stmt::CXXDefaultArgExprClass:
2261+
return VisitCXXDefaultArgExpr(cast<CXXDefaultArgExpr>(S), asc);
2262+
22572263
case Stmt::CXXDefaultInitExprClass:
2258-
// FIXME: The expression inside a CXXDefaultArgExpr is owned by the
2259-
// called function's declaration, not by the caller. If we simply add
2260-
// this expression to the CFG, we could end up with the same Expr
2261-
// appearing multiple times (PR13385).
2262-
//
2263-
// It's likewise possible for multiple CXXDefaultInitExprs for the same
2264-
// expression to be used in the same function (through aggregate
2265-
// initialization).
2266-
return VisitStmt(S, asc);
2264+
return VisitCXXDefaultInitExpr(cast<CXXDefaultInitExpr>(S), asc);
22672265

22682266
case Stmt::CXXBindTemporaryExprClass:
22692267
return VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), asc);
@@ -2433,6 +2431,40 @@ CFGBlock *CFGBuilder::VisitChildren(Stmt *S) {
24332431
return B;
24342432
}
24352433

2434+
CFGBlock *CFGBuilder::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *Arg,
2435+
AddStmtChoice asc) {
2436+
if (Arg->hasRewrittenInit()) {
2437+
if (asc.alwaysAdd(*this, Arg)) {
2438+
autoCreateBlock();
2439+
appendStmt(Block, Arg);
2440+
}
2441+
return VisitStmt(Arg->getExpr(), asc);
2442+
}
2443+
2444+
// We can't add the default argument if it's not rewritten because the
2445+
// expression inside a CXXDefaultArgExpr is owned by the called function's
2446+
// declaration, not by the caller, we could end up with the same expression
2447+
// appearing multiple times.
2448+
return VisitStmt(Arg, asc);
2449+
}
2450+
2451+
CFGBlock *CFGBuilder::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *Init,
2452+
AddStmtChoice asc) {
2453+
if (Init->hasRewrittenInit()) {
2454+
if (asc.alwaysAdd(*this, Init)) {
2455+
autoCreateBlock();
2456+
appendStmt(Block, Init);
2457+
}
2458+
return VisitStmt(Init->getExpr(), asc);
2459+
}
2460+
2461+
// We can't add the default initializer if it's not rewritten because multiple
2462+
// CXXDefaultInitExprs for the same sub-expression to be used in the same
2463+
// function (through aggregate initialization). we could end up with the same
2464+
// expression appearing multiple times.
2465+
return VisitStmt(Init, asc);
2466+
}
2467+
24362468
CFGBlock *CFGBuilder::VisitInitListExpr(InitListExpr *ILE, AddStmtChoice asc) {
24372469
if (asc.alwaysAdd(*this, ILE)) {
24382470
autoCreateBlock();

clang/lib/Sema/SemaExpr.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5599,6 +5599,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
55995599
InitializationContext.emplace(Loc, Field, CurContext);
56005600

56015601
Expr *Init = nullptr;
5602+
bool HasRewrittenInit = false;
56025603

56035604
bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
56045605
bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
@@ -5648,6 +5649,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
56485649
isa_and_present<ExprWithCleanups>(Field->getInClassInitializer());
56495650
if (V.HasImmediateCalls || InLifetimeExtendingContext ||
56505651
ContainsAnyTemporaries) {
5652+
HasRewrittenInit = true;
56515653
ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
56525654
CurContext};
56535655
ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
@@ -5691,7 +5693,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
56915693

56925694
return CXXDefaultInitExpr::Create(Context, InitializationContext->Loc,
56935695
Field, InitializationContext->Context,
5694-
Init);
5696+
HasRewrittenInit ? Init : nullptr);
56955697
}
56965698

56975699
// DR1351:

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1970,33 +1970,45 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
19701970
ExplodedNodeSet Tmp;
19711971
StmtNodeBuilder Bldr2(PreVisit, Tmp, *currBldrCtx);
19721972

1973-
const Expr *ArgE;
1974-
if (const auto *DefE = dyn_cast<CXXDefaultArgExpr>(S))
1973+
bool HasRewrittenInit = false;
1974+
const Expr *ArgE = nullptr;
1975+
if (const auto *DefE = dyn_cast<CXXDefaultArgExpr>(S)) {
19751976
ArgE = DefE->getExpr();
1976-
else if (const auto *DefE = dyn_cast<CXXDefaultInitExpr>(S))
1977+
HasRewrittenInit = DefE->hasRewrittenInit();
1978+
} else if (const auto *DefE = dyn_cast<CXXDefaultInitExpr>(S)) {
19771979
ArgE = DefE->getExpr();
1978-
else
1980+
HasRewrittenInit = DefE->hasRewrittenInit();
1981+
} else
19791982
llvm_unreachable("unknown constant wrapper kind");
19801983

1981-
bool IsTemporary = false;
1982-
if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(ArgE)) {
1983-
ArgE = MTE->getSubExpr();
1984-
IsTemporary = true;
1985-
}
1984+
if (HasRewrittenInit) {
1985+
for (auto *N : PreVisit) {
1986+
ProgramStateRef state = N->getState();
1987+
const LocationContext *LCtx = N->getLocationContext();
1988+
state = state->BindExpr(S, LCtx, state->getSVal(ArgE, LCtx));
1989+
Bldr2.generateNode(S, N, state);
1990+
}
1991+
} else {
1992+
// If it's not rewritten, the contents of these expressions are not
1993+
// actually part of the current function, so we fall back to constant
1994+
// evaluation.
1995+
bool IsTemporary = false;
1996+
if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(ArgE)) {
1997+
ArgE = MTE->getSubExpr();
1998+
IsTemporary = true;
1999+
}
2000+
2001+
std::optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE);
2002+
const LocationContext *LCtx = Pred->getLocationContext();
2003+
for (auto *I : PreVisit) {
2004+
ProgramStateRef State = I->getState();
2005+
State = State->BindExpr(S, LCtx, ConstantVal.value_or(UnknownVal()));
2006+
if (IsTemporary)
2007+
State = createTemporaryRegionIfNeeded(State, LCtx, cast<Expr>(S),
2008+
cast<Expr>(S));
19862009

1987-
std::optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE);
1988-
if (!ConstantVal)
1989-
ConstantVal = UnknownVal();
1990-
1991-
const LocationContext *LCtx = Pred->getLocationContext();
1992-
for (const auto I : PreVisit) {
1993-
ProgramStateRef State = I->getState();
1994-
State = State->BindExpr(S, LCtx, *ConstantVal);
1995-
if (IsTemporary)
1996-
State = createTemporaryRegionIfNeeded(State, LCtx,
1997-
cast<Expr>(S),
1998-
cast<Expr>(S));
1999-
Bldr2.generateNode(S, I, State);
2010+
Bldr2.generateNode(S, I, State);
2011+
}
20002012
}
20012013

20022014
getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);

clang/test/Analysis/cxx-uninitialized-object.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,27 +1114,27 @@ void fCXX11MemberInitTest1() {
11141114
CXX11MemberInitTest1();
11151115
}
11161116

1117+
#ifdef PEDANTIC
11171118
struct CXX11MemberInitTest2 {
11181119
struct RecordType {
1119-
// TODO: we'd expect the note: {{uninitialized field 'this->rec.a'}}
1120-
int a; // no-note
1121-
// TODO: we'd expect the note: {{uninitialized field 'this->rec.b'}}
1122-
int b; // no-note
1120+
int a; // expected-note {{uninitialized field 'this->a'}}
1121+
int b; // expected-note {{uninitialized field 'this->b'}}
11231122

11241123
RecordType(int) {}
11251124
};
11261125

1127-
RecordType rec = RecordType(int());
1126+
RecordType rec = RecordType(int()); // expected-warning {{2 uninitialized fields}}
11281127
int dontGetFilteredByNonPedanticMode = 0;
11291128

11301129
CXX11MemberInitTest2() {}
11311130
};
11321131

11331132
void fCXX11MemberInitTest2() {
1134-
// TODO: we'd expect the warning: {{2 uninitializeds field}}
11351133
CXX11MemberInitTest2(); // no-warning
11361134
}
11371135

1136+
#endif // PEDANTIC
1137+
11381138
//===----------------------------------------------------------------------===//
11391139
// "Esoteric" primitive type tests.
11401140
//===----------------------------------------------------------------------===//

clang/test/Analysis/lifetime-extended-regions.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,10 @@ void aggregateWithReferences() {
121121
clang_analyzer_dump(viaReference.rx); // expected-warning-re {{&lifetime_extended_object{int, viaReference, S{{[0-9]+}}} }}
122122
clang_analyzer_dump(viaReference.ry); // expected-warning-re {{&lifetime_extended_object{Composite, viaReference, S{{[0-9]+}}} }}
123123

124-
// FIXME: clang currently support extending lifetime of object bound to reference members of aggregates,
125-
// that are created from default member initializer. But CFG and ExprEngine need to be updated to address this change.
126-
// The following expect warning: {{&lifetime_extended_object{Composite, defaultInitExtended, S{{[0-9]+}}} }}
124+
// The lifetime lifetime of object bound to reference members of aggregates,
125+
// that are created from default member initializer was extended.
127126
RefAggregate defaultInitExtended{i};
128-
clang_analyzer_dump(defaultInitExtended.ry); // expected-warning {{Unknown }}
127+
clang_analyzer_dump(defaultInitExtended.ry); // expected-warning-re {{&lifetime_extended_object{Composite, defaultInitExtended, S{{[0-9]+}}} }}
129128
}
130129

131130
void lambda() {

0 commit comments

Comments
 (0)