Skip to content

Commit

Permalink
[clang][dataflow] Add Environment::initializeFieldsWithValues(). (#…
Browse files Browse the repository at this point in the history
…81239)

This function will be useful when we change the behavior of record-type
prvalues
so that they directly initialize the associated result object. See also
the
comment here for more details:


https://github.com/llvm/llvm-project/blob/9e73656af524a2c592978aec91de67316c5ce69f/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h#L354

As part of this patch, we document and assert that synthetic fields may
not have
reference type.

There is no practical use case for this: A `StorageLocation` may not
have
reference type, and a synthetic field of the corresponding non-reference
type
can serve the same purpose.
  • Loading branch information
martinboehme committed Feb 13, 2024
1 parent 4588525 commit 270f2c5
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ class DataflowAnalysisContext {
/// to add to a `RecordStorageLocation` of a given type.
/// Typically, this is called from the constructor of a `DataflowAnalysis`
///
/// The field types returned by the callback may not have reference type.
///
/// To maintain the invariant that all `RecordStorageLocation`s of a given
/// type have the same fields:
/// * The callback must always return the same result for a given type
Expand Down Expand Up @@ -205,8 +207,17 @@ class DataflowAnalysisContext {
/// type.
llvm::StringMap<QualType> getSyntheticFields(QualType Type) {
assert(Type->isRecordType());
if (SyntheticFieldCallback)
return SyntheticFieldCallback(Type);
if (SyntheticFieldCallback) {
llvm::StringMap<QualType> Result = SyntheticFieldCallback(Type);
// Synthetic fields are not allowed to have reference type.
assert([&Result] {
for (const auto &Entry : Result)
if (Entry.getValue()->isReferenceType())
return false;
return true;
}());
return Result;
}
return {};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,14 @@ class Environment {
llvm::DenseSet<QualType> &Visited,
int Depth, int &CreatedValuesCount);

/// Initializes the fields (including synthetic fields) of `Loc` with values,
/// unless values of the field type are not supported or we hit one of the
/// limits at which we stop producing values (controlled by `Visited`,
/// `Depth`, and `CreatedValuesCount`).
void initializeFieldsWithValues(RecordStorageLocation &Loc,
llvm::DenseSet<QualType> &Visited, int Depth,
int &CreatedValuesCount);

/// Shared implementation of `createObject()` overloads.
/// `D` and `InitExpr` may be null.
StorageLocation &createObjectInternal(const ValueDecl *D, QualType Ty,
Expand Down
74 changes: 47 additions & 27 deletions clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -887,34 +887,10 @@ Value *Environment::createValueUnlessSelfReferential(

if (Type->isRecordType()) {
CreatedValuesCount++;
llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;
for (const FieldDecl *Field : DACtx->getModeledFields(Type)) {
assert(Field != nullptr);
auto &Loc = cast<RecordStorageLocation>(createStorageLocation(Type));
initializeFieldsWithValues(Loc, Visited, Depth, CreatedValuesCount);

QualType FieldType = Field->getType();

FieldLocs.insert(
{Field, &createLocAndMaybeValue(FieldType, Visited, Depth + 1,
CreatedValuesCount)});
}

RecordStorageLocation::SyntheticFieldMap SyntheticFieldLocs;
for (const auto &Entry : DACtx->getSyntheticFields(Type)) {
SyntheticFieldLocs.insert(
{Entry.getKey(),
&createLocAndMaybeValue(Entry.getValue(), Visited, Depth + 1,
CreatedValuesCount)});
}

RecordStorageLocation &Loc = DACtx->createRecordStorageLocation(
Type, std::move(FieldLocs), std::move(SyntheticFieldLocs));
RecordValue &RecordVal = create<RecordValue>(Loc);

// As we already have a storage location for the `RecordValue`, we can and
// should associate them in the environment.
setValue(Loc, RecordVal);

return &RecordVal;
return &refreshRecordValue(Loc, *this);
}

return nullptr;
Expand Down Expand Up @@ -943,6 +919,50 @@ Environment::createLocAndMaybeValue(QualType Ty,
return Loc;
}

void Environment::initializeFieldsWithValues(RecordStorageLocation &Loc,
llvm::DenseSet<QualType> &Visited,
int Depth,
int &CreatedValuesCount) {
auto initField = [&](QualType FieldType, StorageLocation &FieldLoc) {
if (FieldType->isRecordType()) {
auto &FieldRecordLoc = cast<RecordStorageLocation>(FieldLoc);
setValue(FieldRecordLoc, create<RecordValue>(FieldRecordLoc));
initializeFieldsWithValues(FieldRecordLoc, Visited, Depth + 1,
CreatedValuesCount);
} else {
if (!Visited.insert(FieldType.getCanonicalType()).second)
return;
if (Value *Val = createValueUnlessSelfReferential(
FieldType, Visited, Depth + 1, CreatedValuesCount))
setValue(FieldLoc, *Val);
Visited.erase(FieldType.getCanonicalType());
}
};

for (const auto [Field, FieldLoc] : Loc.children()) {
assert(Field != nullptr);
QualType FieldType = Field->getType();

if (FieldType->isReferenceType()) {
Loc.setChild(*Field,
&createLocAndMaybeValue(FieldType, Visited, Depth + 1,
CreatedValuesCount));
} else {
assert(FieldLoc != nullptr);
initField(FieldType, *FieldLoc);
}
}
for (const auto &[FieldName, FieldLoc] : Loc.synthetic_fields()) {
assert(FieldLoc != nullptr);
QualType FieldType = FieldLoc->getType();

// Synthetic fields cannot have reference type, so we don't need to deal
// with this case.
assert(!FieldType->isReferenceType());
initField(FieldType, Loc.getSyntheticField(FieldName));
}
}

StorageLocation &Environment::createObjectInternal(const ValueDecl *D,
QualType Ty,
const Expr *InitExpr) {
Expand Down

0 comments on commit 270f2c5

Please sign in to comment.