Skip to content

Commit 4d839d8

Browse files
authored
[clang][dataflow] Don't propagate result objects in unevaluated contexts (reland #90438) (#91172)
This relands #90348 with a fix for a [buildbot failure](https://lab.llvm.org/buildbot/#/builders/216/builds/38446) caused by the test being run with `-fno-rtti`.
1 parent 5f73d29 commit 4d839d8

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,17 @@ class ResultObjectVisitor : public RecursiveASTVisitor<ResultObjectVisitor> {
350350
return RecursiveASTVisitor<ResultObjectVisitor>::TraverseDecl(D);
351351
}
352352

353+
// Don't traverse expressions in unevaluated contexts, as we don't model
354+
// fields that are only used in these.
355+
// Note: The operand of the `noexcept` operator is an unevaluated operand, but
356+
// nevertheless it appears in the Clang CFG, so we don't exclude it here.
357+
bool TraverseDecltypeTypeLoc(DecltypeTypeLoc) { return true; }
358+
bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc) { return true; }
359+
bool TraverseCXXTypeidExpr(CXXTypeidExpr *) { return true; }
360+
bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *) {
361+
return true;
362+
}
363+
353364
bool TraverseBindingDecl(BindingDecl *BD) {
354365
// `RecursiveASTVisitor` doesn't traverse holding variables for
355366
// `BindingDecl`s by itself, so we need to tell it to.

clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3400,6 +3400,60 @@ TEST(TransferTest, ResultObjectLocationDontVisitNestedRecordDecl) {
34003400
ASTContext &ASTCtx) {});
34013401
}
34023402

3403+
TEST(TransferTest, ResultObjectLocationDontVisitUnevaluatedContexts) {
3404+
// This is a crash repro.
3405+
// We used to crash because when propagating result objects, we would visit
3406+
// unevaluated contexts, but we don't model fields used only in these.
3407+
3408+
auto testFunction = [](llvm::StringRef Code, llvm::StringRef TargetFun) {
3409+
runDataflow(
3410+
Code,
3411+
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3412+
ASTContext &ASTCtx) {},
3413+
LangStandard::lang_gnucxx17,
3414+
/* ApplyBuiltinTransfer= */ true, TargetFun);
3415+
};
3416+
3417+
std::string Code = R"cc(
3418+
// Definitions needed for `typeid`.
3419+
namespace std {
3420+
class type_info {};
3421+
class bad_typeid {};
3422+
} // namespace std
3423+
3424+
struct S1 {};
3425+
struct S2 { S1 s1; };
3426+
3427+
// We test each type of unevaluated context from a different target
3428+
// function. Some types of unevaluated contexts may actually cause the
3429+
// field `s1` to be modeled, and we don't want this to "pollute" the tests
3430+
// for the other unevaluated contexts.
3431+
void decltypeTarget() {
3432+
decltype(S2{}) Dummy;
3433+
}
3434+
void typeofTarget() {
3435+
typeof(S2{}) Dummy;
3436+
}
3437+
void typeidTarget() {
3438+
#if __has_feature(cxx_rtti)
3439+
typeid(S2{});
3440+
#endif
3441+
}
3442+
void sizeofTarget() {
3443+
sizeof(S2{});
3444+
}
3445+
void noexceptTarget() {
3446+
noexcept(S2{});
3447+
}
3448+
)cc";
3449+
3450+
testFunction(Code, "decltypeTarget");
3451+
testFunction(Code, "typeofTarget");
3452+
testFunction(Code, "typeidTarget");
3453+
testFunction(Code, "sizeofTarget");
3454+
testFunction(Code, "noexceptTarget");
3455+
}
3456+
34033457
TEST(TransferTest, StaticCast) {
34043458
std::string Code = R"(
34053459
void target(int Foo) {

0 commit comments

Comments
 (0)