-
Notifications
You must be signed in to change notification settings - Fork 10.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang][dataflow] Fix an issue with Environment::getResultObjectLocation()
.
#75483
Changes from all commits
3262b00
c366e66
ae82e05
4497aa2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this deserve a FIXME? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't add one originally because there's already an extensive FIXME in the documentation for |
||
} | ||
|
||
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)); | ||
}; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why the special handling of assertion failures? We don't typically do this, IIUC? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think what's unusual about this code is that it's handling a lot of expression node kinds generically. I think I've enumerated all the "is original record constructor" nodes that we need, but it felt sensible to put some kind of fallback in place in case there is some node kind I haven't discovered that doesn't have any children and would therefore return an invalid reference if we didn't have this fallback. |
||
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; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering whether it is better to enumerate the "transparent" AST nodes where we need to do the propagation.
My questions are:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function in the Crubit lifetime analysis gives a rough idea. It does essentially what the FIXME in the documentation for
getResultObjectLocation()
says we eventually want to do also in the framework.From this, we see that there are more "transparent" nodes than "is original record constructor" nodes. Also, the "is original record constructor" nodes are the ones for which we know that the framework produces a
RecordValue
-- so it seemed to make more sense to enumerate those.I think it's a wash. In both cases: