aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
diff options
context:
space:
mode:
authorMartin Braenne <mboehme@google.com>2023-05-12 11:59:21 +0000
committerMartin Braenne <mboehme@google.com>2023-05-15 04:33:29 +0000
commit48bc71505e03694caac6afb2431ff1157a2382a8 (patch)
treef5980e02f7cad79ef631df124ab81819901ee5ef /clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
parent47f5c54f997a59bb2c65abe6b8b811f6e7553456 (diff)
downloadllvm-48bc71505e03694caac6afb2431ff1157a2382a8.zip
llvm-48bc71505e03694caac6afb2431ff1157a2382a8.tar.gz
llvm-48bc71505e03694caac6afb2431ff1157a2382a8.tar.bz2
[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
Diffstat (limited to 'clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp')
-rw-r--r--clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp59
1 files changed, 44 insertions, 15 deletions
diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
index e306b55..b46a57f 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -372,10 +372,26 @@ bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) {
return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal);
}
+StorageLocation *maybeSkipPointer(StorageLocation *Loc,
+ const Environment &Env) {
+ if (Loc == nullptr)
+ return nullptr;
+ if (auto *Val = dyn_cast_or_null<PointerValue>(Env.getValue(*Loc)))
+ return &Val->getPointeeLoc();
+ return Loc;
+}
+
+Value *getValueBehindPossiblePointer(const Expr &E, const Environment &Env) {
+ Value *Val = Env.getValue(E, SkipPast::Reference);
+ if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Val))
+ return Env.getValue(PointerVal->getPointeeLoc());
+ return Val;
+}
+
void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
LatticeTransferState &State) {
if (auto *OptionalVal =
- State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
+ getValueBehindPossiblePointer(*ObjectExpr, State.Env)) {
if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr)
if (auto *Loc = maybeInitializeOptionalValueMember(
UnwrapExpr->getType(), *OptionalVal, State.Env))
@@ -396,8 +412,8 @@ void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
const MatchFinder::MatchResult &,
LatticeTransferState &State) {
if (auto *HasValueVal = getHasValue(
- State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(),
- SkipPast::ReferenceThenPointer))) {
+ State.Env, getValueBehindPossiblePointer(
+ *CallExpr->getImplicitObjectArgument(), State.Env))) {
auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
State.Env.setValue(CallExprLoc, *HasValueVal);
State.Env.setStorageLocation(*CallExpr, CallExprLoc);
@@ -419,8 +435,7 @@ void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
->getImplicitObjectArgument();
auto *HasValueVal = getHasValue(
- State.Env,
- State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer));
+ State.Env, getValueBehindPossiblePointer(*ObjectArgumentExpr, State.Env));
if (HasValueVal == nullptr)
return;
@@ -472,8 +487,8 @@ void transferCallReturningOptional(const CallExpr *E,
void assignOptionalValue(const Expr &E, Environment &Env,
BoolValue &HasValueVal) {
- if (auto *OptionalLoc =
- Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) {
+ if (auto *OptionalLoc = maybeSkipPointer(
+ Env.getStorageLocation(E, SkipPast::Reference), Env)) {
Env.setValue(*OptionalLoc, createOptionalValue(Env, HasValueVal));
}
}
@@ -550,13 +565,11 @@ void transferNulloptAssignment(const CXXOperatorCallExpr *E,
transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
}
-void transferSwap(const Expr &E1, SkipPast E1Skip, const Expr &E2,
+void transferSwap(StorageLocation *Loc1, StorageLocation *Loc2,
Environment &Env) {
// We account for cases where one or both of the optionals are not modeled,
// either lacking associated storage locations, or lacking values associated
// to such storage locations.
- auto *Loc1 = Env.getStorageLocation(E1, E1Skip);
- auto *Loc2 = Env.getStorageLocation(E2, SkipPast::Reference);
if (Loc1 == nullptr) {
if (Loc2 != nullptr)
@@ -590,14 +603,20 @@ void transferSwapCall(const CXXMemberCallExpr *E,
const MatchFinder::MatchResult &,
LatticeTransferState &State) {
assert(E->getNumArgs() == 1);
- transferSwap(*E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer,
- *E->getArg(0), State.Env);
+ transferSwap(maybeSkipPointer(
+ State.Env.getStorageLocation(*E->getImplicitObjectArgument(),
+ SkipPast::Reference),
+ State.Env),
+ State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference),
+ State.Env);
}
void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
LatticeTransferState &State) {
assert(E->getNumArgs() == 2);
- transferSwap(*E->getArg(0), SkipPast::Reference, *E->getArg(1), State.Env);
+ transferSwap(State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference),
+ State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference),
+ State.Env);
}
void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
@@ -756,6 +775,17 @@ auto buildTransferMatchSwitch() {
})
// optional::operator*, optional::operator->
+ // FIXME: This does something slightly strange for `operator->`.
+ // `transferUnwrapCall()` may create a new value of type `T` for the
+ // `optional<T>`, and it associates that value with `E`. In the case of
+ // `operator->`, `E` is a pointer. As a result, we associate an
+ // expression of pointer type with a storage location of non-pointer type
+ // `T`. This can confound other code that expects expressions of
+ // pointer type to be associated with `PointerValue`s, such as the
+ // centrally provided accessors `getImplicitObjectLocation()` and
+ // `getBaseObjectLocation()`, and this is the reason we need to use our
+ // own 'maybeSkipPointer()` and `getValueBehindPossiblePointer()` instead
+ // of these accessors.
.CaseOfCFGStmt<CallExpr>(valueOperatorCall(std::nullopt),
[](const CallExpr *E,
const MatchFinder::MatchResult &,
@@ -837,8 +867,7 @@ auto buildTransferMatchSwitch() {
std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr,
const Expr *ObjectExpr,
const Environment &Env) {
- if (auto *OptionalVal =
- Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
+ if (auto *OptionalVal = getValueBehindPossiblePointer(*ObjectExpr, Env)) {
auto *Prop = OptionalVal->getProperty("has_value");
if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
if (Env.flowConditionImplies(*HasValueVal))