Skip to content

Commit

Permalink
[clang][dataflow] Add transfer functions for bind temporary and stati…
Browse files Browse the repository at this point in the history
…c cast

This is part of the implementation of the dataflow analysis framework.
See "[RFC] A dataflow analysis framework for Clang AST" on cfe-dev.

Differential Revision: https://reviews.llvm.org/D117339
  • Loading branch information
sgatev committed Jan 16, 2022
1 parent 79be1fe commit 37e6496
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 47 deletions.
110 changes: 63 additions & 47 deletions clang/lib/Analysis/FlowSensitive/Transfer.cpp
Expand Up @@ -87,11 +87,50 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
}

void VisitDeclStmt(const DeclStmt *S) {
// FIXME: Add support for group decls, e.g: `int a, b;`
if (S->isSingleDecl()) {
if (const auto *D = dyn_cast<VarDecl>(S->getSingleDecl())) {
visitVarDecl(*D);
// Group decls are converted into single decls in the CFG so the cast below
// is safe.
const auto &D = *cast<VarDecl>(S->getSingleDecl());
auto &Loc = Env.createStorageLocation(D);
Env.setStorageLocation(D, Loc);

const Expr *InitExpr = D.getInit();
if (InitExpr == nullptr) {
// No initializer expression - associate `Loc` with a new value.
Env.initValueInStorageLocation(Loc, D.getType());
return;
}

// The CFG does not contain `ParenExpr` as top-level statements in basic
// blocks, however sub-expressions can still be of that type.
InitExpr = skipExprWithCleanups(D.getInit()->IgnoreParens());
assert(InitExpr != nullptr);

if (D.getType()->isReferenceType()) {
// Initializing a reference variable - do not create a reference to
// reference.
if (auto *InitExprLoc =
Env.getStorageLocation(*InitExpr, SkipPast::Reference)) {
auto &Val =
Env.takeOwnership(std::make_unique<ReferenceValue>(*InitExprLoc));
Env.setValue(Loc, Val);
} else {
// FIXME: The initializer expression must always be assigned a value.
// Replace this with an assert when we have sufficient coverage of
// language features.
Env.initValueInStorageLocation(Loc, D.getType());
}
return;
}

if (auto *InitExprVal = Env.getValue(*InitExpr, SkipPast::None)) {
Env.setValue(Loc, *InitExprVal);
} else if (!D.getType()->isStructureOrClassType()) {
// FIXME: The initializer expression must always be assigned a value.
// Replace this with an assert when we have sufficient coverage of
// language features.
Env.initValueInStorageLocation(Loc, D.getType());
} else {
llvm_unreachable("structs and classes must always be assigned values");
}
}

Expand Down Expand Up @@ -309,57 +348,34 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
Env.setStorageLocation(*S, *SubExprLoc);
}

// FIXME: Add support for:
// - CXXBindTemporaryExpr
// - CXXBoolLiteralExpr
// - CXXStaticCastExpr

private:
void visitVarDecl(const VarDecl &D) {
auto &Loc = Env.createStorageLocation(D);
Env.setStorageLocation(D, Loc);
void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *S) {
const Expr *SubExpr = S->getSubExpr();
assert(SubExpr != nullptr);

const Expr *InitExpr = D.getInit();
if (InitExpr == nullptr) {
// No initializer expression - associate `Loc` with a new value.
Env.initValueInStorageLocation(Loc, D.getType());
auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None);
if (SubExprLoc == nullptr)
return;
}

// The CFG does not contain `ParenExpr` as top-level statements in basic
// blocks, however sub-expressions can still be of that type.
InitExpr = skipExprWithCleanups(D.getInit()->IgnoreParens());
assert(InitExpr != nullptr);
Env.setStorageLocation(*S, *SubExprLoc);
}

