Skip to content

Commit 90e0dd1

Browse files
committed
Revert "[Analyzer][CFG] Correctly handle rebuilt default arg and default init expression (#117437)"
This caused assertion failures: clang/lib/Analysis/CFG.cpp:822: void (anonymous namespace)::CFGBuilder::appendStmt(CFGBlock *, const Stmt *): Assertion `!isa<Expr>(S) || cast<Expr>(S)->IgnoreParens() == S' failed. See comment on the PR. This reverts commit 44aa618.
1 parent d906da5 commit 90e0dd1

File tree

10 files changed

+61
-168
lines changed

10 files changed

+61
-168
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,6 @@ Code Completion
219219
Static Analyzer
220220
---------------
221221

222-
- Clang currently support extending lifetime of object bound to
223-
reference members of aggregates in CFG and ExprEngine, that are
224-
created from default member initializer.
225-
226222
New features
227223
^^^^^^^^^^^^
228224

clang/lib/AST/ParentMap.cpp

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
#include "clang/AST/ParentMap.h"
1414
#include "clang/AST/Decl.h"
1515
#include "clang/AST/Expr.h"
16-
#include "clang/AST/ExprCXX.h"
1716
#include "clang/AST/StmtObjC.h"
1817
#include "llvm/ADT/DenseMap.h"
1918

@@ -104,22 +103,6 @@ static void BuildParentMap(MapTy& M, Stmt* S,
104103
BuildParentMap(M, SubStmt, OVMode);
105104
}
106105
break;
107-
case Stmt::CXXDefaultArgExprClass:
108-
if (auto *Arg = dyn_cast<CXXDefaultArgExpr>(S)) {
109-
if (Arg->hasRewrittenInit()) {
110-
M[Arg->getExpr()] = S;
111-
BuildParentMap(M, Arg->getExpr(), OVMode);
112-
}
113-
}
114-
break;
115-
case Stmt::CXXDefaultInitExprClass:
116-
if (auto *Init = dyn_cast<CXXDefaultInitExpr>(S)) {
117-
if (Init->hasRewrittenInit()) {
118-
M[Init->getExpr()] = S;
119-
BuildParentMap(M, Init->getExpr(), OVMode);
120-
}
121-
}
122-
break;
123106
default:
124107
for (Stmt *SubStmt : S->children()) {
125108
if (SubStmt) {

clang/lib/Analysis/CFG.cpp

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -556,10 +556,6 @@ 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);
563559
CFGBlock *VisitInitListExpr(InitListExpr *ILE, AddStmtChoice asc);
564560
CFGBlock *VisitAddrLabelExpr(AddrLabelExpr *A, AddStmtChoice asc);
565561
CFGBlock *VisitAttributedStmt(AttributedStmt *A, AddStmtChoice asc);
@@ -2265,10 +2261,16 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc,
22652261
asc, ExternallyDestructed);
22662262

22672263
case Stmt::CXXDefaultArgExprClass:
2268-
return VisitCXXDefaultArgExpr(cast<CXXDefaultArgExpr>(S), asc);
2269-
22702264
case Stmt::CXXDefaultInitExprClass:
2271-
return VisitCXXDefaultInitExpr(cast<CXXDefaultInitExpr>(S), asc);
2265+
// FIXME: The expression inside a CXXDefaultArgExpr is owned by the
2266+
// called function's declaration, not by the caller. If we simply add
2267+
// this expression to the CFG, we could end up with the same Expr
2268+
// appearing multiple times (PR13385).
2269+
//
2270+
// It's likewise possible for multiple CXXDefaultInitExprs for the same
2271+
// expression to be used in the same function (through aggregate
2272+
// initialization).
2273+
return VisitStmt(S, asc);
22722274

22732275
case Stmt::CXXBindTemporaryExprClass:
22742276
return VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), asc);
@@ -2438,40 +2440,6 @@ CFGBlock *CFGBuilder::VisitChildren(Stmt *S) {
24382440
return B;
24392441
}
24402442

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

clang/lib/Analysis/ReachableCode.cpp

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -454,12 +454,11 @@ bool DeadCodeScan::isDeadCodeRoot(const clang::CFGBlock *Block) {
454454
return isDeadRoot;
455455
}
456456

457-
// Check if the given `DeadStmt` is one of target statements or is a sub-stmt of
458-
// them. `Block` is the CFGBlock containing the `DeadStmt`.
459-
template <class... Ts>
460-
static bool isDeadStmtInOneOf(const Stmt *DeadStmt, const CFGBlock *Block) {
457+
// Check if the given `DeadStmt` is a coroutine statement and is a substmt of
458+
// the coroutine statement. `Block` is the CFGBlock containing the `DeadStmt`.
459+
static bool isInCoroutineStmt(const Stmt *DeadStmt, const CFGBlock *Block) {
461460
// The coroutine statement, co_return, co_await, or co_yield.
462-
const Stmt *TargetStmt = nullptr;
461+
const Stmt *CoroStmt = nullptr;
463462
// Find the first coroutine statement after the DeadStmt in the block.
464463
bool AfterDeadStmt = false;
465464
for (CFGBlock::const_iterator I = Block->begin(), E = Block->end(); I != E;
@@ -468,27 +467,32 @@ static bool isDeadStmtInOneOf(const Stmt *DeadStmt, const CFGBlock *Block) {
468467
const Stmt *S = CS->getStmt();
469468
if (S == DeadStmt)
470469
AfterDeadStmt = true;
471-
if (AfterDeadStmt && llvm::isa<Ts...>(S)) {
472-
TargetStmt = S;
470+
if (AfterDeadStmt &&
471+
// For simplicity, we only check simple coroutine statements.
472+
(llvm::isa<CoreturnStmt>(S) || llvm::isa<CoroutineSuspendExpr>(S))) {
473+
CoroStmt = S;
473474
break;
474475
}
475476
}
476-
if (!TargetStmt)
477+
if (!CoroStmt)
477478
return false;
478479
struct Checker : DynamicRecursiveASTVisitor {
479480
const Stmt *DeadStmt;
480-
bool IsSubStmtOfTargetStmt = false;
481-
Checker(const Stmt *S) : DeadStmt(S) { ShouldVisitImplicitCode = true; }
481+
bool CoroutineSubStmt = false;
482+
Checker(const Stmt *S) : DeadStmt(S) {
483+
// Statements captured in the CFG can be implicit.
484+
ShouldVisitImplicitCode = true;
485+
}
482486

483487
bool VisitStmt(Stmt *S) override {
484488
if (S == DeadStmt)
485-
IsSubStmtOfTargetStmt = true;
489+
CoroutineSubStmt = true;
486490
return true;
487491
}
488492
};
489493
Checker checker(DeadStmt);
490-
checker.TraverseStmt(const_cast<Stmt *>(TargetStmt));
491-
return checker.IsSubStmtOfTargetStmt;
494+
checker.TraverseStmt(const_cast<Stmt *>(CoroStmt));
495+
return checker.CoroutineSubStmt;
492496
}
493497

494498
static bool isValidDeadStmt(const Stmt *S, const clang::CFGBlock *Block) {
@@ -499,12 +503,7 @@ static bool isValidDeadStmt(const Stmt *S, const clang::CFGBlock *Block) {
499503
// Coroutine statements are never considered dead statements, because removing
500504
// them may change the function semantic if it is the only coroutine statement
501505
// of the coroutine.
502-
//
503-
// If the dead stmt is a sub-stmt of CXXDefaultInitExpr and CXXDefaultArgExpr,
504-
// we would rather expect to find CXXDefaultInitExpr and CXXDefaultArgExpr as
505-
// a valid dead stmt.
506-
return !isDeadStmtInOneOf<CoreturnStmt, CoroutineSuspendExpr,
507-
CXXDefaultArgExpr, CXXDefaultInitExpr>(S, Block);
506+
return !isInCoroutineStmt(S, Block);
508507
}
509508

510509
const Stmt *DeadCodeScan::findDeadCode(const clang::CFGBlock *Block) {

clang/lib/Sema/SemaExpr.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5570,10 +5570,8 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
55705570
/*SkipImmediateInvocations=*/NestedDefaultChecking))
55715571
return ExprError();
55725572

5573-
Expr *RewrittenExpr = (Init == Param->getDefaultArg() ? nullptr : Init);
55745573
return CXXDefaultArgExpr::Create(Context, InitializationContext->Loc, Param,
5575-
RewrittenExpr,
5576-
InitializationContext->Context);
5574+
Init, InitializationContext->Context);
55775575
}
55785576

55795577
static FieldDecl *FindFieldDeclInstantiationPattern(const ASTContext &Ctx,
@@ -5691,11 +5689,10 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
56915689
return ExprError();
56925690
}
56935691
Init = Res.get();
5694-
Expr *RewrittenInit =
5695-
(Init == Field->getInClassInitializer() ? nullptr : Init);
5692+
56965693
return CXXDefaultInitExpr::Create(Context, InitializationContext->Loc,
56975694
Field, InitializationContext->Context,
5698-
RewrittenInit);
5695+
Init);
56995696
}
57005697

57015698
// DR1351:

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp

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

1990-
bool HasRebuiltInit = false;
1991-
const Expr *ArgE = nullptr;
1992-
if (const auto *DefE = dyn_cast<CXXDefaultArgExpr>(S)) {
1990+
const Expr *ArgE;
1991+
if (const auto *DefE = dyn_cast<CXXDefaultArgExpr>(S))
19931992
ArgE = DefE->getExpr();
1994-
HasRebuiltInit = DefE->hasRewrittenInit();
1995-
} else if (const auto *DefE = dyn_cast<CXXDefaultInitExpr>(S)) {
1993+
else if (const auto *DefE = dyn_cast<CXXDefaultInitExpr>(S))
19961994
ArgE = DefE->getExpr();
1997-
HasRebuiltInit = DefE->hasRewrittenInit();
1998-
} else
1995+
else
19991996
llvm_unreachable("unknown constant wrapper kind");
20001997

2001-
if (HasRebuiltInit) {
2002-
for (auto *N : PreVisit) {
2003-
ProgramStateRef state = N->getState();
2004-
const LocationContext *LCtx = N->getLocationContext();
2005-
state = state->BindExpr(S, LCtx, state->getSVal(ArgE, LCtx));
2006-
Bldr2.generateNode(S, N, state);
2007-
}
2008-
} else {
2009-
// If it's not rewritten, the contents of these expressions are not
2010-
// actually part of the current function, so we fall back to constant
2011-
// evaluation.
2012-
bool IsTemporary = false;
2013-
if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(ArgE)) {
2014-
ArgE = MTE->getSubExpr();
2015-
IsTemporary = true;
2016-
}
2017-
2018-
std::optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE);
2019-
const LocationContext *LCtx = Pred->getLocationContext();
2020-
for (auto *I : PreVisit) {
2021-
ProgramStateRef State = I->getState();
2022-
State = State->BindExpr(S, LCtx, ConstantVal.value_or(UnknownVal()));
2023-
if (IsTemporary)
2024-
State = createTemporaryRegionIfNeeded(State, LCtx, cast<Expr>(S),
2025-
cast<Expr>(S));
1998+
bool IsTemporary = false;
1999+
if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(ArgE)) {
2000+
ArgE = MTE->getSubExpr();
2001+
IsTemporary = true;
2002+
}
20262003

2027-
Bldr2.generateNode(S, I, State);
2028-
}
2004+
std::optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE);
2005+
if (!ConstantVal)
2006+
ConstantVal = UnknownVal();
2007+
2008+
const LocationContext *LCtx = Pred->getLocationContext();
2009+
for (const auto I : PreVisit) {
2010+
ProgramStateRef State = I->getState();
2011+
State = State->BindExpr(S, LCtx, *ConstantVal);
2012+
if (IsTemporary)
2013+
State = createTemporaryRegionIfNeeded(State, LCtx,
2014+
cast<Expr>(S),
2015+
cast<Expr>(S));
2016+
Bldr2.generateNode(S, I, State);
20292017
}
20302018

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

clang/test/AST/ast-dump-recovery.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ union U {
507507
// CHECK-NEXT: `-DeclStmt {{.*}}
508508
// CHECK-NEXT: `-VarDecl {{.*}} g 'U':'GH112560::U' listinit
509509
// CHECK-NEXT: `-InitListExpr {{.*}} 'U':'GH112560::U' contains-errors field Field {{.*}} 'f' 'int'
510-
// CHECK-NEXT: `-CXXDefaultInitExpr {{.*}} 'int' contains-errors
510+
// CHECK-NEXT: `-CXXDefaultInitExpr {{.*}} 'int' contains-errors has rewritten init
511511
// CHECK-NEXT: `-RecoveryExpr {{.*}} 'int' contains-errors
512512
// DISABLED-NOT: -RecoveryExpr {{.*}} contains-errors
513513
void foo() {

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,11 @@ 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-
// The lifetime of object bound to reference members of aggregates,
125-
// that are created from default member initializer was extended.
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]+}}} }}
126127
RefAggregate defaultInitExtended{i};
127-
clang_analyzer_dump(defaultInitExtended.ry); // expected-warning-re {{&lifetime_extended_object{Composite, defaultInitExtended, S{{[0-9]+}}} }}
128+
clang_analyzer_dump(defaultInitExtended.ry); // expected-warning {{Unknown }}
128129
}
129130

