Skip to content

Commit a6a6066

Browse files
authored
[clang][dataflow] Fix crash when analyzing a coroutine (#85957)
A coroutine function body (`CoroutineBodyStmt`) may have null children, which causes `isa` to segfault.
1 parent 1b5b4ee commit a6a6066

File tree

2 files changed

+53
-3
lines changed

2 files changed

+53
-3
lines changed

clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ buildContainsExprConsumedInDifferentBlock(
103103
auto CheckChildExprs = [&Result, &StmtToBlock](const Stmt *S,
104104
const CFGBlock *Block) {
105105
for (const Stmt *Child : S->children()) {
106-
if (!isa<Expr>(Child))
106+
if (!isa_and_nonnull<Expr>(Child))
107107
continue;
108108
const CFGBlock *ChildBlock = StmtToBlock.lookup(Child);
109109
if (ChildBlock != Block)

clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,40 @@ using ::testing::Ne;
3737
using ::testing::NotNull;
3838
using ::testing::UnorderedElementsAre;
3939

40+
// Declares a minimal coroutine library.
41+
constexpr llvm::StringRef CoroutineLibrary = R"cc(
42+
struct promise;
43+
struct task;
44+
45+
namespace std {
46+
template <class, class...>
47+
struct coroutine_traits {};
48+
template <>
49+
struct coroutine_traits<task> {
50+
using promise_type = promise;
51+
};
52+
53+
template <class Promise = void>
54+
struct coroutine_handle {
55+
static constexpr coroutine_handle from_address(void *addr) { return {}; }
56+
};
57+
} // namespace std
58+
59+
struct awaitable {
60+
bool await_ready() const noexcept;
61+
void await_suspend(std::coroutine_handle<promise>) const noexcept;
62+
void await_resume() const noexcept;
63+
};
64+
struct task {};
65+
struct promise {
66+
task get_return_object();
67+
awaitable initial_suspend();
68+
awaitable final_suspend() noexcept;
69+
void unhandled_exception();
70+
void return_void();
71+
};
72+
)cc";
73+
4074
void runDataflow(
4175
llvm::StringRef Code,
4276
std::function<
@@ -4607,7 +4641,7 @@ TEST(TransferTest, LoopCanProveInvariantForBoolean) {
46074641
}
46084642

46094643
TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
4610-
std::string Code = R"(
4644+
std::string Code = R"cc(
46114645
union Union {
46124646
int A;
46134647
float B;
@@ -4618,7 +4652,7 @@ TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
46184652
Union B;
46194653
A = B;
46204654
}
4621-
)";
4655+
)cc";
46224656
// This is a crash regression test when calling the transfer function on a
46234657
// `CXXThisExpr` that refers to a union.
46244658
runDataflow(
@@ -4628,6 +4662,22 @@ TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
46284662
LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
46294663
}
46304664

4665+
TEST(TransferTest, DoesNotCrashOnNullChildren) {
4666+
std::string Code = (CoroutineLibrary + R"cc(
4667+
task target() noexcept {
4668+
co_return;
4669+
}
4670+
)cc")
4671+
.str();
4672+
// This is a crash regression test when calling `AdornedCFG::build` on a
4673+
// statement (in this case, the `CoroutineBodyStmt`) with null children.
4674+
runDataflow(
4675+
Code,
4676+
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
4677+
ASTContext &) {},
4678+
LangStandard::lang_cxx20, /*ApplyBuiltinTransfer=*/true);
4679+
}
4680+
46314681
TEST(TransferTest, StructuredBindingAssignFromStructIntMembersToRefs) {
46324682
std::string Code = R"(
46334683
struct A {

0 commit comments

Comments
 (0)