Skip to content

Commit 7f66cc7

Browse files
authored
[clang][dataflow] Merge RecordValues with different locations correctly. (#65319)
Now that prvalue expressions map directly to values (see https://reviews.llvm.org/D158977), it's no longer guaranteed that `RecordValue`s associated with the same expression will always have the same storage location. In other words, D158977 invalidated the assertion in `mergeDistinctValues()`. The newly added test causes this assertion to fail without the other changes in the patch. This patch fixes the issue. However, the real fix will be to eliminate the `StorageLocation` from `RecordValue` entirely.
1 parent cedeb31 commit 7f66cc7

File tree

2 files changed

+82
-12
lines changed

2 files changed

+82
-12
lines changed

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -125,18 +125,19 @@ static Value *mergeDistinctValues(QualType Type, Value &Val1,
125125

126126
Value *MergedVal = nullptr;
127127
if (auto *RecordVal1 = dyn_cast<RecordValue>(&Val1)) {
128-
[[maybe_unused]] auto *RecordVal2 = cast<RecordValue>(&Val2);
129-
130-
// Values to be merged are always associated with the same location in
131-
// `LocToVal`. The location stored in `RecordVal` should therefore also
132-
// be the same.
133-
assert(&RecordVal1->getLoc() == &RecordVal2->getLoc());
134-
135-
// `RecordVal1` and `RecordVal2` may have different properties associated
136-
// with them. Create a new `RecordValue` without any properties so that we
137-
// soundly approximate both values. If a particular analysis needs to merge
138-
// properties, it should do so in `DataflowAnalysis::merge()`.
139-
MergedVal = &MergedEnv.create<RecordValue>(RecordVal1->getLoc());
128+
auto *RecordVal2 = cast<RecordValue>(&Val2);
129+
130+
if (&RecordVal1->getLoc() == &RecordVal2->getLoc())
131+
// `RecordVal1` and `RecordVal2` may have different properties associated
132+
// with them. Create a new `RecordValue` with the same location but
133+
// without any properties so that we soundly approximate both values. If a
134+
// particular analysis needs to merge properties, it should do so in
135+
// `DataflowAnalysis::merge()`.
136+
MergedVal = &MergedEnv.create<RecordValue>(RecordVal1->getLoc());
137+
else
138+
// If the locations for the two records are different, need to create a
139+
// completely new value.
140+
MergedVal = MergedEnv.createValue(Type);
140141
} else {
141142
MergedVal = MergedEnv.createValue(Type);
142143
}

clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,75 @@ TEST_F(EnvironmentTest, CreateValueRecursiveType) {
9696
EXPECT_THAT(PV, NotNull());
9797
}
9898

99+
TEST_F(EnvironmentTest, JoinRecords) {
100+
using namespace ast_matchers;
101+
102+
std::string Code = R"cc(
103+
struct S {};
104+
// Need to use the type somewhere so that the `QualType` gets created;
105+
S s;
106+
)cc";
107+
108+
auto Unit =
109+
tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
110+
auto &Context = Unit->getASTContext();
111+
112+
ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
113+
114+
auto Results =
115+
match(qualType(hasDeclaration(recordDecl(hasName("S")))).bind("SType"),
116+
Context);
117+
const QualType *TyPtr = selectFirst<QualType>("SType", Results);
118+
ASSERT_THAT(TyPtr, NotNull());
119+
QualType Ty = *TyPtr;
120+
ASSERT_FALSE(Ty.isNull());
121+
122+
auto *ConstructExpr = CXXConstructExpr::CreateEmpty(Context, 0);
123+
ConstructExpr->setType(Ty);
124+
ConstructExpr->setValueKind(VK_PRValue);
125+
126+
// Two different `RecordValue`s with the same location are joined into a
127+
// third `RecordValue` with that same location.
128+
{
129+
Environment Env1(DAContext);
130+
auto &Val1 = *cast<RecordValue>(Env1.createValue(Ty));
131+
RecordStorageLocation &Loc = Val1.getLoc();
132+
Env1.setValue(*ConstructExpr, Val1);
133+
134+
Environment Env2(DAContext);
135+
auto &Val2 = Env2.create<RecordValue>(Loc);
136+
Env2.setValue(Loc, Val2);
137+
Env2.setValue(*ConstructExpr, Val2);
138+
139+
Environment::ValueModel Model;
140+
Environment EnvJoined = Environment::join(Env1, Env2, Model);
141+
auto *JoinedVal = cast<RecordValue>(EnvJoined.getValue(*ConstructExpr));
142+
EXPECT_NE(JoinedVal, &Val1);
143+
EXPECT_NE(JoinedVal, &Val2);
144+
EXPECT_EQ(&JoinedVal->getLoc(), &Loc);
145+
}
146+
147+
// Two different `RecordValue`s with different locations are joined into a
148+
// third `RecordValue` with a location different from the other two.
149+
{
150+
Environment Env1(DAContext);
151+
auto &Val1 = *cast<RecordValue>(Env1.createValue(Ty));
152+
Env1.setValue(*ConstructExpr, Val1);
153+
154+
Environment Env2(DAContext);
155+
auto &Val2 = *cast<RecordValue>(Env2.createValue(Ty));
156+
Env2.setValue(*ConstructExpr, Val2);
157+
158+
Environment::ValueModel Model;
159+
Environment EnvJoined = Environment::join(Env1, Env2, Model);
160+
auto *JoinedVal = cast<RecordValue>(EnvJoined.getValue(*ConstructExpr));
161+
EXPECT_NE(JoinedVal, &Val1);
162+
EXPECT_NE(JoinedVal, &Val2);
163+
EXPECT_NE(&JoinedVal->getLoc(), &Val1.getLoc());
164+
EXPECT_NE(&JoinedVal->getLoc(), &Val2.getLoc());
165+
}
166+
}
167+
99168
TEST_F(EnvironmentTest, InitGlobalVarsFun) {
100169
using namespace ast_matchers;
101170

0 commit comments

Comments
 (0)