aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
diff options
context:
space:
mode:
authormartinboehme <mboehme@google.com>2023-12-18 09:10:03 +0100
committerGitHub <noreply@github.com>2023-12-18 09:10:03 +0100
commitca1034341cfec226c09ff0e473c6ecbcc2a1194c (patch)
tree109ad3d6307f9a701738ef979ecf5e5c7bc919b7 /clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
parent9bb47f7f8bcc17d90763d201f383d28489b9b071 (diff)
downloadllvm-ca1034341cfec226c09ff0e473c6ecbcc2a1194c.zip
llvm-ca1034341cfec226c09ff0e473c6ecbcc2a1194c.tar.gz
llvm-ca1034341cfec226c09ff0e473c6ecbcc2a1194c.tar.bz2
[clang][dataflow] Fix an issue with `Environment::getResultObjectLocation()`. (#75483)
So far, if there was a chain of record type prvalues, `getResultObjectLocation()` would assign a different result object location to each one. This makes no sense, of course, as all of these prvalues end up initializing the same result object. This patch fixes this by propagating storage locations up through the entire chain of prvalues. The new implementation also has the desirable effect of making it possible to make `getResultObjectLocation()` const, which seems appropriate given that, logically, it is just an accessor.
Diffstat (limited to 'clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp')
-rw-r--r--clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp79
1 files changed, 58 insertions, 21 deletions
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index b98037b..93919cd 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -726,27 +726,70 @@ void Environment::setStorageLocation(const Expr &E, StorageLocation &Loc) {
// so allow these as an exception.
assert(E.isGLValue() ||
E.getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn));
- setStorageLocationInternal(E, Loc);
+ const Expr &CanonE = ignoreCFGOmittedNodes(E);
+ assert(!ExprToLoc.contains(&CanonE));
+ ExprToLoc[&CanonE] = &Loc;
}
StorageLocation *Environment::getStorageLocation(const Expr &E) const {
// See comment in `setStorageLocation()`.
assert(E.isGLValue() ||
E.getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn));
- return getStorageLocationInternal(E);
+ auto It = ExprToLoc.find(&ignoreCFGOmittedNodes(E));
+ return It == ExprToLoc.end() ? nullptr : &*It->second;
+}
+
+// Returns whether a prvalue of record type is the one that originally
+// constructs the object (i.e. it doesn't propagate it from one of its
+// children).
+static bool isOriginalRecordConstructor(const Expr &RecordPRValue) {
+ if (auto *Init = dyn_cast<InitListExpr>(&RecordPRValue))
+ return !Init->isSemanticForm() || !Init->isTransparent();
+ return isa<CXXConstructExpr>(RecordPRValue) || isa<CallExpr>(RecordPRValue) ||
+ isa<LambdaExpr>(RecordPRValue) ||
+ // The framework currently does not propagate the objects created in
+ // the two branches of a `ConditionalOperator` because there is no way
+ // to reconcile their storage locations, which are different. We
+ // therefore claim that the `ConditionalOperator` is the expression
+ // that originally constructs the object.
+ // Ultimately, this will be fixed by propagating locations down from
+ // the result object, rather than up from the original constructor as
+ // we do now (see also the FIXME in the documentation for
+ // `getResultObjectLocation()`).
+ isa<ConditionalOperator>(RecordPRValue);
}
RecordStorageLocation &
-Environment::getResultObjectLocation(const Expr &RecordPRValue) {
+Environment::getResultObjectLocation(const Expr &RecordPRValue) const {
assert(RecordPRValue.getType()->isRecordType());
assert(RecordPRValue.isPRValue());
- if (StorageLocation *ExistingLoc = getStorageLocationInternal(RecordPRValue))
- return *cast<RecordStorageLocation>(ExistingLoc);
- auto &Loc = cast<RecordStorageLocation>(
- DACtx->getStableStorageLocation(RecordPRValue));
- setStorageLocationInternal(RecordPRValue, Loc);
- return Loc;
+ // Returns a storage location that we can use if assertions fail.
+ auto FallbackForAssertFailure =
+ [this, &RecordPRValue]() -> RecordStorageLocation & {
+ return cast<RecordStorageLocation>(
+ DACtx->getStableStorageLocation(RecordPRValue));
+ };
+
+ if (isOriginalRecordConstructor(RecordPRValue)) {
+ auto *Val = cast_or_null<RecordValue>(getValue(RecordPRValue));
+ // The builtin transfer function should have created a `RecordValue` for all
+ // original record constructors.
+ assert(Val);
+ if (!Val)
+ return FallbackForAssertFailure();
+ return Val->getLoc();
+ }
+
+ // Expression nodes that propagate a record prvalue should have exactly one
+ // child.
+ llvm::SmallVector<const Stmt *> children(RecordPRValue.child_begin(),
+ RecordPRValue.child_end());
+ assert(children.size() == 1);
+ if (children.empty())
+ return FallbackForAssertFailure();
+
+ return getResultObjectLocation(*cast<Expr>(children[0]));
}
PointerValue &Environment::getOrCreateNullPointerValue(QualType PointeeType) {
@@ -760,6 +803,11 @@ void Environment::setValue(const StorageLocation &Loc, Value &Val) {
}
void Environment::setValue(const Expr &E, Value &Val) {
+ if (auto *RecordVal = dyn_cast<RecordValue>(&Val)) {
+ assert(isOriginalRecordConstructor(E) ||
+ &RecordVal->getLoc() == &getResultObjectLocation(E));
+ }
+
assert(E.isPRValue());
ExprToVal[&E] = &Val;
}
@@ -799,18 +847,6 @@ Value *Environment::createValue(QualType Type) {
return Val;
}
-void Environment::setStorageLocationInternal(const Expr &E,
- StorageLocation &Loc) {
- const Expr &CanonE = ignoreCFGOmittedNodes(E);
- assert(!ExprToLoc.contains(&CanonE));
- ExprToLoc[&CanonE] = &Loc;
-}
-
-StorageLocation *Environment::getStorageLocationInternal(const Expr &E) const {
- auto It = ExprToLoc.find(&ignoreCFGOmittedNodes(E));
- return It == ExprToLoc.end() ? nullptr : &*It->second;
-}
-
Value *Environment::createValueUnlessSelfReferential(
QualType Type, llvm::DenseSet<QualType> &Visited, int Depth,
int &CreatedValuesCount) {
@@ -1044,6 +1080,7 @@ RecordValue &refreshRecordValue(const Expr &Expr, Environment &Env) {
if (auto *ExistingVal = cast_or_null<RecordValue>(Env.getValue(Expr))) {
auto &NewVal = Env.create<RecordValue>(ExistingVal->getLoc());
Env.setValue(Expr, NewVal);
+ Env.setValue(NewVal.getLoc(), NewVal);
return NewVal;
}