Skip to content

Commit 48bc715

Browse files
committed
[clang][dataflow] Eliminate SkipPast::ReferenceThenPointer.
As a replacement, we provide the accessors `getImplicitObjectLocation()` and `getBaseObjectLocation()`, which are higher-level constructs that cover the use cases in which `SkipPast::ReferenceThenPointer` was typically used. Unfortunately, it isn't possible to use these accessors in UncheckedOptionalAccessModel.cpp; I've added a FIXME to the code explaining the details. I initially attempted to resolve the issue as part of this patch, but it turned out to be non-trivial to fix. Instead, I have therefore added a lower-level replacement for `SkipPast::ReferenceThenPointer` that is used only within this file. The wider context of this change is that `SkipPast` will be going away entirely. See also the RFC at https://discourse.llvm.org/t/70086. Reviewed By: ymandel, gribozavr2 Differential Revision: https://reviews.llvm.org/D149838
1 parent 47f5c54 commit 48bc715

File tree

5 files changed

+94
-31
lines changed

5 files changed

+94
-31
lines changed

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,6 @@ enum class SkipPast {
4646
None,
4747
/// An optional reference should be skipped past.
4848
Reference,
49-
/// An optional reference should be skipped past, then an optional pointer
50-
/// should be skipped past.
51-
ReferenceThenPointer,
5249
};
5350

5451
/// Indicates the result of a tentative comparison.
@@ -477,6 +474,19 @@ class Environment {
477474
AtomicBoolValue *FlowConditionToken;
478475
};
479476

477+
/// Returns the storage location for the implicit object of a
478+
/// `CXXMemberCallExpr`, or null if none is defined in the environment.
479+
/// Dereferences the pointer if the member call expression was written using
480+
/// `->`.
481+
AggregateStorageLocation *
482+
getImplicitObjectLocation(const CXXMemberCallExpr &MCE, const Environment &Env);
483+
484+
/// Returns the storage location for the base object of a `MemberExpr`, or null
485+
/// if none is defined in the environment. Dereferences the pointer if the
486+
/// member expression was written using `->`.
487+
AggregateStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
488+
const Environment &Env);
489+
480490
} // namespace dataflow
481491
} // namespace clang
482492

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -782,11 +782,6 @@ StorageLocation &Environment::skip(StorageLocation &Loc, SkipPast SP) const {
782782
if (auto *Val = dyn_cast_or_null<ReferenceValue>(getValue(Loc)))
783783
return Val->getReferentLoc();
784784
return Loc;
785-
case SkipPast::ReferenceThenPointer:
786-
StorageLocation &LocPastRef = skip(Loc, SkipPast::Reference);
787-
if (auto *Val = dyn_cast_or_null<PointerValue>(getValue(LocPastRef)))
788-
return Val->getPointeeLoc();
789-
return LocPastRef;
790785
}
791786
llvm_unreachable("bad SkipPast kind");
792787
}
@@ -828,5 +823,39 @@ void Environment::dump() const {
828823
dump(llvm::dbgs());
829824
}
830825

826+
AggregateStorageLocation *
827+
getImplicitObjectLocation(const CXXMemberCallExpr &MCE,
828+
const Environment &Env) {
829+
Expr *ImplicitObject = MCE.getImplicitObjectArgument();
830+
if (ImplicitObject == nullptr)
831+
return nullptr;
832+
StorageLocation *Loc =
833+
Env.getStorageLocation(*ImplicitObject, SkipPast::Reference);
834+
if (Loc == nullptr)
835+
return nullptr;
836+
if (ImplicitObject->getType()->isPointerType()) {
837+
if (auto *Val = cast_or_null<PointerValue>(Env.getValue(*Loc)))
838+
return &cast<AggregateStorageLocation>(Val->getPointeeLoc());
839+
return nullptr;
840+
}
841+
return cast<AggregateStorageLocation>(Loc);
842+
}
843+
844+
AggregateStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
845+
const Environment &Env) {
846+
Expr *Base = ME.getBase();
847+
if (Base == nullptr)
848+
return nullptr;
849+
StorageLocation *Loc = Env.getStorageLocation(*Base, SkipPast::Reference);
850+
if (Loc == nullptr)
851+
return nullptr;
852+
if (ME.isArrow()) {
853+
if (auto *Val = cast_or_null<PointerValue>(Env.getValue(*Loc)))
854+
return &cast<AggregateStorageLocation>(Val->getPointeeLoc());
855+
return nullptr;
856+
}
857+
return cast<AggregateStorageLocation>(Loc);
858+
}
859+
831860
} // namespace dataflow
832861
} // namespace clang

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

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -372,10 +372,26 @@ bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) {
372372
return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal);
373373
}
374374