if (D.getType()->isReferenceType()) {
// Initializing a reference variable - do not create a reference to
// reference.
if (auto *InitExprLoc =
Env.getStorageLocation(*InitExpr, SkipPast::Reference)) {
auto &Val =
Env.takeOwnership(std::make_unique<ReferenceValue>(*InitExprLoc));
Env.setValue(Loc, Val);
} else {
// FIXME: The initializer expression must always be assigned a value.
// Replace this with an assert when we have sufficient coverage of
// language features.
Env.initValueInStorageLocation(Loc, D.getType());
}
return;
}
void VisitCXXStaticCastExpr(const CXXStaticCastExpr *S) {
if (S->getCastKind() == CK_NoOp) {
const Expr *SubExpr = S->getSubExpr();
assert(SubExpr != nullptr);

if (auto *InitExprVal = Env.getValue(*InitExpr, SkipPast::None)) {
Env.setValue(Loc, *InitExprVal);
} else if (!D.getType()->isStructureOrClassType()) {
// FIXME: The initializer expression must always be assigned a value.
// Replace this with an assert when we have sufficient coverage of
// language features.
Env.initValueInStorageLocation(Loc, D.getType());
} else {
llvm_unreachable("structs and classes must always be assigned values");
auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None);
if (SubExprLoc == nullptr)
return;

Env.setStorageLocation(*S, *SubExprLoc);
}
}

// FIXME: Add support for:
// - CXXBoolLiteralExpr

private:
Environment &Env;
};

Expand Down
105 changes: 105 additions & 0 deletions clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Expand Up @@ -489,6 +489,44 @@ TEST_F(TransferTest, SelfReferentialPointerVarDecl) {
});
}

TEST_F(TransferTest, MultipleVarsDecl) {
std::string Code = R"(
void target() {
int Foo, Bar;
(void)0;
// [[p]]
}
)";
runDataflow(Code,
[](llvm::ArrayRef<
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
Results,
ASTContext &ASTCtx) {
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
const Environment &Env = Results[0].second.Env;

const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());

const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
ASSERT_THAT(BarDecl, NotNull());

const StorageLocation *FooLoc =
Env.getStorageLocation(*FooDecl, SkipPast::None);
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));

const StorageLocation *BarLoc =
Env.getStorageLocation(*BarDecl, SkipPast::None);
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));

const Value *FooVal = Env.getValue(*FooLoc);
EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));

const Value *BarVal = Env.getValue(*BarLoc);
EXPECT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
});
}

TEST_F(TransferTest, JoinVarDecl) {
std::string Code = R"(
void target(bool B) {
Expand Down Expand Up @@ -1589,4 +1627,71 @@ TEST_F(TransferTest, MoveConstructor) {
});
}

TEST_F(TransferTest, BindTemporary) {
std::string Code = R"(
struct A {
virtual ~A() = default;
int Baz;
};
void target(A Foo) {
int Bar = A(Foo).Baz;
// [[p]]
}
)";
runDataflow(Code,
[](llvm::ArrayRef<
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
Results,
ASTContext &ASTCtx) {
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
const Environment &Env = Results[0].second.Env;

const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());

const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
ASSERT_THAT(BarDecl, NotNull());

const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
ASSERT_THAT(BazDecl, NotNull());

const auto &FooVal =
*cast<StructValue>(Env.getValue(*FooDecl, SkipPast::None));
const auto *BarVal =
cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
EXPECT_EQ(BarVal, &FooVal.getChild(*BazDecl));
});
}

TEST_F(TransferTest, StaticCast) {
std::string Code = R"(
void target(int Foo) {
int Bar = static_cast<int>(Foo);
// [[p]]
}
)";
runDataflow(Code,
[](llvm::ArrayRef<
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
Results,
ASTContext &ASTCtx) {
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
const Environment &Env = Results[0].second.Env;

const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
ASSERT_THAT(FooDecl, NotNull());

const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
ASSERT_THAT(BarDecl, NotNull());

const auto *FooVal =
cast<IntegerValue>(Env.getValue(*FooDecl, SkipPast::None));
const auto *BarVal =
cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
EXPECT_EQ(FooVal, BarVal);
});
}

} // namespace

0 comments on commit 37e6496

Please sign in to comment.