Skip to content

Commit 36d4e84

Browse files
committed
[clang][dataflow] Fix handling of base-class fields.
Currently, the framework does not track derived class access to base fields. This patch adds that support and a corresponding test. Differential Revision: https://reviews.llvm.org/D122273
1 parent 884d7c6 commit 36d4e84

File tree

4 files changed

+213
-5
lines changed

4 files changed

+213
-5
lines changed

clang/include/clang/Analysis/FlowSensitive/StorageLocation.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ class ScalarStorageLocation final : public StorageLocation {
5656

5757
/// A storage location which is subdivided into smaller storage locations that
5858
/// can be traced independently by abstract interpretation. For example: a
59-
/// struct with public members.
59+
/// struct with public members. The child map is flat, so when used for a struct
60+
/// or class type, all accessible members of base struct and class types are
61+
/// directly accesible as children of this location.
6062
class AggregateStorageLocation final : public StorageLocation {
6163
public:
6264
explicit AggregateStorageLocation(QualType Type)

clang/include/clang/Analysis/FlowSensitive/Value.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,9 @@ class PointerValue final : public IndirectionValue {
189189
}
190190
};
191191

192-
/// Models a value of `struct` or `class` type.
192+
/// Models a value of `struct` or `class` type, with a flat map of fields to
193+
/// child storage locations, containing all accessible members of base struct
194+
/// and class types.
193195
class StructValue final : public Value {
194196
public:
195197
StructValue() : StructValue(llvm::DenseMap<const ValueDecl *, Value *>()) {}

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,42 @@ joinConstraints(DataflowAnalysisContext *Context,
164164
return JoinedConstraints;
165165
}
166166

167+
static void
168+
getFieldsFromClassHierarchy(QualType Type, bool IgnorePrivateFields,
169+
llvm::DenseSet<const FieldDecl *> &Fields) {
170+
if (Type->isIncompleteType() || Type->isDependentType() ||
171+
!Type->isRecordType())
172+
return;
173+
174+
for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) {
175+
if (IgnorePrivateFields &&
176+
(Field->getAccess() == AS_private ||
177+
(Field->getAccess() == AS_none && Type->getAsRecordDecl()->isClass())))
178+
continue;
179+
Fields.insert(Field);
180+
}
181+
if (auto *CXXRecord = Type->getAsCXXRecordDecl()) {
182+
for (const CXXBaseSpecifier &Base : CXXRecord->bases()) {
183+
// Ignore private fields (including default access in C++ classes) in
184+
// base classes, because they are not visible in derived classes.
185+
getFieldsFromClassHierarchy(Base.getType(), /*IgnorePrivateFields=*/true,
186+
Fields);
187+
}
188+
}
189+
}
190+
191+
/// Gets the set of all fields accesible from the type.
192+
///
193+
/// FIXME: Does not precisely handle non-virtual diamond inheritance. A single
194+
/// field decl will be modeled for all instances of the inherited field.
195+
static llvm::DenseSet<const FieldDecl *>
196+
getAccessibleObjectFields(QualType Type) {
197+
llvm::DenseSet<const FieldDecl *> Fields;
198+
// Don't ignore private fields for the class itself, only its super classes.
199+
getFieldsFromClassHierarchy(Type, /*IgnorePrivateFields=*/false, Fields);
200+
return Fields;
201+
}
202+
167203
Environment::Environment(DataflowAnalysisContext &DACtx,
168204
const DeclContext &DeclCtx)
169205
: Environment(DACtx) {
@@ -296,7 +332,7 @@ StorageLocation &Environment::createStorageLocation(QualType Type) {
296332
// FIXME: Explore options to avoid eager initialization of fields as some of
297333
// them might not be needed for a particular analysis.
298334
llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;
299-
for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) {
335+
for (const FieldDecl *Field : getAccessibleObjectFields(Type)) {
300336
FieldLocs.insert({Field, &createStorageLocation(Field->getType())});
301337
}
302338
return takeOwnership(
@@ -363,7 +399,7 @@ void Environment::setValue(const StorageLocation &Loc, Value &Val) {
363399
const QualType Type = AggregateLoc.getType();
364400
assert(Type->isStructureOrClassType());
365401

366-
for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) {
402+
for (const FieldDecl *Field : getAccessibleObjectFields(Type)) {
367403
assert(Field != nullptr);
368404
StorageLocation &FieldLoc = AggregateLoc.getChild(*Field);
369405
MemberLocToStruct[&FieldLoc] = std::make_pair(StructVal, Field);
@@ -479,7 +515,7 @@ Value *Environment::createValueUnlessSelfReferential(
479515
// FIXME: Initialize only fields that are accessed in the context that is
480516
// being analyzed.
481517
llvm::DenseMap<const ValueDecl *, Value *> FieldValues;
482-
for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) {
518+
for (const FieldDecl *Field : getAccessibleObjectFields(Type)) {
483519
assert(Field != nullptr);
484520

485521
QualType FieldType = Field->getType();

clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,174 @@ TEST_F(TransferTest, StructMember) {
10091009
});
10101010
}
10111011

1012+
TEST_F(TransferTest, DerivedBaseMemberClass) {
1013+
std::string Code = R"(
1014+
class A {
1015+
int ADefault;
1016+
protected:
1017+
int AProtected;
1018+
private:
1019+
int APrivate;
1020+
public:
1021+
int APublic;
1022+
};
1023+
1024+
class B : public A {
1025+
int BDefault;
1026+
protected:
1027+
int BProtected;
1028+
private:
1029+
int BPrivate;
1030+
};
1031+
1032+
void target() {
1033+
B Foo;
1034+
// [[p]]
1035+
}
1036+
)";
1037+
runDataflow(
1038+
Code, [](llvm::ArrayRef<
1039+
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
1040+
Results,
1041+
ASTContext &ASTCtx) {
1042+
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
1043+
const Environment &Env = Results[0].second.Env;
1044+
1045+
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
1046+
ASSERT_THAT(FooDecl, NotNull());
1047+
ASSERT_TRUE(FooDecl->getType()->isRecordType());
1048+
1049+
// Derived-class fields.
1050+
const FieldDecl *BDefaultDecl = nullptr;
1051+
const FieldDecl *BProtectedDecl = nullptr;
1052+
const FieldDecl *BPrivateDecl = nullptr;
1053+
for (const FieldDecl *Field :
1054+
FooDecl->getType()->getAsRecordDecl()->fields()) {
1055+
if (Field->getNameAsString() == "BDefault") {
1056+
BDefaultDecl = Field;
1057+
} else if (Field->getNameAsString() == "BProtected") {
1058+
BProtectedDecl = Field;
1059+
} else if (Field->getNameAsString() == "BPrivate") {
1060+
BPrivateDecl = Field;
1061+
} else {
1062+
FAIL() << "Unexpected field: " << Field->getNameAsString();
1063+
}
1064+
}
1065+
ASSERT_THAT(BDefaultDecl, NotNull());
1066+
ASSERT_THAT(BProtectedDecl, NotNull());
1067+
ASSERT_THAT(BPrivateDecl, NotNull());
1068+
1069+
// Base-class fields.
1070+
const FieldDecl *ADefaultDecl = nullptr;
1071+
const FieldDecl *APrivateDecl = nullptr;
1072+
const FieldDecl *AProtectedDecl = nullptr;
1073+
const FieldDecl *APublicDecl = nullptr;
1074+
for (const clang::CXXBaseSpecifier &Base :
1075+
FooDecl->getType()->getAsCXXRecordDecl()->bases()) {
1076+
QualType BaseType = Base.getType();
1077+
ASSERT_TRUE(BaseType->isRecordType());
1078+
for (const FieldDecl *Field : BaseType->getAsRecordDecl()->fields()) {
1079+
if (Field->getNameAsString() == "ADefault") {
1080+
ADefaultDecl = Field;
1081+
} else if (Field->getNameAsString() == "AProtected") {
1082+
AProtectedDecl = Field;
1083+
} else if (Field->getNameAsString() == "APrivate") {
1084+
APrivateDecl = Field;
1085+
} else if (Field->getNameAsString() == "APublic") {
1086+
APublicDecl = Field;
1087+
} else {
1088+
FAIL() << "Unexpected field: " << Field->getNameAsString();
1089+
}
1090+
}
1091+
}
1092+
ASSERT_THAT(ADefaultDecl, NotNull());
1093+
ASSERT_THAT(AProtectedDecl, NotNull());
1094+
ASSERT_THAT(APrivateDecl, NotNull());
1095+
ASSERT_THAT(APublicDecl, NotNull());
1096+
1097+
const auto &FooLoc = *cast<AggregateStorageLocation>(
1098+
Env.getStorageLocation(*FooDecl, SkipPast::None));
1099+
const auto &FooVal = *cast<StructValue>(Env.getValue(FooLoc));
1100+
1101+
// Note: we can't test presence of children in `FooLoc`, because
1102+
// `getChild` requires its argument be present (or fails an assert). So,
1103+
// we limit to testing presence in `FooVal` and coherence between the
1104+
// two.
1105+
1106+
// Base-class fields.
1107+
EXPECT_THAT(FooVal.getChild(*ADefaultDecl), IsNull());
1108+
EXPECT_THAT(FooVal.getChild(*APrivateDecl), IsNull());
1109+
1110+
EXPECT_THAT(FooVal.getChild(*AProtectedDecl), NotNull());
1111+
EXPECT_EQ(Env.getValue(FooLoc.getChild(*APublicDecl)),
1112+
FooVal.getChild(*APublicDecl));
1113+
EXPECT_THAT(FooVal.getChild(*APublicDecl), NotNull());
1114+
EXPECT_EQ(Env.getValue(FooLoc.getChild(*AProtectedDecl)),
1115+
FooVal.getChild(*AProtectedDecl));
1116+
1117+
// Derived-class fields.
1118+
EXPECT_THAT(FooVal.getChild(*BDefaultDecl), NotNull());
1119+
EXPECT_EQ(Env.getValue(FooLoc.getChild(*BDefaultDecl)),
1120+
FooVal.getChild(*BDefaultDecl));
1121+
EXPECT_THAT(FooVal.getChild(*BProtectedDecl), NotNull());
1122+
EXPECT_EQ(Env.getValue(FooLoc.getChild(*BProtectedDecl)),
1123+
FooVal.getChild(*BProtectedDecl));
1124+
EXPECT_THAT(FooVal.getChild(*BPrivateDecl), NotNull());
1125+
EXPECT_EQ(Env.getValue(FooLoc.getChild(*BPrivateDecl)),
1126+
FooVal.getChild(*BPrivateDecl));
1127+
});
1128+
}
1129+
1130+
TEST_F(TransferTest, DerivedBaseMemberStructDefault) {
1131+
std::string Code = R"(
1132+
struct A {
1133+
int Bar;
1134+
};
1135+
struct B : public A {
1136+
};
1137+
1138+
void target() {
1139+
B Foo;
1140+
// [[p]]
1141+
}
1142+
)";
1143+
runDataflow(
1144+
Code, [](llvm::ArrayRef<
1145+
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
1146+
Results,
1147+
ASTContext &ASTCtx) {
1148+
ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
1149+
const Environment &Env = Results[0].second.Env;
1150+
1151+
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
1152+
ASSERT_THAT(FooDecl, NotNull());
1153+
1154+
ASSERT_TRUE(FooDecl->getType()->isRecordType());
1155+
const FieldDecl *BarDecl = nullptr;
1156+
for (const clang::CXXBaseSpecifier &Base :
1157+
FooDecl->getType()->getAsCXXRecordDecl()->bases()) {
1158+
QualType BaseType = Base.getType();
1159+
ASSERT_TRUE(BaseType->isStructureType());
1160+
1161+
for (const FieldDecl *Field : BaseType->getAsRecordDecl()->fields()) {
1162+
if (Field->getNameAsString() == "Bar") {
1163+
BarDecl = Field;
1164+
} else {
1165+
FAIL() << "Unexpected field: " << Field->getNameAsString();
1166+
}
1167+
}
1168+
}
1169+
ASSERT_THAT(BarDecl, NotNull());
1170+
1171+
const auto &FooLoc = *cast<AggregateStorageLocation>(
1172+
Env.getStorageLocation(*FooDecl, SkipPast::None));
1173+
const auto &FooVal = *cast<StructValue>(Env.getValue(FooLoc));
1174+
EXPECT_THAT(FooVal.getChild(*BarDecl), NotNull());
1175+
EXPECT_EQ(Env.getValue(FooLoc.getChild(*BarDecl)),
1176+
FooVal.getChild(*BarDecl));
1177+
});
1178+
}
1179+
10121180
TEST_F(TransferTest, ClassMember) {
10131181
std::string Code = R"(
10141182
class A {

0 commit comments

Comments
 (0)