375+
StorageLocation *maybeSkipPointer(StorageLocation *Loc,
376+
const Environment &Env) {
377+
if (Loc == nullptr)
378+
return nullptr;
379+
if (auto *Val = dyn_cast_or_null<PointerValue>(Env.getValue(*Loc)))
380+
return &Val->getPointeeLoc();
381+
return Loc;
382+
}
383+
384+
Value *getValueBehindPossiblePointer(const Expr &E, const Environment &Env) {
385+
Value *Val = Env.getValue(E, SkipPast::Reference);
386+
if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Val))
387+
return Env.getValue(PointerVal->getPointeeLoc());
388+
return Val;
389+
}
390+
375391
void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
376392
LatticeTransferState &State) {
377393
if (auto *OptionalVal =
378-
State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
394+
getValueBehindPossiblePointer(*ObjectExpr, State.Env)) {
379395
if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr)
380396
if (auto *Loc = maybeInitializeOptionalValueMember(
381397
UnwrapExpr->getType(), *OptionalVal, State.Env))
@@ -396,8 +412,8 @@ void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
396412
const MatchFinder::MatchResult &,
397413
LatticeTransferState &State) {
398414
if (auto *HasValueVal = getHasValue(
399-
State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(),
400-
SkipPast::ReferenceThenPointer))) {
415+
State.Env, getValueBehindPossiblePointer(
416+
*CallExpr->getImplicitObjectArgument(), State.Env))) {
401417
auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
402418
State.Env.setValue(CallExprLoc, *HasValueVal);
403419
State.Env.setStorageLocation(*CallExpr, CallExprLoc);
@@ -419,8 +435,7 @@ void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
419435
->getImplicitObjectArgument();
420436

421437
auto *HasValueVal = getHasValue(
422-
State.Env,
423-
State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer));
438+
State.Env, getValueBehindPossiblePointer(*ObjectArgumentExpr, State.Env));
424439
if (HasValueVal == nullptr)
425440
return;
426441

@@ -472,8 +487,8 @@ void transferCallReturningOptional(const CallExpr *E,
472487

473488
void assignOptionalValue(const Expr &E, Environment &Env,
474489
BoolValue &HasValueVal) {
475-
if (auto *OptionalLoc =
476-
Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) {
490+
if (auto *OptionalLoc = maybeSkipPointer(
491+
Env.getStorageLocation(E, SkipPast::Reference), Env)) {
477492
Env.setValue(*OptionalLoc, createOptionalValue(Env, HasValueVal));
478493
}
479494
}
@@ -550,13 +565,11 @@ void transferNulloptAssignment(const CXXOperatorCallExpr *E,
550565
transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
551566
}
552567

553-
void transferSwap(const Expr &E1, SkipPast E1Skip, const Expr &E2,
568+
void transferSwap(StorageLocation *Loc1, StorageLocation *Loc2,
554569
Environment &Env) {
555570
// We account for cases where one or both of the optionals are not modeled,
556571
// either lacking associated storage locations, or lacking values associated
557572
// to such storage locations.
558-
auto *Loc1 = Env.getStorageLocation(E1, E1Skip);
559-
auto *Loc2 = Env.getStorageLocation(E2, SkipPast::Reference);
560573

561574
if (Loc1 == nullptr) {
562575
if (Loc2 != nullptr)
@@ -590,14 +603,20 @@ void transferSwapCall(const CXXMemberCallExpr *E,
590603
const MatchFinder::MatchResult &,
591604
LatticeTransferState &State) {
592605
assert(E->getNumArgs() == 1);
593-
transferSwap(*E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer,
594-
*E->getArg(0), State.Env);
606+
transferSwap(maybeSkipPointer(
607+
State.Env.getStorageLocation(*E->getImplicitObjectArgument(),
608+
SkipPast::Reference),
609+
State.Env),
610+
State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference),
611+
State.Env);
595612
}
596613

597614
void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
598615
LatticeTransferState &State) {
599616
assert(E->getNumArgs() == 2);
600-
transferSwap(*E->getArg(0), SkipPast::Reference, *E->getArg(1), State.Env);
617+
transferSwap(State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference),
618+
State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference),
619+
State.Env);
601620
}
602621

603622
void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
@@ -756,6 +775,17 @@ auto buildTransferMatchSwitch() {
756775
})
757776

