Skip to content

Commit c70f058

Browse files
authored
[clang][dataflow] Fix crash when ConstantExpr is used in conditional operator. (#90112)
`ConstantExpr` does not appear as a `CFGStmt` in the CFG, so `StmtToEnvMap::getEnvironment()` was not finding an entry for it in the map, causing a crash when we tried to access the iterator resulting from the map lookup. The fix is to make `ignoreCFGOmittedNodes()` ignore `ConstantExpr`, but in addition, I'm hardening `StmtToEnvMap::getEnvironment()` to make sure release builds don't crash in similar situations in the future.
1 parent 8979644 commit c70f058

File tree

3 files changed

+49
-5
lines changed

3 files changed

+49
-5
lines changed

clang/lib/Analysis/FlowSensitive/ASTOps.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,20 @@ namespace clang::dataflow {
3333

3434
const Expr &ignoreCFGOmittedNodes(const Expr &E) {
3535
const Expr *Current = &E;
36-
if (auto *EWC = dyn_cast<ExprWithCleanups>(Current)) {
37-
Current = EWC->getSubExpr();
36+
const Expr *Last = nullptr;
37+
while (Current != Last) {
38+
Last = Current;
39+
if (auto *EWC = dyn_cast<ExprWithCleanups>(Current)) {
40+
Current = EWC->getSubExpr();
41+
assert(Current != nullptr);
42+
}
43+
if (auto *CE = dyn_cast<ConstantExpr>(Current)) {
44+
Current = CE->getSubExpr();
45+
assert(Current != nullptr);
46+
}
47+
Current = Current->IgnoreParens();
3848
assert(Current != nullptr);
3949
}
40-
Current = Current->IgnoreParens();
41-
assert(Current != nullptr);
4250
return *Current;
4351
}
4452

clang/lib/Analysis/FlowSensitive/Transfer.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ namespace dataflow {
4141

4242
const Environment *StmtToEnvMap::getEnvironment(const Stmt &S) const {
4343
auto BlockIt = ACFG.getStmtToBlock().find(&ignoreCFGOmittedNodes(S));
44-
assert(BlockIt != ACFG.getStmtToBlock().end());
44+
if (BlockIt == ACFG.getStmtToBlock().end()) {
45+
assert(false);
46+
// Return null to avoid dereferencing the end iterator in non-assert builds.
47+
return nullptr;
48+
}
4549
if (!ACFG.isBlockReachable(*BlockIt->getSecond()))
4650
return nullptr;
4751
if (BlockIt->getSecond()->getBlockID() == CurBlockID)

clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5357,6 +5357,38 @@ TEST(TransferTest, ConditionalOperatorLocation) {
53575357
});
53585358
}
53595359

5360+
TEST(TransferTest, ConditionalOperatorOnConstantExpr) {
5361+
// This is a regression test: We used to crash when a `ConstantExpr` was used
5362+
// in the branches of a conditional operator.
5363+
std::string Code = R"cc(
5364+
consteval bool identity(bool B) { return B; }
5365+
void target(bool Cond) {
5366+
bool JoinTrueTrue = Cond ? identity(true) : identity(true);
5367+
bool JoinTrueFalse = Cond ? identity(true) : identity(false);
5368+
// [[p]]
5369+
}
5370+
)cc";
5371+
runDataflow(
5372+
Code,
5373+
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5374+
ASTContext &ASTCtx) {
5375+
Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();
5376+
5377+
auto &JoinTrueTrue =
5378+
getValueForDecl<BoolValue>(ASTCtx, Env, "JoinTrueTrue");
5379+
// FIXME: This test documents the current behavior, namely that we
5380+
// don't actually use the constant result of the `ConstantExpr` and
5381+
// instead treat it like a normal function call.
5382+
EXPECT_EQ(JoinTrueTrue.formula().kind(), Formula::Kind::AtomRef);
5383+
// EXPECT_TRUE(JoinTrueTrue.formula().literal());
5384+
5385+
auto &JoinTrueFalse =
5386+
getValueForDecl<BoolValue>(ASTCtx, Env, "JoinTrueFalse");
5387+
EXPECT_EQ(JoinTrueFalse.formula().kind(), Formula::Kind::AtomRef);
5388+
},
5389+
LangStandard::lang_cxx20);
5390+
}
5391+
53605392
TEST(TransferTest, IfStmtBranchExtendsFlowCondition) {
53615393
std::string Code = R"(
53625394
void target(bool Foo) {

0 commit comments

Comments
 (0)