[clang][dataflow] Update StructValue child when assigning a value
When assigning a value to a storage location of a struct member we need to also update the value in the corresponding `StructValue`. This is part of the implementation of the dataflow analysis framework. See "[RFC] A dataflow analysis framework for Clang AST" on cfe-dev. Reviewed-by: ymandel, xazax.hun Differential Revision: https://reviews.llvm.org/D120414
This commit is contained in:
parent
ba18c360b2
commit
baa0f221d6
|
@ -265,6 +265,12 @@ private:
|
|||
|
||||
llvm::DenseMap<const StorageLocation *, Value *> LocToVal;
|
||||
|
||||
// Maps locations of struct members to symbolic values of the structs that own
|
||||
// them and the decls of the struct members.
|
||||
llvm::DenseMap<const StorageLocation *,
|
||||
std::pair<StructValue *, const ValueDecl *>>
|
||||
MemberLocToStruct;
|
||||
|
||||
// FIXME: Add flow condition constraints.
|
||||
};
|
||||
|
||||
|
|
|
@ -138,6 +138,9 @@ bool Environment::equivalentTo(const Environment &Other,
|
|||
if (ExprToLoc != Other.ExprToLoc)
|
||||
return false;
|
||||
|
||||
if (MemberLocToStruct != Other.MemberLocToStruct)
|
||||
return false;
|
||||
|
||||
if (LocToVal.size() != Other.LocToVal.size())
|
||||
return false;
|
||||
|
||||
|
@ -176,6 +179,12 @@ LatticeJoinEffect Environment::join(const Environment &Other,
|
|||
if (ExprToLocSizeBefore != ExprToLoc.size())
|
||||
Effect = LatticeJoinEffect::Changed;
|
||||
|
||||
const unsigned MemberLocToStructSizeBefore = MemberLocToStruct.size();
|
||||
MemberLocToStruct =
|
||||
intersectDenseMaps(MemberLocToStruct, Other.MemberLocToStruct);
|
||||
if (MemberLocToStructSizeBefore != MemberLocToStruct.size())
|
||||
Effect = LatticeJoinEffect::Changed;
|
||||
|
||||
// Move `LocToVal` so that `Environment::ValueModel::merge` can safely assign
|
||||
// values to storage locations while this code iterates over the current
|
||||
// assignments.
|
||||
|
@ -285,9 +294,25 @@ void Environment::setValue(const StorageLocation &Loc, Value &Val) {
|
|||
|
||||
for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) {
|
||||
assert(Field != nullptr);
|
||||
setValue(AggregateLoc.getChild(*Field), StructVal->getChild(*Field));
|
||||
StorageLocation &FieldLoc = AggregateLoc.getChild(*Field);
|
||||
MemberLocToStruct[&FieldLoc] = std::make_pair(StructVal, Field);
|
||||
setValue(FieldLoc, StructVal->getChild(*Field));
|
||||
}
|
||||
}
|
||||
|
||||
auto IT = MemberLocToStruct.find(&Loc);
|
||||
if (IT != MemberLocToStruct.end()) {
|
||||
// `Loc` is the location of a struct member so we need to also update the
|
||||
// value of the member in the corresponding `StructValue`.
|
||||
|
||||
assert(IT->second.first != nullptr);
|
||||
StructValue &StructVal = *IT->second.first;
|
||||
|
||||
assert(IT->second.second != nullptr);
|
||||
const ValueDecl &Member = *IT->second.second;
|
||||
|
||||
StructVal.setChild(Member, Val);
|
||||
}
|
||||
}
|
||||
|
||||
Value *Environment::getValue(const StorageLocation &Loc) const {
|
||||
|
|
|
@ -2320,4 +2320,48 @@ TEST_F(TransferTest, StaticMemberRefVarDecl) {
|
|||
});
|
||||
}
|
||||
|
||||
TEST_F(TransferTest, AssignMemberBeforeCopy) {
|
||||
std::string Code = R"(
|
||||
struct A {
|
||||
int Foo;
|
||||
};
|
||||
|
||||
void target() {
|
||||
A A1;
|
||||
A A2;
|
||||
int Bar;
|
||||
A1.Foo = Bar;
|
||||
A2 = A1;
|
||||
// [[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 *A1Decl = findValueDecl(ASTCtx, "A1");
|
||||
ASSERT_THAT(A1Decl, NotNull());
|
||||
|
||||
const ValueDecl *A2Decl = findValueDecl(ASTCtx, "A2");
|
||||
ASSERT_THAT(A2Decl, NotNull());
|
||||
|
||||
const auto *BarVal =
|
||||
cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
|
||||
|
||||
const auto *A2Val =
|
||||
cast<StructValue>(Env.getValue(*A2Decl, SkipPast::None));
|
||||
EXPECT_EQ(&A2Val->getChild(*FooDecl), BarVal);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
Loading…
Reference in a new issue