[clang] [clang][dataflow] Expose fields, globals, and functions referenced. (PR #88534)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 12 09:33:10 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Samira Bazuzi (bazuzi)
<details>
<summary>Changes</summary>
Exposes the collection functionality, but does not alter it beyond using a return value instead of output parameters. Also relocates underlying and related functions and a class from DataflowEnvironment's files to DataflowAnalysisContext's files, as no Environment is needed.
---
Patch is 21.33 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/88534.diff
5 Files Affected:
- (modified) clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h (+46)
- (modified) clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h (-36)
- (modified) clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp (+174)
- (modified) clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp (+4-172)
- (modified) clang/lib/Analysis/FlowSensitive/Transfer.cpp (+1)
``````````diff
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
index 909a91059438ca..a34e5f603eb396 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -62,6 +62,52 @@ FieldSet getObjectFields(QualType Type);
bool containsSameFields(const FieldSet &Fields,
const RecordStorageLocation::FieldToLoc &FieldLocs);
+/// Returns the fields of a `RecordDecl` that are initialized by an
+/// `InitListExpr`, in the order in which they appear in
+/// `InitListExpr::inits()`.
+/// `Init->getType()` must be a record type.
+std::vector<const FieldDecl *>
+getFieldsForInitListExpr(const InitListExpr *InitList);
+
+/// Helper class for initialization of a record with an `InitListExpr`.
+/// `InitListExpr::inits()` contains the initializers for both the base classes
+/// and the fields of the record; this helper class separates these out into two
+/// different lists. In addition, it deals with special cases associated with
+/// unions.
+class RecordInitListHelper {
+public:
+ // `InitList` must have record type.
+ RecordInitListHelper(const InitListExpr *InitList);
+
+ // Base classes with their associated initializer expressions.
+ ArrayRef<std::pair<const CXXBaseSpecifier *, Expr *>> base_inits() const {
+ return BaseInits;
+ }
+
+ // Fields with their associated initializer expressions.
+ ArrayRef<std::pair<const FieldDecl *, Expr *>> field_inits() const {
+ return FieldInits;
+ }
+
+private:
+ SmallVector<std::pair<const CXXBaseSpecifier *, Expr *>> BaseInits;
+ SmallVector<std::pair<const FieldDecl *, Expr *>> FieldInits;
+
+ // We potentially synthesize an `ImplicitValueInitExpr` for unions. It's a
+ // member variable because we store a pointer to it in `FieldInits`.
+ std::optional<ImplicitValueInitExpr> ImplicitValueInitForUnion;
+};
+
+struct FieldsGlobalsAndFuncs {
+ FieldSet Fields;
+ // Globals includes all variables with global storage, notably including
+ // static data members and static variables declared within a function.
+ llvm::DenseSet<const VarDecl *> Globals;
+ llvm::DenseSet<const FunctionDecl *> Funcs;
+};
+
+FieldsGlobalsAndFuncs getFieldsGlobalsAndFuncs(const FunctionDecl &FD);
+
struct ContextSensitiveOptions {
/// The maximum depth to analyze. A value of zero is equivalent to disabling
/// context-sensitive analysis entirely.
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
index 706664d7db1c25..4277792219c0af 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -775,42 +775,6 @@ RecordStorageLocation *getImplicitObjectLocation(const CXXMemberCallExpr &MCE,
RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
const Environment &Env);
-/// Returns the fields of a `RecordDecl` that are initialized by an
-/// `InitListExpr`, in the order in which they appear in
-/// `InitListExpr::inits()`.
-/// `Init->getType()` must be a record type.
-std::vector<const FieldDecl *>
-getFieldsForInitListExpr(const InitListExpr *InitList);
-
-/// Helper class for initialization of a record with an `InitListExpr`.
-/// `InitListExpr::inits()` contains the initializers for both the base classes
-/// and the fields of the record; this helper class separates these out into two
-/// different lists. In addition, it deals with special cases associated with
-/// unions.
-class RecordInitListHelper {
-public:
- // `InitList` must have record type.
- RecordInitListHelper(const InitListExpr *InitList);
-
- // Base classes with their associated initializer expressions.
- ArrayRef<std::pair<const CXXBaseSpecifier *, Expr *>> base_inits() const {
- return BaseInits;
- }
-
- // Fields with their associated initializer expressions.
- ArrayRef<std::pair<const FieldDecl *, Expr *>> field_inits() const {
- return FieldInits;
- }
-
-private:
- SmallVector<std::pair<const CXXBaseSpecifier *, Expr *>> BaseInits;
- SmallVector<std::pair<const FieldDecl *, Expr *>> FieldInits;
-
- // We potentially synthesize an `ImplicitValueInitExpr` for unions. It's a
- // member variable because we store a pointer to it in `FieldInits`.
- std::optional<ImplicitValueInitExpr> ImplicitValueInitForUnion;
-};
-
/// Associates a new `RecordValue` with `Loc` and returns the new value.
RecordValue &refreshRecordValue(RecordStorageLocation &Loc, Environment &Env);
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
index d520539dd25355..74b1299f014c66 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -55,6 +55,180 @@ FieldSet DataflowAnalysisContext::getModeledFields(QualType Type) {
return llvm::set_intersection(getObjectFields(Type), ModeledFields);
}
+std::vector<const FieldDecl *>
+getFieldsForInitListExpr(const InitListExpr *InitList) {
+ const RecordDecl *RD = InitList->getType()->getAsRecordDecl();
+ assert(RD != nullptr);
+
+ std::vector<const FieldDecl *> Fields;
+
+ if (InitList->getType()->isUnionType()) {
+ Fields.push_back(InitList->getInitializedFieldInUnion());
+ return Fields;
+ }
+
+ // Unnamed bitfields are only used for padding and do not appear in
+ // `InitListExpr`'s inits. However, those fields do appear in `RecordDecl`'s
+ // field list, and we thus need to remove them before mapping inits to
+ // fields to avoid mapping inits to the wrongs fields.
+ llvm::copy_if(
+ RD->fields(), std::back_inserter(Fields),
+ [](const FieldDecl *Field) { return !Field->isUnnamedBitfield(); });
+ return Fields;
+}
+
+RecordInitListHelper::RecordInitListHelper(const InitListExpr *InitList) {
+ auto *RD = InitList->getType()->getAsCXXRecordDecl();
+ assert(RD != nullptr);
+
+ std::vector<const FieldDecl *> Fields = getFieldsForInitListExpr(InitList);
+ ArrayRef<Expr *> Inits = InitList->inits();
+
+ // Unions initialized with an empty initializer list need special treatment.
+ // For structs/classes initialized with an empty initializer list, Clang
+ // puts `ImplicitValueInitExpr`s in `InitListExpr::inits()`, but for unions,
+ // it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves.
+ SmallVector<Expr *> InitsForUnion;
+ if (InitList->getType()->isUnionType() && Inits.empty()) {
+ assert(Fields.size() == 1);
+ ImplicitValueInitForUnion.emplace(Fields.front()->getType());
+ InitsForUnion.push_back(&*ImplicitValueInitForUnion);
+ Inits = InitsForUnion;
+ }
+
+ size_t InitIdx = 0;
+
+ assert(Fields.size() + RD->getNumBases() == Inits.size());
+ for (const CXXBaseSpecifier &Base : RD->bases()) {
+ assert(InitIdx < Inits.size());
+ Expr *Init = Inits[InitIdx++];
+ BaseInits.emplace_back(&Base, Init);
+ }
+
+ assert(Fields.size() == Inits.size() - InitIdx);
+ for (const FieldDecl *Field : Fields) {
+ assert(InitIdx < Inits.size());
+ Expr *Init = Inits[InitIdx++];
+ FieldInits.emplace_back(Field, Init);
+ }
+}
+
+static void insertIfGlobal(const Decl &D,
+ llvm::DenseSet<const VarDecl *> &Vars) {
+ if (auto *V = dyn_cast<VarDecl>(&D))
+ if (V->hasGlobalStorage())
+ Vars.insert(V);
+}
+
+static void insertIfFunction(const Decl &D,
+ llvm::DenseSet<const FunctionDecl *> &Funcs) {
+ if (auto *FD = dyn_cast<FunctionDecl>(&D))
+ Funcs.insert(FD);
+}
+
+static MemberExpr *getMemberForAccessor(const CXXMemberCallExpr &C) {
+ // Use getCalleeDecl instead of getMethodDecl in order to handle
+ // pointer-to-member calls.
+ const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(C.getCalleeDecl());
+ if (!MethodDecl)
+ return nullptr;
+ auto *Body = dyn_cast_or_null<CompoundStmt>(MethodDecl->getBody());
+ if (!Body || Body->size() != 1)
+ return nullptr;
+ if (auto *RS = dyn_cast<ReturnStmt>(*Body->body_begin()))
+ if (auto *Return = RS->getRetValue())
+ return dyn_cast<MemberExpr>(Return->IgnoreParenImpCasts());
+ return nullptr;
+}
+
+static void
+getFieldsGlobalsAndFuncs(const Decl &D, FieldSet &Fields,
+ llvm::DenseSet<const VarDecl *> &Vars,
+ llvm::DenseSet<const FunctionDecl *> &Funcs) {
+ insertIfGlobal(D, Vars);
+ insertIfFunction(D, Funcs);
+ if (const auto *Decomp = dyn_cast<DecompositionDecl>(&D))
+ for (const auto *B : Decomp->bindings())
+ if (auto *ME = dyn_cast_or_null<MemberExpr>(B->getBinding()))
+ // FIXME: should we be using `E->getFoundDecl()`?
+ if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()))
+ Fields.insert(FD);
+}
+
+/// Traverses `S` and inserts into `Fields`, `Vars` and `Funcs` any fields,
+/// global variables and functions that are declared in or referenced from
+/// sub-statements.
+static void
+getFieldsGlobalsAndFuncs(const Stmt &S, FieldSet &Fields,
+ llvm::DenseSet<const VarDecl *> &Vars,
+ llvm::DenseSet<const FunctionDecl *> &Funcs) {
+ for (auto *Child : S.children())
+ if (Child != nullptr)
+ getFieldsGlobalsAndFuncs(*Child, Fields, Vars, Funcs);
+ if (const auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(&S))
+ getFieldsGlobalsAndFuncs(*DefaultArg->getExpr(), Fields, Vars, Funcs);
+ if (const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(&S))
+ getFieldsGlobalsAndFuncs(*DefaultInit->getExpr(), Fields, Vars, Funcs);
+
+ if (auto *DS = dyn_cast<DeclStmt>(&S)) {
+ if (DS->isSingleDecl())
+ getFieldsGlobalsAndFuncs(*DS->getSingleDecl(), Fields, Vars, Funcs);
+ else
+ for (auto *D : DS->getDeclGroup())
+ getFieldsGlobalsAndFuncs(*D, Fields, Vars, Funcs);
+ } else if (auto *E = dyn_cast<DeclRefExpr>(&S)) {
+ insertIfGlobal(*E->getDecl(), Vars);
+ insertIfFunction(*E->getDecl(), Funcs);
+ } else if (const auto *C = dyn_cast<CXXMemberCallExpr>(&S)) {
+ // If this is a method that returns a member variable but does nothing else,
+ // model the field of the return value.
+ if (MemberExpr *E = getMemberForAccessor(*C))
+ if (const auto *FD = dyn_cast<FieldDecl>(E->getMemberDecl()))
+ Fields.insert(FD);
+ } else if (auto *E = dyn_cast<MemberExpr>(&S)) {
+ // FIXME: should we be using `E->getFoundDecl()`?
+ const ValueDecl *VD = E->getMemberDecl();
+ insertIfGlobal(*VD, Vars);
+ insertIfFunction(*VD, Funcs);
+ if (const auto *FD = dyn_cast<FieldDecl>(VD))
+ Fields.insert(FD);
+ } else if (auto *InitList = dyn_cast<InitListExpr>(&S)) {
+ if (InitList->getType()->isRecordType())
+ for (const auto *FD : getFieldsForInitListExpr(InitList))
+ Fields.insert(FD);
+ }
+}
+
+/// Collects and returns fields, global variables and functions that are
+/// declared in or referenced from `FD`.
+FieldsGlobalsAndFuncs getFieldsGlobalsAndFuncs(const FunctionDecl &FD) {
+ FieldsGlobalsAndFuncs Result;
+ // Look for global variable and field references in the
+ // constructor-initializers.
+ if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(&FD)) {
+ for (const auto *Init : CtorDecl->inits()) {
+ if (Init->isMemberInitializer()) {
+ Result.Fields.insert(Init->getMember());
+ } else if (Init->isIndirectMemberInitializer()) {
+ for (const auto *I : Init->getIndirectMember()->chain())
+ Result.Fields.insert(cast<FieldDecl>(I));
+ }
+ const Expr *E = Init->getInit();
+ assert(E != nullptr);
+ getFieldsGlobalsAndFuncs(*E, Result.Fields, Result.Globals, Result.Funcs);
+ }
+ // Add all fields mentioned in default member initializers.
+ for (const FieldDecl *F : CtorDecl->getParent()->fields())
+ if (const auto *I = F->getInClassInitializer())
+ getFieldsGlobalsAndFuncs(*I, Result.Fields, Result.Globals,
+ Result.Funcs);
+ }
+ getFieldsGlobalsAndFuncs(*FD.getBody(), Result.Fields, Result.Globals,
+ Result.Funcs);
+
+ return Result;
+}
+
void DataflowAnalysisContext::addModeledFields(const FieldSet &Fields) {
ModeledFields.set_union(Fields);
}
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index bea15ce9bd24d1..6dae25441968de 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -304,93 +304,6 @@ widenKeyToValueMap(const llvm::MapVector<Key, Value *> &CurMap,
return WidenedMap;
}
-/// Initializes a global storage value.
-static void insertIfGlobal(const Decl &D,
- llvm::DenseSet<const VarDecl *> &Vars) {
- if (auto *V = dyn_cast<VarDecl>(&D))
- if (V->hasGlobalStorage())
- Vars.insert(V);
-}
-
-static void insertIfFunction(const Decl &D,
- llvm::DenseSet<const FunctionDecl *> &Funcs) {
- if (auto *FD = dyn_cast<FunctionDecl>(&D))
- Funcs.insert(FD);
-}
-
-static MemberExpr *getMemberForAccessor(const CXXMemberCallExpr &C) {
- // Use getCalleeDecl instead of getMethodDecl in order to handle
- // pointer-to-member calls.
- const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(C.getCalleeDecl());
- if (!MethodDecl)
- return nullptr;
- auto *Body = dyn_cast_or_null<CompoundStmt>(MethodDecl->getBody());
- if (!Body || Body->size() != 1)
- return nullptr;
- if (auto *RS = dyn_cast<ReturnStmt>(*Body->body_begin()))
- if (auto *Return = RS->getRetValue())
- return dyn_cast<MemberExpr>(Return->IgnoreParenImpCasts());
- return nullptr;
-}
-
-static void
-getFieldsGlobalsAndFuncs(const Decl &D, FieldSet &Fields,
- llvm::DenseSet<const VarDecl *> &Vars,
- llvm::DenseSet<const FunctionDecl *> &Funcs) {
- insertIfGlobal(D, Vars);
- insertIfFunction(D, Funcs);
- if (const auto *Decomp = dyn_cast<DecompositionDecl>(&D))
- for (const auto *B : Decomp->bindings())
- if (auto *ME = dyn_cast_or_null<MemberExpr>(B->getBinding()))
- // FIXME: should we be using `E->getFoundDecl()`?
- if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()))
- Fields.insert(FD);
-}
-
-/// Traverses `S` and inserts into `Fields`, `Vars` and `Funcs` any fields,
-/// global variables and functions that are declared in or referenced from
-/// sub-statements.
-static void
-getFieldsGlobalsAndFuncs(const Stmt &S, FieldSet &Fields,
- llvm::DenseSet<const VarDecl *> &Vars,
- llvm::DenseSet<const FunctionDecl *> &Funcs) {
- for (auto *Child : S.children())
- if (Child != nullptr)
- getFieldsGlobalsAndFuncs(*Child, Fields, Vars, Funcs);
- if (const auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(&S))
- getFieldsGlobalsAndFuncs(*DefaultArg->getExpr(), Fields, Vars, Funcs);
- if (const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(&S))
- getFieldsGlobalsAndFuncs(*DefaultInit->getExpr(), Fields, Vars, Funcs);
-
- if (auto *DS = dyn_cast<DeclStmt>(&S)) {
- if (DS->isSingleDecl())
- getFieldsGlobalsAndFuncs(*DS->getSingleDecl(), Fields, Vars, Funcs);
- else
- for (auto *D : DS->getDeclGroup())
- getFieldsGlobalsAndFuncs(*D, Fields, Vars, Funcs);
- } else if (auto *E = dyn_cast<DeclRefExpr>(&S)) {
- insertIfGlobal(*E->getDecl(), Vars);
- insertIfFunction(*E->getDecl(), Funcs);
- } else if (const auto *C = dyn_cast<CXXMemberCallExpr>(&S)) {
- // If this is a method that returns a member variable but does nothing else,
- // model the field of the return value.
- if (MemberExpr *E = getMemberForAccessor(*C))
- if (const auto *FD = dyn_cast<FieldDecl>(E->getMemberDecl()))
- Fields.insert(FD);
- } else if (auto *E = dyn_cast<MemberExpr>(&S)) {
- // FIXME: should we be using `E->getFoundDecl()`?
- const ValueDecl *VD = E->getMemberDecl();
- insertIfGlobal(*VD, Vars);
- insertIfFunction(*VD, Funcs);
- if (const auto *FD = dyn_cast<FieldDecl>(VD))
- Fields.insert(FD);
- } else if (auto *InitList = dyn_cast<InitListExpr>(&S)) {
- if (InitList->getType()->isRecordType())
- for (const auto *FD : getFieldsForInitListExpr(InitList))
- Fields.insert(FD);
- }
-}
-
namespace {
// Visitor that builds a map from record prvalues to result objects.
@@ -648,36 +561,13 @@ void Environment::initialize() {
void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) {
assert(FuncDecl->doesThisDeclarationHaveABody());
- FieldSet Fields;
- llvm::DenseSet<const VarDecl *> Vars;
- llvm::DenseSet<const FunctionDecl *> Funcs;
-
- // Look for global variable and field references in the
- // constructor-initializers.
- if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FuncDecl)) {
- for (const auto *Init : CtorDecl->inits()) {
- if (Init->isMemberInitializer()) {
- Fields.insert(Init->getMember());
- } else if (Init->isIndirectMemberInitializer()) {
- for (const auto *I : Init->getIndirectMember()->chain())
- Fields.insert(cast<FieldDecl>(I));
- }
- const Expr *E = Init->getInit();
- assert(E != nullptr);
- getFieldsGlobalsAndFuncs(*E, Fields, Vars, Funcs);
- }
- // Add all fields mentioned in default member initializers.
- for (const FieldDecl *F : CtorDecl->getParent()->fields())
- if (const auto *I = F->getInClassInitializer())
- getFieldsGlobalsAndFuncs(*I, Fields, Vars, Funcs);
- }
- getFieldsGlobalsAndFuncs(*FuncDecl->getBody(), Fields, Vars, Funcs);
+ FieldsGlobalsAndFuncs FGF = getFieldsGlobalsAndFuncs(*FuncDecl);
// These have to be added before the lines that follow to ensure that
// `create*` work correctly for structs.
- DACtx->addModeledFields(Fields);
+ DACtx->addModeledFields(FGF.Fields);
- for (const VarDecl *D : Vars) {
+ for (const VarDecl *D : FGF.Globals) {
if (getStorageLocation(*D) != nullptr)
continue;
@@ -689,7 +579,7 @@ void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) {
setStorageLocation(*D, createObject(*D, nullptr));
}
- for (const FunctionDecl *FD : Funcs) {
+ for (const FunctionDecl *FD : FGF.Funcs) {
if (getStorageLocation(*FD) != nullptr)
continue;
auto &Loc = createStorageLocation(*FD);
@@ -1349,64 +1239,6 @@ RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
return Env.get<RecordStorageLocation>(*Base);
}
-std::vector<const FieldDecl *>
-getFieldsForInitListExpr(const InitListExpr *InitList) {
- const RecordDecl *RD = InitList->getType()->getAsRecordDecl();
- assert(RD != nullptr);
-
- std::vector<const FieldDecl *> Fields;
-
- if (InitList->getType()->isUnionType()) {
- Fields.push_back(InitList->getInitializedFieldInUnion());
- return Fields;
- }
-
- // Unnamed bitfields are only used for padding and do not appear in
- // `InitListExpr`'s inits. However, those fields do appear in `RecordDecl`'s
- // field list, and we thus need to remove them before mapping inits to
- // fields to avoid mapping inits to the wrongs fields.
- llvm::copy_if(
- RD->fields(), std::back_inserter(Fields),
- [](const FieldDecl *Field) { return !Field->isUnnamedBitfield(); });
- return Fields;
-}
-
-RecordInitListHelper::RecordInitListHelper(const InitListExpr *InitList) {
- auto *RD = InitList->getType()->getAsCXXRecordDecl();
- assert(RD != nullptr);
-
- std::vector<const FieldDecl *> Fields = getFieldsForInitListExpr(InitList);
- ArrayRef<Expr *> Inits = InitList->inits();
-
- // Unions initialized with an empty initializer list need special treatment.
- // For structs/classes initialized with an empty initializer list, Clang
- // puts `ImplicitValueInitExpr`s in `InitListExpr::inits()`, but for unions,
- // it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves.
- SmallVector<Expr *> InitsForUnion;
- if (InitList->getType()->isUnionType() && Inits.empty()) {
- assert(Fields.size() == 1);
- ImplicitValueInitForUnion.emplace(Fields.front()->getType());
- Init...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/88534
More information about the cfe-commits
mailing list