Skip to content

Commit 469374e

Browse files
authored
[clang][dataflow] Disallow setting properties on RecordValues. (#76042)
Instead, synthetic fields should now be used for the same purpose. These have a number of advantages, as described in #73860, and longer-term, we want to eliminate `RecordValue` entirely. As `RecordValue`s cannot have properties any more, I have replaced the `OptionalIntAnalysis` with an equivalent analysis that tracks nullness of pointers (instead of whether an optional has a value). This serves the same purpose, namely to check whether the framework applies a custom `merge()` operation to widen properties.
1 parent da4bd5b commit 469374e

File tree

6 files changed

+88
-195
lines changed

6 files changed

+88
-195
lines changed

clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -727,20 +727,9 @@ RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
727727
std::vector<FieldDecl *> getFieldsForInitListExpr(const RecordDecl *RD);
728728

729729
/// Associates a new `RecordValue` with `Loc` and returns the new value.
730-
/// It is not defined whether the field values remain the same or not.
731-
///
732-
/// This function is primarily intended for use by checks that set custom
733-
/// properties on `RecordValue`s to model the state of these values. Such checks
734-
/// should avoid modifying the properties of an existing `RecordValue` because
735-
/// these changes would be visible to other `Environment`s that share the same
736-
/// `RecordValue`. Instead, call `refreshRecordValue()`, then set the properties
737-
/// on the new `RecordValue` that it returns. Typical usage:
738-
///
739-
/// refreshRecordValue(Loc, Env).setProperty("my_prop", MyPropValue);
740730
RecordValue &refreshRecordValue(RecordStorageLocation &Loc, Environment &Env);
741731

742732
/// Associates a new `RecordValue` with `Expr` and returns the new value.
743-
/// See also documentation for the overload above.
744733
RecordValue &refreshRecordValue(const Expr &Expr, Environment &Env);
745734

746735
} // namespace dataflow

