Skip to content

[clang][dataflow] Don't propagate result objects in unevaluated contexts #90438

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,17 @@ class ResultObjectVisitor : public RecursiveASTVisitor<ResultObjectVisitor> {
return RecursiveASTVisitor<ResultObjectVisitor>::TraverseDecl(D);
}

// Don't traverse expressions in unevaluated contexts, as we don't model
// fields that are only used in these.
// Note: The operand of the `noexcept` operator is an unevaluated operand, but
// nevertheless it appears in the Clang CFG, so we don't exclude it here.
bool TraverseDecltypeTypeLoc(DecltypeTypeLoc) { return true; }
bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc) { return true; }
bool TraverseCXXTypeidExpr(CXXTypeidExpr *) { return true; }
bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *) {
return true;
}

bool TraverseBindingDecl(BindingDecl *BD) {
// `RecursiveASTVisitor` doesn't traverse holding variables for
// `BindingDecl`s by itself, so we need to tell it to.
Expand Down
52 changes: 52 additions & 0 deletions clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3331,6 +3331,58 @@ TEST(TransferTest, ResultObjectLocationDontVisitNestedRecordDecl) {
ASTContext &ASTCtx) {});
}

TEST(TransferTest, ResultObjectLocationDontVisitUnevaluatedContexts) {
// This is a crash repro.
// We used to crash because when propagating result objects, we would visit
// unevaluated contexts, but we don't model fields used only in these.

auto testFunction = [](llvm::StringRef Code, llvm::StringRef TargetFun) {
runDataflow(
Code,
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
ASTContext &ASTCtx) {},
LangStandard::lang_gnucxx17,
/* ApplyBuiltinTransfer= */ true, TargetFun);
};

std::string Code = R"cc(
// Definitions needed for `typeid`.
namespace std {
class type_info {};
class bad_typeid {};
} // namespace std

struct S1 {};
struct S2 { S1 s1; };

// We test each type of unevaluated context from a different target
// function. Some types of unevaluated contexts may actually cause the
// field `s1` to be modeled, and we don't want this to "pollute" the tests
// for the other unevaluated contexts.
void decltypeTarget() {
decltype(S2{}) Dummy;
}
void typeofTarget() {
typeof(S2{}) Dummy;
}
void typeidTarget() {
typeid(S2{});
}
void sizeofTarget() {
sizeof(S2{});
}
void noexceptTarget() {
noexcept(S2{});
}
)cc";

testFunction(Code, "decltypeTarget");
testFunction(Code, "typeofTarget");
testFunction(Code, "typeidTarget");
testFunction(Code, "sizeofTarget");
testFunction(Code, "noexceptTarget");
}

TEST(TransferTest, StaticCast) {
std::string Code = R"(
void target(int Foo) {
Expand Down