Skip to content

Commit 2ee396b

Browse files
authored
[clang][dataflow] Add Environment::get<>(). (llvm#76027)
This template function casts the result of `getValue()` or `getStorageLocation()` to a given subclass of `Value` or `StorageLocation` (using `cast_or_null`). It's a common pattern to do something like this: ```cxx auto *Val = cast_or_null<PointerValue>(Env.getValue(E)); ``` This can now be expressed more concisely like this: ```cxx auto *Val = Env.get<PointerValue>(E); ``` Instead of adding a new method `get()`, I had originally considered simply adding a template parameter to `getValue()` and `getStorageLocation()` (with a default argument of `Value` or `StorageLocation`), but this results in an undesirable repetition at the callsite, e.g. `getStorageLocation<RecordStorageLocation>(...)`. The `Value` and `StorageLocation` in the method name adds nothing of value when the template argument already contains this information, so it seemed best to shorten the method name to simply `get()`.
1 parent 591fc4f commit 2ee396b

File tree

7 files changed

+65
-41
lines changed

7 files changed

+65
-41
lines changed

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,22 @@ class Environment {
289289
/// `E` must be a glvalue or a `BuiltinType::BuiltinFn`
290290
StorageLocation *getStorageLocation(const Expr &E) const;
291291

292+
/// Returns the result of casting `getStorageLocation(...)` to a subclass of
293+
/// `StorageLocation` (using `cast_or_null<T>`).
294+
/// This assert-fails if the result of `getStorageLocation(...)` is not of
295+
/// type `T *`; if the storage location is not guaranteed to have type `T *`,
296+
/// consider using `dyn_cast_or_null<T>(getStorageLocation(...))` instead.
297+
template <typename T>
298+
std::enable_if_t<std::is_base_of_v<StorageLocation, T>, T *>
299+
get(const ValueDecl &D) const {
300+
return cast_or_null<T>(getStorageLocation(D));
301+
}
302+
template <typename T>
303+
std::enable_if_t<std::is_base_of_v<StorageLocation, T>, T *>
304+
get(const Expr &E) const {
305+
return cast_or_null<T>(getStorageLocation(E));
306+
}
307+
292308
/// Returns the storage location assigned to the `this` pointee in the
293309
/// environment or null if the `this` pointee has no assigned storage location
294310
/// in the environment.
@@ -457,6 +473,26 @@ class Environment {
457473
/// storage location in the environment, otherwise returns null.
458474
Value *getValue(const Expr &E) const;
459475

476+
/// Returns the result of casting `getValue(...)` to a subclass of `Value`
477+
/// (using `cast_or_null<T>`).
478+
/// This assert-fails if the result of `getValue(...)` is not of type `T *`;
479+
/// if the value is not guaranteed to have type `T *`, consider using
480+
/// `dyn_cast_or_null<T>(getValue(...))` instead.
481+
template <typename T>
482+
std::enable_if_t<std::is_base_of_v<Value, T>, T *>
483+
get(const StorageLocation &Loc) const {
484+
return cast_or_null<T>(getValue(Loc));
485+
}
486+
template <typename T>
487+
std::enable_if_t<std::is_base_of_v<Value, T>, T *>
488+
get(const ValueDecl &D) const {
489+
return cast_or_null<T>(getValue(D));
490+
}
491+
template <typename T>
492+
std::enable_if_t<std::is_base_of_v<Value, T>, T *> get(const Expr &E) const {
493+
return cast_or_null<T>(getValue(E));
494+
}
495+
460496
// FIXME: should we deprecate the following & call arena().create() directly?
461497

462498
/// Creates a `T` (some subclass of `Value`), forwarding `args` to the

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,7 +1034,7 @@ RecordStorageLocation *getImplicitObjectLocation(const CXXMemberCallExpr &MCE,
10341034
if (ImplicitObject == nullptr)
10351035
return nullptr;
10361036
if (ImplicitObject->getType()->isPointerType()) {
1037-
if (auto *Val = cast_or_null<PointerValue>(Env.getValue(*ImplicitObject)))
1037+
if (auto *Val = Env.get<PointerValue>(*ImplicitObject))
10381038
return &cast<RecordStorageLocation>(Val->getPointeeLoc());
10391039
return nullptr;
10401040
}
@@ -1048,11 +1048,11 @@ RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
10481048
if (Base == nullptr)
10491049
return nullptr;
10501050
if (ME.isArrow()) {
1051-
if (auto *Val = cast_or_null<PointerValue>(Env.getValue(*Base)))
1051+
if (auto *Val = Env.get<PointerValue>(*Base))
10521052
return &cast<RecordStorageLocation>(Val->getPointeeLoc());
10531053
return nullptr;
10541054
}
1055-
return cast_or_null<RecordStorageLocation>(Env.getStorageLocation(*Base));
1055+
return Env.get<RecordStorageLocation>(*Base);
10561056
}
10571057

10581058
std::vector<FieldDecl *> getFieldsForInitListExpr(const RecordDecl *RD) {
@@ -1077,7 +1077,7 @@ RecordValue &refreshRecordValue(const Expr &Expr, Environment &Env) {
10771077
assert(Expr.getType()->isRecordType());
10781078

10791079
if (Expr.isPRValue()) {
1080-
if (auto *ExistingVal = cast_or_null<RecordValue>(Env.getValue(Expr))) {
1080+
if (auto *ExistingVal = Env.get<RecordValue>(Expr)) {
10811081
auto &NewVal = Env.create<RecordValue>(ExistingVal->getLoc());
10821082
Env.setValue(Expr, NewVal);
10831083
Env.setValue(NewVal.getLoc(), NewVal);
@@ -1089,8 +1089,7 @@ RecordValue &refreshRecordValue(const Expr &Expr, Environment &Env) {
10891089
return NewVal;
10901090
}
10911091

1092-
if (auto *Loc =
1093-
cast_or_null<RecordStorageLocation>(Env.getStorageLocation(Expr))) {
1092+
if (auto *Loc = Env.get<RecordStorageLocation>(Expr)) {
10941093
auto &NewVal = Env.create<RecordValue>(*Loc);
10951094
Env.setValue(*Loc, NewVal);
10961095
return NewVal;

clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
226226

227227
/// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula.
228228
const Formula &forceBoolValue(Environment &Env, const Expr &Expr) {
229-
auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr));
229+
auto *Value = Env.get<BoolValue>(Expr);
230230
if (Value != nullptr)
231231
return Value->formula();
232232

@@ -267,7 +267,7 @@ BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) {
267267
if (OptionalLoc == nullptr)
268268
return nullptr;
269269
StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc);
270-
auto *HasValueVal = cast_or_null<BoolValue>(Env.getValue(HasValueLoc));
270+
auto *HasValueVal = Env.get<BoolValue>(HasValueLoc);
271271
if (HasValueVal == nullptr) {
272272
HasValueVal = &Env.makeAtomicBoolValue();
273273
Env.setValue(HasValueLoc, *HasValueVal);
@@ -406,7 +406,7 @@ void transferCallReturningOptional(const CallExpr *E,
406406
if (E->isPRValue()) {
407407
Loc = &State.Env.getResultObjectLocation(*E);
408408
} else {
409-
Loc = cast_or_null<RecordStorageLocation>(State.Env.getStorageLocation(*E));
409+
Loc = State.Env.get<RecordStorageLocation>(*E);
410410
if (Loc == nullptr) {
411411
Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E));
412412
State.Env.setStorageLocation(*E, *Loc);
@@ -449,8 +449,7 @@ BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E,
449449

450450
// This is a constructor/assignment call for `optional<T>` with argument of
451451
// type `optional<U>` such that `T` is constructible from `U`.
452-
auto *Loc =
453-
cast_or_null<RecordStorageLocation>(State.Env.getStorageLocation(E));
452+
auto *Loc = State.Env.get<RecordStorageLocation>(E);
454453
if (auto *HasValueVal = getHasValue(State.Env, Loc))
455454
return *HasValueVal;
456455
return State.Env.makeAtomicBoolValue();
@@ -471,8 +470,7 @@ void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
471470
LatticeTransferState &State) {
472471
assert(E->getNumArgs() > 0);
473472

474-
if (auto *Loc = cast_or_null<RecordStorageLocation>(
475-
State.Env.getStorageLocation(*E->getArg(0)))) {
473+
if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) {
476474
createOptionalValue(*Loc, HasValueVal, State.Env);
477475

478476
// Assign a storage location for the whole expression.
@@ -534,18 +532,15 @@ void transferSwapCall(const CXXMemberCallExpr *E,
534532
const MatchFinder::MatchResult &,
535533
LatticeTransferState &State) {
536534
assert(E->getNumArgs() == 1);
537-
auto *OtherLoc = cast_or_null<RecordStorageLocation>(
538-
State.Env.getStorageLocation(*E->getArg(0)));
535+
auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
539536
transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env);
540537
}
541538

542539
void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
543540
LatticeTransferState &State) {
544541
assert(E->getNumArgs() == 2);
545-
auto *Arg0Loc = cast_or_null<RecordStorageLocation>(
546-
State.Env.getStorageLocation(*E->getArg(0)));
547-
auto *Arg1Loc = cast_or_null<RecordStorageLocation>(
548-
State.Env.getStorageLocation(*E->getArg(1)));
542+
auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
543+
auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1));
549544
transferSwap(Arg0Loc, Arg1Loc, State.Env);
550545
}
551546

@@ -585,11 +580,9 @@ void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
585580
Environment &Env = State.Env;
586581
auto &A = Env.arena();
587582
auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
588-
auto *Arg0Loc = cast_or_null<RecordStorageLocation>(
589-
Env.getStorageLocation(*CmpExpr->getArg(0)));
583+
auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0));
590584
if (auto *LHasVal = getHasValue(Env, Arg0Loc)) {
591-
auto *Arg1Loc = cast_or_null<RecordStorageLocation>(
592-
Env.getStorageLocation(*CmpExpr->getArg(1)));
585+
auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1));
593586
if (auto *RHasVal = getHasValue(Env, Arg1Loc)) {
594587
if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
595588
CmpValue = &A.makeNot(*CmpValue);
@@ -603,7 +596,7 @@ void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
603596
const clang::Expr *E, Environment &Env) {
604597
auto &A = Env.arena();
605598
auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
606-
auto *Loc = cast_or_null<RecordStorageLocation>(Env.getStorageLocation(*E));
599+
auto *Loc = Env.get<RecordStorageLocation>(*E);
607600
if (auto *HasVal = getHasValue(Env, Loc)) {
608601
if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
609602
CmpValue = &A.makeNot(*CmpValue);
@@ -616,7 +609,7 @@ void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr,
616609
const clang::Expr *E, Environment &Env) {
617610
auto &A = Env.arena();
618611
auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
619-
auto *Loc = cast_or_null<RecordStorageLocation>(Env.getStorageLocation(*E));
612+
auto *Loc = Env.get<RecordStorageLocation>(*E);
620613
if (auto *HasVal = getHasValue(Env, Loc)) {
621614
if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
622615
CmpValue = &A.makeNot(*CmpValue);

clang/lib/Analysis/FlowSensitive/RecordOps.cpp

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

69-
RecordValue *SrcVal = cast_or_null<RecordValue>(Env.getValue(Src));
70-
RecordValue *DstVal = cast_or_null<RecordValue>(Env.getValue(Dst));
69+
RecordValue *SrcVal = Env.get<RecordValue>(Src);
70+
RecordValue *DstVal = Env.get<RecordValue>(Dst);
7171

7272
DstVal = &Env.create<RecordValue>(Dst);
7373
Env.setValue(Dst, *DstVal);
@@ -127,10 +127,10 @@ bool clang::dataflow::recordsEqual(const RecordStorageLocation &Loc1,
127127

128128
llvm::StringMap<Value *> Props1, Props2;
129129

130-
if (RecordValue *Val1 = cast_or_null<RecordValue>(Env1.getValue(Loc1)))
130+
if (RecordValue *Val1 = Env1.get<RecordValue>(Loc1))
131131
for (const auto &[Name, Value] : Val1->properties())
132132
Props1[Name] = Value;
133-
if (RecordValue *Val2 = cast_or_null<RecordValue>(Env2.getValue(Loc2)))
133+
if (RecordValue *Val2 = Env2.get<RecordValue>(Loc2))
134134
for (const auto &[Name, Value] : Val2->properties())
135135
Props2[Name] = Value;
136136

clang/lib/Analysis/FlowSensitive/Transfer.cpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,7 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
339339

340340
switch (S->getOpcode()) {
341341
case UO_Deref: {
342-
const auto *SubExprVal =
343-
cast_or_null<PointerValue>(Env.getValue(*SubExpr));
342+
const auto *SubExprVal = Env.get<PointerValue>(*SubExpr);
344343
if (SubExprVal == nullptr)
345344
break;
346345

@@ -467,8 +466,7 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
467466
const Expr *Arg = S->getArg(0);
468467
assert(Arg != nullptr);
469468

470-
auto *ArgLoc =
471-
cast_or_null<RecordStorageLocation>(Env.getStorageLocation(*Arg));
469+
auto *ArgLoc = Env.get<RecordStorageLocation>(*Arg);
472470
if (ArgLoc == nullptr)
473471
return;
474472

@@ -515,14 +513,12 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
515513

516514
RecordStorageLocation *LocSrc = nullptr;
517515
if (Arg1->isPRValue()) {
518-
if (auto *Val = cast_or_null<RecordValue>(Env.getValue(*Arg1)))
516+
if (auto *Val = Env.get<RecordValue>(*Arg1))
519517
LocSrc = &Val->getLoc();
520518
} else {
521-
LocSrc =
522-
cast_or_null<RecordStorageLocation>(Env.getStorageLocation(*Arg1));
519+
LocSrc = Env.get<RecordStorageLocation>(*Arg1);
523520
}
524-
auto *LocDst =
525-
cast_or_null<RecordStorageLocation>(Env.getStorageLocation(*Arg0));
521+
auto *LocDst = Env.get<RecordStorageLocation>(*Arg0);
526522

527523
if (LocSrc == nullptr || LocDst == nullptr)
528524
return;
@@ -676,7 +672,7 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
676672
auto Init = Inits[InitIdx++];
677673
assert(Base.getType().getCanonicalType() ==
678674
Init->getType().getCanonicalType());
679-
auto* BaseVal = cast_or_null<RecordValue>(Env.getValue(*Init));
675+
auto *BaseVal = Env.get<RecordValue>(*Init);
680676
if (!BaseVal)
681677
BaseVal = cast<RecordValue>(Env.createValue(Init->getType()));
682678
// Take ownership of the fields of the `RecordValue` for the base class

clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ class TerminatorVisitor
130130
if (Env.getValue(Cond) == nullptr)
131131
transfer(StmtToEnv, Cond, Env);
132132

133-
auto *Val = cast_or_null<BoolValue>(Env.getValue(Cond));
133+
auto *Val = Env.get<BoolValue>(Cond);
134134
// Value merging depends on flow conditions from different environments
135135
// being mutually exclusive -- that is, they cannot both be true in their
136136
// entirety (even if they may share some clauses). So, we need *some* value

clang/unittests/Analysis/FlowSensitive/SignAnalysisTest.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ void transferBinary(const BinaryOperator *BO, const MatchFinder::MatchResult &M,
133133
LatticeTransferState &State) {
134134
auto &A = State.Env.arena();
135135
const Formula *Comp;
136-
if (BoolValue *V = cast_or_null<BoolValue>(State.Env.getValue(*BO))) {
136+
if (BoolValue *V = State.Env.get<BoolValue>(*BO)) {
137137
Comp = &V->formula();
138138
} else {
139139
Comp = &A.makeAtomRef(A.makeAtom());

0 commit comments

Comments
 (0)