ymandel updated this revision to Diff 419499.
ymandel marked 5 inline comments as done.
ymandel added a comment.
adjusted test
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D122273/new/
https://reviews.llvm.org/D122273
Files:
clang/include/clang/Analysis/FlowSensitive/StorageLocation.h
clang/include/clang/Analysis/FlowSensitive/Value.h
clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -1009,6 +1009,157 @@
});
}
+TEST_F(TransferTest, DerivedBaseMemberClass) {
+ std::string Code = R"(
+ class A {
+ int ADefault;
+ protected:
+ int AProtected;
+ private:
+ int APrivate;
+ public:
+ int APublic;
+ };
+
+ class B : public A {
+ int BDefault;
+ protected:
+ int BProtected;
+ private:
+ int BPrivate;
+ };
+
+ void target() {
+ B 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());
+ ASSERT_TRUE(FooDecl->getType()->isRecordType());
+
+ // Derived-class fields.
+ const FieldDecl *BDefaultDecl = nullptr;
+ const FieldDecl *BProtectedDecl = nullptr;
+ const FieldDecl *BPrivateDecl = nullptr;
+ for (const FieldDecl *Field :
+ FooDecl->getType()->getAsRecordDecl()->fields()) {
+ if (Field->getNameAsString() == "BDefault") {
+ BDefaultDecl = Field;
+ } else if (Field->getNameAsString() == "BProtected") {
+ BProtectedDecl = Field;
+ } else if (Field->getNameAsString() == "BPrivate") {
+ BPrivateDecl = Field;
+ } else {
+ FAIL() << "Unexpected field: " << Field->getNameAsString();
+ }
+ }
+ ASSERT_THAT(BDefaultDecl, NotNull());
+ ASSERT_THAT(BProtectedDecl, NotNull());
+ ASSERT_THAT(BPrivateDecl, NotNull());
+
+ // Base-class fields.
+ const FieldDecl *ADefaultDecl = nullptr;
+ const FieldDecl *APrivateDecl = nullptr;
+ const FieldDecl *AProtectedDecl = nullptr;
+ const FieldDecl *APublicDecl = nullptr;
+ for (const clang::CXXBaseSpecifier &Base :
+ FooDecl->getType()->getAsCXXRecordDecl()->bases()) {
+ QualType BaseType = Base.getType();
+ ASSERT_TRUE(BaseType->isRecordType());
+ for (const FieldDecl *Field : BaseType->getAsRecordDecl()->fields()) {
+ if (Field->getNameAsString() == "ADefault") {
+ ADefaultDecl = Field;
+ } else if (Field->getNameAsString() == "AProtected") {
+ AProtectedDecl = Field;
+ } else if (Field->getNameAsString() == "APrivate") {
+ APrivateDecl = Field;
+ } else if (Field->getNameAsString() == "APublic") {
+ APublicDecl = Field;
+ } else {
+ FAIL() << "Unexpected field: " << Field->getNameAsString();
+ }
+ }
+ }
+ ASSERT_THAT(ADefaultDecl, NotNull());
+ ASSERT_THAT(AProtectedDecl, NotNull());
+ ASSERT_THAT(APrivateDecl, NotNull());
+ ASSERT_THAT(APublicDecl, NotNull());
+
+ const auto *FooLoc = cast<AggregateStorageLocation>(
+ Env.getStorageLocation(*FooDecl, SkipPast::None));
+ const auto &FooVal = *cast<StructValue>(Env.getValue(*FooLoc));
+ EXPECT_THAT(FooVal.getChild(*APublicDecl), NotNull());
+
+ // Base-class fields.
+ EXPECT_THAT(FooVal.getChild(*ADefaultDecl), IsNull());
+ EXPECT_THAT(FooVal.getChild(*APrivateDecl), IsNull());
+ EXPECT_THAT(FooVal.getChild(*AProtectedDecl), NotNull());
+ EXPECT_THAT(FooVal.getChild(*APublicDecl), NotNull());
+
+ // Derived-class fields.
+ EXPECT_THAT(FooVal.getChild(*BDefaultDecl), NotNull());
+ EXPECT_THAT(FooVal.getChild(*BProtectedDecl), NotNull());
+ EXPECT_THAT(FooVal.getChild(*BPrivateDecl), NotNull());
+ });
+}
+
+TEST_F(TransferTest, DerivedBaseMemberStructDefault) {
+ std::string Code = R"(
+ struct A {
+ int Bar;
+ };
+ struct B : public A {
+ };
+
+ void target() {
+ B 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());
+
+ ASSERT_TRUE(FooDecl->getType()->isRecordType());
+ const FieldDecl *BarDecl = nullptr;
+ for (const clang::CXXBaseSpecifier &Base :
+ FooDecl->getType()->getAsCXXRecordDecl()->bases()) {
+ QualType BaseType = Base.getType();
+ ASSERT_TRUE(BaseType->isStructureType());
+
+ for (const FieldDecl *Field : BaseType->getAsRecordDecl()->fields()) {
+ if (Field->getNameAsString() == "Bar") {
+ BarDecl = Field;
+ } else {
+ FAIL() << "Unexpected field: " << Field->getNameAsString();
+ }
+ }
+ }
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const auto *FooLoc = cast<AggregateStorageLocation>(
+ Env.getStorageLocation(*FooDecl, SkipPast::None));
+ const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
+ EXPECT_THAT(FooVal->getChild(*BarDecl), NotNull());
+ });
+}
+
TEST_F(TransferTest, ClassMember) {
std::string Code = R"(
class A {
Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -164,6 +164,42 @@
return JoinedConstraints;
}
+static void
+getFieldsFromClassHierarchy(QualType Type, bool IgnorePrivateFields,
+ llvm::DenseSet<const FieldDecl *> &Fields) {
+ if (Type->isIncompleteType() || Type->isDependentType() ||
+ !Type->isRecordType())
+ return;
+
+ for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) {
+ if (IgnorePrivateFields &&
+ (Field->getAccess() == AS_private ||
+ (Field->getAccess() == AS_none && Type->getAsRecordDecl()->isClass())))
+ continue;
+ Fields.insert(Field);
+ }
+ if (auto *CXXRecord = Type->getAsCXXRecordDecl()) {
+ for (const CXXBaseSpecifier &Base : CXXRecord->bases()) {
+ // Ignore private fields (including default access in C++ classes) in
+ // base classes, because they are not visible in derived classes.
+ getFieldsFromClassHierarchy(Base.getType(), /*IgnorePrivateFields=*/true,
+ Fields);
+ }
+ }
+}
+
+/// Gets the set of all fields accesible from the type.
+///
+/// FIXME: Does not precisely handle non-virtual diamond inheritance. A single
+/// field decl will be modeled for all instances of the inherited field.
+static llvm::DenseSet<const FieldDecl *>
+getAccessibleObjectFields(QualType Type) {
+ llvm::DenseSet<const FieldDecl *> Fields;
+ // Don't ignore private fields for the class itself, only its super classes.
+ getFieldsFromClassHierarchy(Type, /*IgnorePrivateFields=*/false, Fields);
+ return Fields;
+}
+
Environment::Environment(DataflowAnalysisContext &DACtx,
const DeclContext &DeclCtx)
: Environment(DACtx) {
@@ -296,7 +332,7 @@
// FIXME: Explore options to avoid eager initialization of fields as some of
// them might not be needed for a particular analysis.
llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;
- for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) {
+ for (const FieldDecl *Field : getAccessibleObjectFields(Type)) {
FieldLocs.insert({Field, &createStorageLocation(Field->getType())});
}
return takeOwnership(
@@ -363,7 +399,7 @@
const QualType Type = AggregateLoc.getType();
assert(Type->isStructureOrClassType());
- for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) {
+ for (const FieldDecl *Field : getAccessibleObjectFields(Type)) {
assert(Field != nullptr);
StorageLocation &FieldLoc = AggregateLoc.getChild(*Field);
MemberLocToStruct[&FieldLoc] = std::make_pair(StructVal, Field);
@@ -479,7 +515,7 @@
// FIXME: Initialize only fields that are accessed in the context that is
// being analyzed.
llvm::DenseMap<const ValueDecl *, Value *> FieldValues;
- for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) {
+ for (const FieldDecl *Field : getAccessibleObjectFields(Type)) {
assert(Field != nullptr);
QualType FieldType = Field->getType();
Index: clang/include/clang/Analysis/FlowSensitive/Value.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/Value.h
+++ clang/include/clang/Analysis/FlowSensitive/Value.h
@@ -189,7 +189,9 @@
}
};
-/// Models a value of `struct` or `class` type.
+/// Models a value of `struct` or `class` type. Implements a flat struct layout,
+/// where the child values are directly reachable from the struct value (rather
+/// than indirectly, through `StorageLocation`s).
class StructValue final : public Value {
public:
StructValue() : StructValue(llvm::DenseMap<const ValueDecl *, Value *>()) {}
Index: clang/include/clang/Analysis/FlowSensitive/StorageLocation.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/StorageLocation.h
+++ clang/include/clang/Analysis/FlowSensitive/StorageLocation.h
@@ -56,7 +56,8 @@
/// A storage location which is subdivided into smaller storage locations that
/// can be traced independently by abstract interpretation. For example: a
-/// struct with public members.
+/// struct with public members. Note that the corresponding `StructValue` has a
+/// flat layout that does not contain the child locations stored here.
class AggregateStorageLocation final : public StorageLocation {
public:
explicit AggregateStorageLocation(QualType Type)
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits