181ad6265SDimitry Andric //===-- DataflowAnalysisContext.cpp -----------------------------*- C++ -*-===// 281ad6265SDimitry Andric // 381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 681ad6265SDimitry Andric // 781ad6265SDimitry Andric //===----------------------------------------------------------------------===// 881ad6265SDimitry Andric // 981ad6265SDimitry Andric // This file defines a DataflowAnalysisContext class that owns objects that 1081ad6265SDimitry Andric // encompass the state of a program and stores context that is used during 1181ad6265SDimitry Andric // dataflow analysis. 1281ad6265SDimitry Andric // 1381ad6265SDimitry Andric //===----------------------------------------------------------------------===// 1481ad6265SDimitry Andric 1581ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" 1681ad6265SDimitry Andric #include "clang/AST/ExprCXX.h" 1781ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/Value.h" 1881ad6265SDimitry Andric #include <cassert> 1981ad6265SDimitry Andric #include <memory> 2081ad6265SDimitry Andric #include <utility> 2181ad6265SDimitry Andric 2281ad6265SDimitry Andric namespace clang { 2381ad6265SDimitry Andric namespace dataflow { 2481ad6265SDimitry Andric 2581ad6265SDimitry Andric StorageLocation & 2681ad6265SDimitry Andric DataflowAnalysisContext::getStableStorageLocation(QualType Type) { 27*753f127fSDimitry Andric if (!Type.isNull() && 28*753f127fSDimitry Andric (Type->isStructureOrClassType() || Type->isUnionType())) { 2981ad6265SDimitry Andric // FIXME: Explore options to avoid eager initialization of fields as some of 3081ad6265SDimitry Andric // them might not be needed for a particular analysis. 3181ad6265SDimitry Andric llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs; 3281ad6265SDimitry Andric for (const FieldDecl *Field : getObjectFields(Type)) 3381ad6265SDimitry Andric FieldLocs.insert({Field, &getStableStorageLocation(Field->getType())}); 3481ad6265SDimitry Andric return takeOwnership( 3581ad6265SDimitry Andric std::make_unique<AggregateStorageLocation>(Type, std::move(FieldLocs))); 3681ad6265SDimitry Andric } 3781ad6265SDimitry Andric return takeOwnership(std::make_unique<ScalarStorageLocation>(Type)); 3881ad6265SDimitry Andric } 3981ad6265SDimitry Andric 4081ad6265SDimitry Andric StorageLocation & 4181ad6265SDimitry Andric DataflowAnalysisContext::getStableStorageLocation(const VarDecl &D) { 4281ad6265SDimitry Andric if (auto *Loc = getStorageLocation(D)) 4381ad6265SDimitry Andric return *Loc; 4481ad6265SDimitry Andric auto &Loc = getStableStorageLocation(D.getType()); 4581ad6265SDimitry Andric setStorageLocation(D, Loc); 4681ad6265SDimitry Andric return Loc; 4781ad6265SDimitry Andric } 4881ad6265SDimitry Andric 4981ad6265SDimitry Andric StorageLocation & 5081ad6265SDimitry Andric DataflowAnalysisContext::getStableStorageLocation(const Expr &E) { 5181ad6265SDimitry Andric if (auto *Loc = getStorageLocation(E)) 5281ad6265SDimitry Andric return *Loc; 5381ad6265SDimitry Andric auto &Loc = getStableStorageLocation(E.getType()); 5481ad6265SDimitry Andric setStorageLocation(E, Loc); 5581ad6265SDimitry Andric return Loc; 5681ad6265SDimitry Andric } 5781ad6265SDimitry Andric 5881ad6265SDimitry Andric PointerValue & 5981ad6265SDimitry Andric DataflowAnalysisContext::getOrCreateNullPointerValue(QualType PointeeType) { 60*753f127fSDimitry Andric auto CanonicalPointeeType = 61*753f127fSDimitry Andric PointeeType.isNull() ? PointeeType : PointeeType.getCanonicalType(); 6281ad6265SDimitry Andric auto Res = NullPointerVals.try_emplace(CanonicalPointeeType, nullptr); 6381ad6265SDimitry Andric if (Res.second) { 6481ad6265SDimitry Andric auto &PointeeLoc = getStableStorageLocation(CanonicalPointeeType); 6581ad6265SDimitry Andric Res.first->second = 6681ad6265SDimitry Andric &takeOwnership(std::make_unique<PointerValue>(PointeeLoc)); 6781ad6265SDimitry Andric } 6881ad6265SDimitry Andric return *Res.first->second; 6981ad6265SDimitry Andric } 7081ad6265SDimitry Andric 7181ad6265SDimitry Andric static std::pair<BoolValue *, BoolValue *> 7281ad6265SDimitry Andric makeCanonicalBoolValuePair(BoolValue &LHS, BoolValue &RHS) { 7381ad6265SDimitry Andric auto Res = std::make_pair(&LHS, &RHS); 7481ad6265SDimitry Andric if (&RHS < &LHS) 7581ad6265SDimitry Andric std::swap(Res.first, Res.second); 7681ad6265SDimitry Andric return Res; 7781ad6265SDimitry Andric } 7881ad6265SDimitry Andric 7981ad6265SDimitry Andric BoolValue &DataflowAnalysisContext::getOrCreateConjunction(BoolValue &LHS, 8081ad6265SDimitry Andric BoolValue &RHS) { 8181ad6265SDimitry Andric if (&LHS == &RHS) 8281ad6265SDimitry Andric return LHS; 8381ad6265SDimitry Andric 8481ad6265SDimitry Andric auto Res = ConjunctionVals.try_emplace(makeCanonicalBoolValuePair(LHS, RHS), 8581ad6265SDimitry Andric nullptr); 8681ad6265SDimitry Andric if (Res.second) 8781ad6265SDimitry Andric Res.first->second = 8881ad6265SDimitry Andric &takeOwnership(std::make_unique<ConjunctionValue>(LHS, RHS)); 8981ad6265SDimitry Andric return *Res.first->second; 9081ad6265SDimitry Andric } 9181ad6265SDimitry Andric 9281ad6265SDimitry Andric BoolValue &DataflowAnalysisContext::getOrCreateDisjunction(BoolValue &LHS, 9381ad6265SDimitry Andric BoolValue &RHS) { 9481ad6265SDimitry Andric if (&LHS == &RHS) 9581ad6265SDimitry Andric return LHS; 9681ad6265SDimitry Andric 9781ad6265SDimitry Andric auto Res = DisjunctionVals.try_emplace(makeCanonicalBoolValuePair(LHS, RHS), 9881ad6265SDimitry Andric nullptr); 9981ad6265SDimitry Andric if (Res.second) 10081ad6265SDimitry Andric Res.first->second = 10181ad6265SDimitry Andric &takeOwnership(std::make_unique<DisjunctionValue>(LHS, RHS)); 10281ad6265SDimitry Andric return *Res.first->second; 10381ad6265SDimitry Andric } 10481ad6265SDimitry Andric 10581ad6265SDimitry Andric BoolValue &DataflowAnalysisContext::getOrCreateNegation(BoolValue &Val) { 10681ad6265SDimitry Andric auto Res = NegationVals.try_emplace(&Val, nullptr); 10781ad6265SDimitry Andric if (Res.second) 10881ad6265SDimitry Andric Res.first->second = &takeOwnership(std::make_unique<NegationValue>(Val)); 10981ad6265SDimitry Andric return *Res.first->second; 11081ad6265SDimitry Andric } 11181ad6265SDimitry Andric 11281ad6265SDimitry Andric BoolValue &DataflowAnalysisContext::getOrCreateImplication(BoolValue &LHS, 11381ad6265SDimitry Andric BoolValue &RHS) { 11481ad6265SDimitry Andric return &LHS == &RHS ? getBoolLiteralValue(true) 11581ad6265SDimitry Andric : getOrCreateDisjunction(getOrCreateNegation(LHS), RHS); 11681ad6265SDimitry Andric } 11781ad6265SDimitry Andric 11881ad6265SDimitry Andric BoolValue &DataflowAnalysisContext::getOrCreateIff(BoolValue &LHS, 11981ad6265SDimitry Andric BoolValue &RHS) { 12081ad6265SDimitry Andric return &LHS == &RHS 12181ad6265SDimitry Andric ? getBoolLiteralValue(true) 12281ad6265SDimitry Andric : getOrCreateConjunction(getOrCreateImplication(LHS, RHS), 12381ad6265SDimitry Andric getOrCreateImplication(RHS, LHS)); 12481ad6265SDimitry Andric } 12581ad6265SDimitry Andric 12681ad6265SDimitry Andric AtomicBoolValue &DataflowAnalysisContext::makeFlowConditionToken() { 12781ad6265SDimitry Andric return createAtomicBoolValue(); 12881ad6265SDimitry Andric } 12981ad6265SDimitry Andric 13081ad6265SDimitry Andric void DataflowAnalysisContext::addFlowConditionConstraint( 13181ad6265SDimitry Andric AtomicBoolValue &Token, BoolValue &Constraint) { 13281ad6265SDimitry Andric auto Res = FlowConditionConstraints.try_emplace(&Token, &Constraint); 13381ad6265SDimitry Andric if (!Res.second) { 13481ad6265SDimitry Andric Res.first->second = &getOrCreateConjunction(*Res.first->second, Constraint); 13581ad6265SDimitry Andric } 13681ad6265SDimitry Andric } 13781ad6265SDimitry Andric 13881ad6265SDimitry Andric AtomicBoolValue & 13981ad6265SDimitry Andric DataflowAnalysisContext::forkFlowCondition(AtomicBoolValue &Token) { 14081ad6265SDimitry Andric auto &ForkToken = makeFlowConditionToken(); 14181ad6265SDimitry Andric FlowConditionDeps[&ForkToken].insert(&Token); 14281ad6265SDimitry Andric addFlowConditionConstraint(ForkToken, Token); 14381ad6265SDimitry Andric return ForkToken; 14481ad6265SDimitry Andric } 14581ad6265SDimitry Andric 14681ad6265SDimitry Andric AtomicBoolValue & 14781ad6265SDimitry Andric DataflowAnalysisContext::joinFlowConditions(AtomicBoolValue &FirstToken, 14881ad6265SDimitry Andric AtomicBoolValue &SecondToken) { 14981ad6265SDimitry Andric auto &Token = makeFlowConditionToken(); 15081ad6265SDimitry Andric FlowConditionDeps[&Token].insert(&FirstToken); 15181ad6265SDimitry Andric FlowConditionDeps[&Token].insert(&SecondToken); 15281ad6265SDimitry Andric addFlowConditionConstraint(Token, 15381ad6265SDimitry Andric getOrCreateDisjunction(FirstToken, SecondToken)); 15481ad6265SDimitry Andric return Token; 15581ad6265SDimitry Andric } 15681ad6265SDimitry Andric 15781ad6265SDimitry Andric Solver::Result 15881ad6265SDimitry Andric DataflowAnalysisContext::querySolver(llvm::DenseSet<BoolValue *> Constraints) { 15981ad6265SDimitry Andric Constraints.insert(&getBoolLiteralValue(true)); 16081ad6265SDimitry Andric Constraints.insert(&getOrCreateNegation(getBoolLiteralValue(false))); 16181ad6265SDimitry Andric return S->solve(std::move(Constraints)); 16281ad6265SDimitry Andric } 16381ad6265SDimitry Andric 16481ad6265SDimitry Andric bool DataflowAnalysisContext::flowConditionImplies(AtomicBoolValue &Token, 16581ad6265SDimitry Andric BoolValue &Val) { 16681ad6265SDimitry Andric // Returns true if and only if truth assignment of the flow condition implies 16781ad6265SDimitry Andric // that `Val` is also true. We prove whether or not this property holds by 16881ad6265SDimitry Andric // reducing the problem to satisfiability checking. In other words, we attempt 16981ad6265SDimitry Andric // to show that assuming `Val` is false makes the constraints induced by the 17081ad6265SDimitry Andric // flow condition unsatisfiable. 17181ad6265SDimitry Andric llvm::DenseSet<BoolValue *> Constraints = {&Token, &getOrCreateNegation(Val)}; 17281ad6265SDimitry Andric llvm::DenseSet<AtomicBoolValue *> VisitedTokens; 17381ad6265SDimitry Andric addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens); 17481ad6265SDimitry Andric return isUnsatisfiable(std::move(Constraints)); 17581ad6265SDimitry Andric } 17681ad6265SDimitry Andric 17781ad6265SDimitry Andric bool DataflowAnalysisContext::flowConditionIsTautology(AtomicBoolValue &Token) { 17881ad6265SDimitry Andric // Returns true if and only if we cannot prove that the flow condition can 17981ad6265SDimitry Andric // ever be false. 18081ad6265SDimitry Andric llvm::DenseSet<BoolValue *> Constraints = {&getOrCreateNegation(Token)}; 18181ad6265SDimitry Andric llvm::DenseSet<AtomicBoolValue *> VisitedTokens; 18281ad6265SDimitry Andric addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens); 18381ad6265SDimitry Andric return isUnsatisfiable(std::move(Constraints)); 18481ad6265SDimitry Andric } 18581ad6265SDimitry Andric 18681ad6265SDimitry Andric bool DataflowAnalysisContext::equivalentBoolValues(BoolValue &Val1, 18781ad6265SDimitry Andric BoolValue &Val2) { 18881ad6265SDimitry Andric llvm::DenseSet<BoolValue *> Constraints = { 18981ad6265SDimitry Andric &getOrCreateNegation(getOrCreateIff(Val1, Val2))}; 19081ad6265SDimitry Andric return isUnsatisfiable(Constraints); 19181ad6265SDimitry Andric } 19281ad6265SDimitry Andric 19381ad6265SDimitry Andric void DataflowAnalysisContext::addTransitiveFlowConditionConstraints( 19481ad6265SDimitry Andric AtomicBoolValue &Token, llvm::DenseSet<BoolValue *> &Constraints, 19581ad6265SDimitry Andric llvm::DenseSet<AtomicBoolValue *> &VisitedTokens) { 19681ad6265SDimitry Andric auto Res = VisitedTokens.insert(&Token); 19781ad6265SDimitry Andric if (!Res.second) 19881ad6265SDimitry Andric return; 19981ad6265SDimitry Andric 20081ad6265SDimitry Andric auto ConstraintsIT = FlowConditionConstraints.find(&Token); 20181ad6265SDimitry Andric if (ConstraintsIT == FlowConditionConstraints.end()) { 20281ad6265SDimitry Andric Constraints.insert(&Token); 20381ad6265SDimitry Andric } else { 20481ad6265SDimitry Andric // Bind flow condition token via `iff` to its set of constraints: 20581ad6265SDimitry Andric // FC <=> (C1 ^ C2 ^ ...), where Ci are constraints 20681ad6265SDimitry Andric Constraints.insert(&getOrCreateIff(Token, *ConstraintsIT->second)); 20781ad6265SDimitry Andric } 20881ad6265SDimitry Andric 20981ad6265SDimitry Andric auto DepsIT = FlowConditionDeps.find(&Token); 21081ad6265SDimitry Andric if (DepsIT != FlowConditionDeps.end()) { 21181ad6265SDimitry Andric for (AtomicBoolValue *DepToken : DepsIT->second) { 21281ad6265SDimitry Andric addTransitiveFlowConditionConstraints(*DepToken, Constraints, 21381ad6265SDimitry Andric VisitedTokens); 21481ad6265SDimitry Andric } 21581ad6265SDimitry Andric } 21681ad6265SDimitry Andric } 21781ad6265SDimitry Andric 21881ad6265SDimitry Andric BoolValue &DataflowAnalysisContext::substituteBoolValue( 21981ad6265SDimitry Andric BoolValue &Val, 22081ad6265SDimitry Andric llvm::DenseMap<BoolValue *, BoolValue *> &SubstitutionsCache) { 22181ad6265SDimitry Andric auto IT = SubstitutionsCache.find(&Val); 22281ad6265SDimitry Andric if (IT != SubstitutionsCache.end()) { 22381ad6265SDimitry Andric // Return memoized result of substituting this boolean value. 22481ad6265SDimitry Andric return *IT->second; 22581ad6265SDimitry Andric } 22681ad6265SDimitry Andric 22781ad6265SDimitry Andric // Handle substitution on the boolean value (and its subvalues), saving the 22881ad6265SDimitry Andric // result into `SubstitutionsCache`. 22981ad6265SDimitry Andric BoolValue *Result; 23081ad6265SDimitry Andric switch (Val.getKind()) { 23181ad6265SDimitry Andric case Value::Kind::AtomicBool: { 23281ad6265SDimitry Andric Result = &Val; 23381ad6265SDimitry Andric break; 23481ad6265SDimitry Andric } 23581ad6265SDimitry Andric case Value::Kind::Negation: { 23681ad6265SDimitry Andric auto &Negation = *cast<NegationValue>(&Val); 23781ad6265SDimitry Andric auto &Sub = substituteBoolValue(Negation.getSubVal(), SubstitutionsCache); 23881ad6265SDimitry Andric Result = &getOrCreateNegation(Sub); 23981ad6265SDimitry Andric break; 24081ad6265SDimitry Andric } 24181ad6265SDimitry Andric case Value::Kind::Disjunction: { 24281ad6265SDimitry Andric auto &Disjunct = *cast<DisjunctionValue>(&Val); 24381ad6265SDimitry Andric auto &LeftSub = 24481ad6265SDimitry Andric substituteBoolValue(Disjunct.getLeftSubValue(), SubstitutionsCache); 24581ad6265SDimitry Andric auto &RightSub = 24681ad6265SDimitry Andric substituteBoolValue(Disjunct.getRightSubValue(), SubstitutionsCache); 24781ad6265SDimitry Andric Result = &getOrCreateDisjunction(LeftSub, RightSub); 24881ad6265SDimitry Andric break; 24981ad6265SDimitry Andric } 25081ad6265SDimitry Andric case Value::Kind::Conjunction: { 25181ad6265SDimitry Andric auto &Conjunct = *cast<ConjunctionValue>(&Val); 25281ad6265SDimitry Andric auto &LeftSub = 25381ad6265SDimitry Andric substituteBoolValue(Conjunct.getLeftSubValue(), SubstitutionsCache); 25481ad6265SDimitry Andric auto &RightSub = 25581ad6265SDimitry Andric substituteBoolValue(Conjunct.getRightSubValue(), SubstitutionsCache); 25681ad6265SDimitry Andric Result = &getOrCreateConjunction(LeftSub, RightSub); 25781ad6265SDimitry Andric break; 25881ad6265SDimitry Andric } 25981ad6265SDimitry Andric default: 26081ad6265SDimitry Andric llvm_unreachable("Unhandled Value Kind"); 26181ad6265SDimitry Andric } 26281ad6265SDimitry Andric SubstitutionsCache[&Val] = Result; 26381ad6265SDimitry Andric return *Result; 26481ad6265SDimitry Andric } 26581ad6265SDimitry Andric 26681ad6265SDimitry Andric BoolValue &DataflowAnalysisContext::buildAndSubstituteFlowCondition( 26781ad6265SDimitry Andric AtomicBoolValue &Token, 26881ad6265SDimitry Andric llvm::DenseMap<AtomicBoolValue *, BoolValue *> Substitutions) { 26981ad6265SDimitry Andric assert( 27081ad6265SDimitry Andric Substitutions.find(&getBoolLiteralValue(true)) == Substitutions.end() && 27181ad6265SDimitry Andric Substitutions.find(&getBoolLiteralValue(false)) == Substitutions.end() && 27281ad6265SDimitry Andric "Do not substitute true/false boolean literals"); 27381ad6265SDimitry Andric llvm::DenseMap<BoolValue *, BoolValue *> SubstitutionsCache( 27481ad6265SDimitry Andric Substitutions.begin(), Substitutions.end()); 27581ad6265SDimitry Andric return buildAndSubstituteFlowConditionWithCache(Token, SubstitutionsCache); 27681ad6265SDimitry Andric } 27781ad6265SDimitry Andric 27881ad6265SDimitry Andric BoolValue &DataflowAnalysisContext::buildAndSubstituteFlowConditionWithCache( 27981ad6265SDimitry Andric AtomicBoolValue &Token, 28081ad6265SDimitry Andric llvm::DenseMap<BoolValue *, BoolValue *> &SubstitutionsCache) { 28181ad6265SDimitry Andric auto ConstraintsIT = FlowConditionConstraints.find(&Token); 28281ad6265SDimitry Andric if (ConstraintsIT == FlowConditionConstraints.end()) { 28381ad6265SDimitry Andric return getBoolLiteralValue(true); 28481ad6265SDimitry Andric } 28581ad6265SDimitry Andric auto DepsIT = FlowConditionDeps.find(&Token); 28681ad6265SDimitry Andric if (DepsIT != FlowConditionDeps.end()) { 28781ad6265SDimitry Andric for (AtomicBoolValue *DepToken : DepsIT->second) { 28881ad6265SDimitry Andric auto &NewDep = buildAndSubstituteFlowConditionWithCache( 28981ad6265SDimitry Andric *DepToken, SubstitutionsCache); 29081ad6265SDimitry Andric SubstitutionsCache[DepToken] = &NewDep; 29181ad6265SDimitry Andric } 29281ad6265SDimitry Andric } 29381ad6265SDimitry Andric return substituteBoolValue(*ConstraintsIT->second, SubstitutionsCache); 29481ad6265SDimitry Andric } 29581ad6265SDimitry Andric 29681ad6265SDimitry Andric } // namespace dataflow 29781ad6265SDimitry Andric } // namespace clang 29881ad6265SDimitry Andric 29981ad6265SDimitry Andric using namespace clang; 30081ad6265SDimitry Andric 30181ad6265SDimitry Andric const Expr &clang::dataflow::ignoreCFGOmittedNodes(const Expr &E) { 30281ad6265SDimitry Andric const Expr *Current = &E; 30381ad6265SDimitry Andric if (auto *EWC = dyn_cast<ExprWithCleanups>(Current)) { 30481ad6265SDimitry Andric Current = EWC->getSubExpr(); 30581ad6265SDimitry Andric assert(Current != nullptr); 30681ad6265SDimitry Andric } 30781ad6265SDimitry Andric Current = Current->IgnoreParens(); 30881ad6265SDimitry Andric assert(Current != nullptr); 30981ad6265SDimitry Andric return *Current; 31081ad6265SDimitry Andric } 31181ad6265SDimitry Andric 31281ad6265SDimitry Andric const Stmt &clang::dataflow::ignoreCFGOmittedNodes(const Stmt &S) { 31381ad6265SDimitry Andric if (auto *E = dyn_cast<Expr>(&S)) 31481ad6265SDimitry Andric return ignoreCFGOmittedNodes(*E); 31581ad6265SDimitry Andric return S; 31681ad6265SDimitry Andric } 31781ad6265SDimitry Andric 31881ad6265SDimitry Andric // FIXME: Does not precisely handle non-virtual diamond inheritance. A single 31981ad6265SDimitry Andric // field decl will be modeled for all instances of the inherited field. 32081ad6265SDimitry Andric static void 32181ad6265SDimitry Andric getFieldsFromClassHierarchy(QualType Type, 32281ad6265SDimitry Andric llvm::DenseSet<const FieldDecl *> &Fields) { 32381ad6265SDimitry Andric if (Type->isIncompleteType() || Type->isDependentType() || 32481ad6265SDimitry Andric !Type->isRecordType()) 32581ad6265SDimitry Andric return; 32681ad6265SDimitry Andric 32781ad6265SDimitry Andric for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) 32881ad6265SDimitry Andric Fields.insert(Field); 32981ad6265SDimitry Andric if (auto *CXXRecord = Type->getAsCXXRecordDecl()) 33081ad6265SDimitry Andric for (const CXXBaseSpecifier &Base : CXXRecord->bases()) 33181ad6265SDimitry Andric getFieldsFromClassHierarchy(Base.getType(), Fields); 33281ad6265SDimitry Andric } 33381ad6265SDimitry Andric 33481ad6265SDimitry Andric /// Gets the set of all fields in the type. 33581ad6265SDimitry Andric llvm::DenseSet<const FieldDecl *> 33681ad6265SDimitry Andric clang::dataflow::getObjectFields(QualType Type) { 33781ad6265SDimitry Andric llvm::DenseSet<const FieldDecl *> Fields; 33881ad6265SDimitry Andric getFieldsFromClassHierarchy(Type, Fields); 33981ad6265SDimitry Andric return Fields; 34081ad6265SDimitry Andric } 341