clang/include/clang/Analysis/FlowSensitive/RecordOps.h

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,13 @@ namespace dataflow {
2222
/// Copies a record (struct, class, or union) from `Src` to `Dst`.
2323
///
2424
/// This performs a deep copy, i.e. it copies every field (including synthetic
25-
/// fields) and recurses on fields of record type. It also copies properties
26-
/// from the `RecordValue` associated with `Src` to the `RecordValue` associated
27-
/// with `Dst` (if these `RecordValue`s exist).
25+
/// fields) and recurses on fields of record type.
2826
///
2927
/// If there is a `RecordValue` associated with `Dst` in the environment, this
3028
/// function creates a new `RecordValue` and associates it with `Dst`; clients
3129
/// need to be aware of this and must not assume that the `RecordValue`
3230
/// associated with `Dst` remains the same after the call.
3331
///
34-
/// We create a new `RecordValue` rather than modifying properties on the old
35-
/// `RecordValue` because the old `RecordValue` may be shared with other
36-
/// `Environment`s, and we don't want changes to properties to be visible there.
37-
///
3832
/// Requirements:
3933
///
4034
/// `Src` and `Dst` must have the same canonical unqualified type.
@@ -49,9 +43,7 @@ void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst,
4943
///
5044
/// This performs a deep comparison, i.e. it compares every field (including
5145
/// synthetic fields) and recurses on fields of record type. Fields of reference
52-
/// type compare equal if they refer to the same storage location. If
53-
/// `RecordValue`s are associated with `Loc1` and Loc2`, it also compares the
54-
/// properties on those `RecordValue`s.
46+
/// type compare equal if they refer to the same storage location.
5547
///
5648
/// Note on how to interpret the result:
5749
/// - If this returns true, the records are guaranteed to be equal at runtime.

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

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ class Value {
6363

6464
/// Assigns `Val` as the value of the synthetic property with the given
6565
/// `Name`.
66+
///
67+
/// Properties may not be set on `RecordValue`s; use synthetic fields instead
68+
/// (for details, see documentation for `RecordStorageLocation`).
6669
void setProperty(llvm::StringRef Name, Value &Val) {
70+
assert(getKind() != Kind::Record);
6771
Properties.insert_or_assign(Name, &Val);
6872
}
6973

@@ -184,33 +188,23 @@ class PointerValue final : public Value {
184188
/// In C++, prvalues of class type serve only a limited purpose: They can only
185189
/// be used to initialize a result object. It is not possible to access member
186190
/// variables or call member functions on a prvalue of class type.
187-
/// Correspondingly, `RecordValue` also serves only two limited purposes:
188-
/// - It conveys a prvalue of class type from the place where the object is
189-
/// constructed to the result object that it initializes.
191+
/// Correspondingly, `RecordValue` also serves only a limited purpose: It
192+
/// conveys a prvalue of class type from the place where the object is
193+
/// constructed to the result object that it initializes.
190194
///
191-
/// When creating a prvalue of class type, we already need a storage location
192-
/// for `this`, even though prvalues are otherwise not associated with storage
193-
/// locations. `RecordValue` is therefore essentially a wrapper for a storage
194-
/// location, which is then used to set the storage location for the result
195-
/// object when we process the AST node for that result object.
195+
/// When creating a prvalue of class type, we already need a storage location
196+
/// for `this`, even though prvalues are otherwise not associated with storage
197+
/// locations. `RecordValue` is therefore essentially a wrapper for a storage
198+
/// location, which is then used to set the storage location for the result
199+
/// object when we process the AST node for that result object.
196200
///
197-
/// For example:
198-
/// MyStruct S = MyStruct(3);
201+
/// For example:
202+
/// MyStruct S = MyStruct(3);
199203
///
200-
/// In this example, `MyStruct(3) is a prvalue, which is modeled as a
201-
/// `RecordValue` that wraps a `RecordStorageLocation`. This
202-
// `RecordStorageLocation` is then used as the storage location for `S`.
204+
/// In this example, `MyStruct(3) is a prvalue, which is modeled as a
205+
/// `RecordValue` that wraps a `RecordStorageLocation`. This
206+
/// `RecordStorageLocation` is then used as the storage location for `S`.
203207
///
204-
/// - It allows properties to be associated with an object of class type.
205-
/// Note that when doing so, you should avoid mutating the properties of an
206-
/// existing `RecordValue` in place, as these changes would be visible to
207-
/// other `Environment`s that share the same `RecordValue`. Instead, associate
208-
/// a new `RecordValue` with the `RecordStorageLocation` and set the
209-
/// properties on this new `RecordValue`. (See also `refreshRecordValue()` in
210-
/// DataflowEnvironment.h, which makes this easy.)
211-
/// Note also that this implies that it is common for the same
212-
/// `RecordStorageLocation` to be associated with different `RecordValue`s
213-
/// in different environments.
214208
/// Over time, we may eliminate `RecordValue` entirely. See also the discussion
215209
/// here: https://reviews.llvm.org/D155204#inline-1503204
216210
class RecordValue final : public Value {

clang/lib/Analysis/FlowSensitive/RecordOps.cpp

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,8 @@ void clang::dataflow::copyRecord(RecordStorageLocation &Src,
6666
}
6767
}
6868

69-
RecordValue *SrcVal = Env.get<RecordValue>(Src);
70-
RecordValue *DstVal = Env.get<RecordValue>(Dst);
71-
72-
DstVal = &Env.create<RecordValue>(Dst);
69+
RecordValue *DstVal = &Env.create<RecordValue>(Dst);
7370
Env.setValue(Dst, *DstVal);
74-
75-
if (SrcVal == nullptr)
76-
return;
77-
78-
for (const auto &[Name, Value] : SrcVal->properties()) {
79-
if (Value != nullptr)
80-
DstVal->setProperty(Name, *Value);
81-
}
8271
}
8372

8473
bool clang::dataflow::recordsEqual(const RecordStorageLocation &Loc1,
@@ -125,25 +114,5 @@ bool clang::dataflow::recordsEqual(const RecordStorageLocation &Loc1,
125114
}
126115
}
127116

128-
llvm::StringMap<Value *> Props1, Props2;
129-
130-
if (RecordValue *Val1 = Env1.get<RecordValue>(Loc1))
131-
for (const auto &[Name, Value] : Val1->properties())
132-
Props1[Name] = Value;
133-
if (RecordValue *Val2 = Env2.get<RecordValue>(Loc2))
134-
for (const auto &[Name, Value] : Val2->properties())
135-
Props2[Name] = Value;
136-
137-
if (Props1.size() != Props2.size())
138-
return false;
139-
140-
for (const auto &[Name, Value] : Props1) {
141-
auto It = Props2.find(Name);
142-
if (It == Props2.end())
143-
return false;
144-
if (Value != It->second)
145-
return false;
146-
}
147-
148117
return true;
149118
}

clang/unittests/Analysis/FlowSensitive/RecordOpsTest.cpp

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,6 @@ TEST(RecordOpsTest, CopyRecord) {
8989
auto *S2Val = cast<RecordValue>(Env.getValue(S2));
9090
EXPECT_NE(S1Val, S2Val);
9191

92-
S1Val->setProperty("prop", Env.getBoolLiteralValue(true));
93-
9492
copyRecord(S1, S2, Env);
9593

9694
EXPECT_EQ(getFieldValue(&S1, *OuterIntDecl, Env),
@@ -104,8 +102,6 @@ TEST(RecordOpsTest, CopyRecord) {
104102
S1Val = cast<RecordValue>(Env.getValue(S1));
105103
S2Val = cast<RecordValue>(Env.getValue(S2));
106104
EXPECT_NE(S1Val, S2Val);
107-
108-
EXPECT_EQ(S2Val->getProperty("prop"), &Env.getBoolLiteralValue(true));
109105
});
110106
}
111107

@@ -150,9 +146,6 @@ TEST(RecordOpsTest, RecordsEqual) {
150146
Env.setValue(S1.getSyntheticField("synth_int"),
151147
Env.create<IntegerValue>());
152148

153-
cast<RecordValue>(Env.getValue(S1))
154-
->setProperty("prop", Env.getBoolLiteralValue(true));
155-
156149
// Strategy: Create two equal records, then verify each of the various
157150
// ways in which records can differ causes recordsEqual to return false.
158151
// changes we can make to the record.
@@ -202,36 +195,6 @@ TEST(RecordOpsTest, RecordsEqual) {
202195
EXPECT_FALSE(recordsEqual(S1, S2, Env));
203196
copyRecord(S1, S2, Env);
204197
EXPECT_TRUE(recordsEqual(S1, S2, Env));
205-
206-
// S1 and S2 have the same property with different values.
207-
cast<RecordValue>(Env.getValue(S2))
208-
->setProperty("prop", Env.getBoolLiteralValue(false));
209-
EXPECT_FALSE(recordsEqual(S1, S2, Env));
210-
copyRecord(S1, S2, Env);
211-
EXPECT_TRUE(recordsEqual(S1, S2, Env));
212-
213-
// S1 has a property that S2 doesn't have.
214-
cast<RecordValue>(Env.getValue(S1))
215-
->setProperty("other_prop", Env.getBoolLiteralValue(false));
216-
EXPECT_FALSE(recordsEqual(S1, S2, Env));
217-
// We modified S1 this time, so need to copy back the other way.
218-
copyRecord(S2, S1, Env);
219-
EXPECT_TRUE(recordsEqual(S1, S2, Env));
220-
221-
// S2 has a property that S1 doesn't have.
222-
cast<RecordValue>(Env.getValue(S2))
223-
->setProperty("other_prop", Env.getBoolLiteralValue(false));
224-
EXPECT_FALSE(recordsEqual(S1, S2, Env));
225-
copyRecord(S1, S2, Env);
226-
EXPECT_TRUE(recordsEqual(S1, S2, Env));
227-
228-
// S1 and S2 have the same number of properties, but with different
229-
// names.
230-
cast<RecordValue>(Env.getValue(S1))
231-
->setProperty("prop1", Env.getBoolLiteralValue(false));
232-
cast<RecordValue>(Env.getValue(S2))
233-
->setProperty("prop2", Env.getBoolLiteralValue(false));
234-
EXPECT_FALSE(recordsEqual(S1, S2, Env));
235198
});
236199
}
237200

0 commit comments

Comments
 (0)