758777
// optional::operator*, optional::operator->
778+
// FIXME: This does something slightly strange for `operator->`.
779+
// `transferUnwrapCall()` may create a new value of type `T` for the
780+
// `optional<T>`, and it associates that value with `E`. In the case of
781+
// `operator->`, `E` is a pointer. As a result, we associate an
782+
// expression of pointer type with a storage location of non-pointer type
783+
// `T`. This can confound other code that expects expressions of
784+
// pointer type to be associated with `PointerValue`s, such as the
785+
// centrally provided accessors `getImplicitObjectLocation()` and
786+
// `getBaseObjectLocation()`, and this is the reason we need to use our
787+
// own 'maybeSkipPointer()` and `getValueBehindPossiblePointer()` instead
788+
// of these accessors.
759789
.CaseOfCFGStmt<CallExpr>(valueOperatorCall(std::nullopt),
760790
[](const CallExpr *E,
761791
const MatchFinder::MatchResult &,
@@ -837,8 +867,7 @@ auto buildTransferMatchSwitch() {
837867
std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr,
838868
const Expr *ObjectExpr,
839869
const Environment &Env) {
840-
if (auto *OptionalVal =
841-
Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
870+
if (auto *OptionalVal = getValueBehindPossiblePointer(*ObjectExpr, Env)) {
842871
auto *Prop = OptionalVal->getProperty("has_value");
843872
if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
844873
if (Env.flowConditionImplies(*HasValueVal))

clang/lib/Analysis/FlowSensitive/Transfer.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -542,10 +542,7 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
542542
}
543543
}
544544

545-
// The receiver can be either a value or a pointer to a value. Skip past the
546-
// indirection to handle both cases.
547-
auto *BaseLoc = cast_or_null<AggregateStorageLocation>(
548-
Env.getStorageLocation(*S->getBase(), SkipPast::ReferenceThenPointer));
545+
AggregateStorageLocation *BaseLoc = getBaseObjectLocation(*S, Env);
549546
if (BaseLoc == nullptr)
550547
return;
551548

clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -372,8 +372,7 @@ class SpecialBoolAnalysis final
372372
auto *Object = E->getImplicitObjectArgument();
373373
assert(Object != nullptr);
374374

375-
auto *ObjectLoc =
376-
Env.getStorageLocation(*Object, SkipPast::ReferenceThenPointer);
375+
auto *ObjectLoc = getImplicitObjectLocation(*E, Env);
377376
assert(ObjectLoc != nullptr);
378377

379378
auto &ConstructorVal = *Env.createValue(Object->getType());
@@ -532,8 +531,7 @@ class OptionalIntAnalysis final
532531
auto *Object = E->getArg(0);
533532
assert(Object != nullptr);
534533

535-
auto *ObjectLoc =
536-
Env.getStorageLocation(*Object, SkipPast::ReferenceThenPointer);
534+
auto *ObjectLoc = Env.getStorageLocation(*Object, SkipPast::Reference);
537535
assert(ObjectLoc != nullptr);
538536

539537
auto &ConstructorVal = *Env.createValue(Object->getType());

0 commit comments

Comments
 (0)