aboutsummaryrefslogtreecommitdiff
path: root/clang/lib
diff options
context:
space:
mode:
authorMartin Braenne <mboehme@google.com>2023-07-17 06:27:59 +0000
committerMartin Braenne <mboehme@google.com>2023-07-17 07:26:10 +0000
commit6d768548ecc0ca37026986f397392c1d0ace9736 (patch)
tree58d657134c6d88484d96d43f4a912321f6854fd7 /clang/lib
parent4782597e3cd1b26cf8bd437e36fd6320f55d3d89 (diff)
downloadllvm-6d768548ecc0ca37026986f397392c1d0ace9736.zip
llvm-6d768548ecc0ca37026986f397392c1d0ace9736.tar.gz
llvm-6d768548ecc0ca37026986f397392c1d0ace9736.tar.bz2
[clang][dataflow] Add `DataflowEnvironment::createObject()`.
This consolidates the code used in various places to initialize objects (usually for variables) into one central location. It will also help reduce the number of changes needed when we make the upcoming API changes to `AggregateStorageLocation` and `StructValue`. Depends On D155074 Reviewed By: ymandel, xazax.hun Differential Revision: https://reviews.llvm.org/D155075
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp2
-rw-r--r--clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp86
-rw-r--r--clang/lib/Analysis/FlowSensitive/Transfer.cpp61
3 files changed, 60 insertions, 89 deletions
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
index 531115e..bb1c2c7 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -72,7 +72,7 @@ StorageLocation &
DataflowAnalysisContext::getStableStorageLocation(const VarDecl &D) {
if (auto *Loc = getStorageLocation(D))
return *Loc;
- auto &Loc = createStorageLocation(D.getType());
+ auto &Loc = createStorageLocation(D.getType().getNonReferenceType());
setStorageLocation(D, Loc);
return Loc;
}
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index a49c529..fdd6b59 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -260,10 +260,8 @@ void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) {
for (const VarDecl *D : Vars) {
if (getStorageLocation(*D) != nullptr)
continue;
- auto &Loc = createStorageLocation(D->getType().getNonReferenceType());
- setStorageLocation(*D, Loc);
- if (auto *Val = createValue(D->getType().getNonReferenceType()))
- setValue(Loc, *Val);
+
+ setStorageLocation(*D, createObject(*D));
}
for (const FunctionDecl *FD : Funcs) {
@@ -296,16 +294,7 @@ Environment::Environment(DataflowAnalysisContext &DACtx,
for (const auto *ParamDecl : FuncDecl->parameters()) {
assert(ParamDecl != nullptr);
- // References aren't objects, so the reference itself doesn't have a
- // storage location. Instead, the storage location for a reference refers
- // directly to an object of the referenced type -- so strip off any
- // reference from the type.
- auto &ParamLoc =
- createStorageLocation(ParamDecl->getType().getNonReferenceType());
- setStorageLocation(*ParamDecl, ParamLoc);
- if (Value *ParamVal =
- createValue(ParamDecl->getType().getNonReferenceType()))
- setValue(ParamLoc, *ParamVal);
+ setStorageLocation(*ParamDecl, createObject(*ParamDecl, nullptr));
}
}
@@ -382,27 +371,8 @@ void Environment::pushCallInternal(const FunctionDecl *FuncDecl,
// overloaded operators implemented as member functions, and parameter packs.
for (unsigned ArgIndex = 0; ArgIndex < Args.size(); ++ParamIt, ++ArgIndex) {
assert(ParamIt != FuncDecl->param_end());
-
- const Expr *Arg = Args[ArgIndex];
- auto *ArgLoc = getStorageLocation(*Arg, SkipPast::Reference);
- if (ArgLoc == nullptr)
- continue;
-
const VarDecl *Param = *ParamIt;
-
- QualType ParamType = Param->getType();
- if (ParamType->isReferenceType()) {
- setStorageLocation(*Param, *ArgLoc);
- } else {
- auto &Loc = createStorageLocation(*Param);
- setStorageLocation(*Param, Loc);
-
- if (auto *ArgVal = getValue(*ArgLoc)) {
- setValue(Loc, *ArgVal);
- } else if (Value *Val = createValue(ParamType)) {
- setValue(Loc, *Val);
- }
- }
+ setStorageLocation(*Param, createObject(*Param, Args[ArgIndex]));
}
}
@@ -872,6 +842,54 @@ Value *Environment::createValueUnlessSelfReferential(
return nullptr;
}
+StorageLocation &Environment::createObjectInternal(const VarDecl *D,
+ QualType Ty,
+ const Expr *InitExpr) {
+ if (Ty->isReferenceType()) {
+ // Although variables of reference type always need to be initialized, it
+ // can happen that we can't see the initializer, so `InitExpr` may still
+ // be null.
+ if (InitExpr) {
+ if (auto *InitExprLoc =
+ getStorageLocation(*InitExpr, SkipPast::Reference))
+ return *InitExprLoc;
+ }
+
+ // Even though we have an initializer, we might not get an
+ // InitExprLoc, for example if the InitExpr is a CallExpr for which we
+ // don't have a function body. In this case, we just invent a storage
+ // location and value -- it's the best we can do.
+ return createObjectInternal(D, Ty.getNonReferenceType(), nullptr);
+ }
+
+ Value *Val = nullptr;
+ if (InitExpr)
+ // In the (few) cases where an expression is intentionally
+ // "uninterpreted", `InitExpr` is not associated with a value. There are
+ // two ways to handle this situation: propagate the status, so that
+ // uninterpreted initializers result in uninterpreted variables, or
+ // provide a default value. We choose the latter so that later refinements
+ // of the variable can be used for reasoning about the surrounding code.
+ // For this reason, we let this case be handled by the `createValue()`
+ // call below.
+ //
+ // FIXME. If and when we interpret all language cases, change this to
+ // assert that `InitExpr` is interpreted, rather than supplying a
+ // default value (assuming we don't update the environment API to return
+ // references).
+ Val = getValueStrict(*InitExpr);
+ if (!Val)
+ Val = createValue(Ty);
+
+ StorageLocation &Loc =
+ D ? createStorageLocation(*D) : createStorageLocation(Ty);
+
+ if (Val)
+ setValue(Loc, *Val);
+
+ return Loc;
+}
+
StorageLocation &Environment::skip(StorageLocation &Loc, SkipPast SP) const {
switch (SP) {
case SkipPast::None:
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index 4c97e81..f51b9f3 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -208,62 +208,15 @@ public:
if (D.hasGlobalStorage())
return;
- if (D.getType()->isReferenceType()) {
- // If this is the holding variable for a `BindingDecl`, we may already
- // have a storage location set up -- so check. (See also explanation below
- // where we process the `BindingDecl`.)
- if (Env.getStorageLocation(D) == nullptr) {
- const Expr *InitExpr = D.getInit();
- assert(InitExpr != nullptr);
- if (auto *InitExprLoc =
- Env.getStorageLocation(*InitExpr, SkipPast::Reference)) {
- Env.setStorageLocation(D, *InitExprLoc);
- } else {
- // Even though we have an initializer, we might not get an
- // InitExprLoc, for example if the InitExpr is a CallExpr for which we
- // don't have a function body. In this case, we just invent a storage
- // location and value -- it's the best we can do.
- StorageLocation &Loc =
- Env.createStorageLocation(D.getType().getNonReferenceType());
- Env.setStorageLocation(D, Loc);
- if (Value *Val = Env.createValue(D.getType().getNonReferenceType()))
- Env.setValue(Loc, *Val);
- }
- }
- } else {
- // Not a reference type.
+ // If this is the holding variable for a `BindingDecl`, we may already
+ // have a storage location set up -- so check. (See also explanation below
+ // where we process the `BindingDecl`.)
+ if (D.getType()->isReferenceType() && Env.getStorageLocation(D) != nullptr)
+ return;
- assert(Env.getStorageLocation(D) == nullptr);
- StorageLocation &Loc = Env.createStorageLocation(D);
- Env.setStorageLocation(D, Loc);
+ assert(Env.getStorageLocation(D) == nullptr);
- const Expr *InitExpr = D.getInit();
- if (InitExpr == nullptr) {
- // No initializer expression - associate `Loc` with a new value.
- if (Value *Val = Env.createValue(D.getType()))
- Env.setValue(Loc, *Val);
- return;
- }
-
- if (auto *InitExprVal = Env.getValueStrict(*InitExpr))
- Env.setValue(Loc, *InitExprVal);
-
- if (Env.getValue(Loc) == nullptr) {
- // We arrive here in (the few) cases where an expression is
- // intentionally "uninterpreted". There are two ways to handle this
- // situation: propagate the status, so that uninterpreted initializers
- // result in uninterpreted variables, or provide a default value. We
- // choose the latter so that later refinements of the variable can be
- // used for reasoning about the surrounding code.
- //
- // FIXME. If and when we interpret all language cases, change this to
- // assert that `InitExpr` is interpreted, rather than supplying a
- // default value (assuming we don't update the environment API to return
- // references).
- if (Value *Val = Env.createValue(D.getType()))
- Env.setValue(Loc, *Val);
- }
- }
+ Env.setStorageLocation(D, Env.createObject(D));
// `DecompositionDecl` must be handled after we've interpreted the loc
// itself, because the binding expression refers back to the