130131
void lambda() {

clang/test/SemaCXX/cxx2c-placeholder-vars.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,16 +274,16 @@ void f() {
274274
// CHECK: ClassTemplateSpecializationDecl {{.*}} struct A definition
275275
// CHECK: CXXConstructorDecl {{.*}} implicit used constexpr A 'void () noexcept'
276276
// CHECK-NEXT: CXXCtorInitializer Field {{.*}} '_' 'int'
277-
// CHECK-NEXT: CXXDefaultInitExpr {{.*}} 'int'
277+
// CHECK-NEXT: CXXDefaultInitExpr {{.*}} 'int' has rewritten init
278278
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1
279279
// CHECK-NEXT: CXXCtorInitializer Field {{.*}} '_' 'int'
280-
// CHECK-NEXT: CXXDefaultInitExpr {{.*}} 'int'
280+
// CHECK-NEXT: CXXDefaultInitExpr {{.*}} 'int' has rewritten init
281281
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 2
282282
// CHECK-NEXT: CXXCtorInitializer Field {{.*}} 'a' 'int'
283-
// CHECK-NEXT: CXXDefaultInitExpr {{.*}} 'int'
283+
// CHECK-NEXT: CXXDefaultInitExpr {{.*}} 'int' has rewritten init
284284
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3
285285
// CHECK-NEXT: CXXCtorInitializer Field {{.*}} '_' 'int'
286-
// CHECK-NEXT: CXXDefaultInitExpr {{.*}} 'int'
286+
// CHECK-NEXT: CXXDefaultInitExpr {{.*}} 'int' has rewritten init
287287
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 4
288288
// CHECK-NEXT: CompoundStmt {{.*}}
289289

clang/test/SemaCXX/warn-unreachable.cpp

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -414,42 +414,3 @@ void tautological_compare(bool x, int y) {
414414
calledFun();
415415

416416
}
417-
418-
namespace test_rebuilt_default_arg {
419-
struct A {
420-
explicit A(int = __builtin_LINE());
421-
};
422-
423-
int h(int a) {
424-
return 3;
425-
A(); // expected-warning {{will never be executed}}
426-
}
427-
428-
struct Temp {
429-
Temp();
430-
~Temp();
431-
};
432-
433-
struct B {
434-
explicit B(const Temp &t = Temp());
435-
};
436-
int f(int a) {
437-
return 3;
438-
B(); // expected-warning {{will never be executed}}
439-
}
440-
} // namespace test_rebuilt_default_arg
441-
namespace test_rebuilt_default_init {
442-
443-
struct A {
444-
A();
445-
~A();
446-
};
447-
448-
struct B {
449-
const A &t = A();
450-
};
451-
int f(int a) {
452-
return 3;
453-
A{}; // expected-warning {{will never be executed}}
454-
}
455-
} // namespace test_rebuilt_default_init

0 commit comments

Comments
 (0)