Path: blob/main/contrib/llvm-project/clang/lib/AST/CXXInheritance.cpp
35260 views
//===- CXXInheritance.cpp - C++ Inheritance -------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file provides routines that help analyzing C++ inheritance hierarchies.9//10//===----------------------------------------------------------------------===//1112#include "clang/AST/CXXInheritance.h"13#include "clang/AST/ASTContext.h"14#include "clang/AST/Decl.h"15#include "clang/AST/DeclBase.h"16#include "clang/AST/DeclCXX.h"17#include "clang/AST/DeclTemplate.h"18#include "clang/AST/RecordLayout.h"19#include "clang/AST/TemplateName.h"20#include "clang/AST/Type.h"21#include "clang/Basic/LLVM.h"22#include "llvm/ADT/DenseMap.h"23#include "llvm/ADT/STLExtras.h"24#include "llvm/ADT/SmallVector.h"25#include "llvm/ADT/iterator_range.h"26#include "llvm/Support/Casting.h"27#include <algorithm>28#include <utility>29#include <cassert>30#include <vector>3132using namespace clang;3334/// isAmbiguous - Determines whether the set of paths provided is35/// ambiguous, i.e., there are two or more paths that refer to36/// different base class subobjects of the same type. BaseType must be37/// an unqualified, canonical class type.38bool CXXBasePaths::isAmbiguous(CanQualType BaseType) {39BaseType = BaseType.getUnqualifiedType();40IsVirtBaseAndNumberNonVirtBases Subobjects = ClassSubobjects[BaseType];41return Subobjects.NumberOfNonVirtBases + (Subobjects.IsVirtBase ? 1 : 0) > 1;42}4344/// clear - Clear out all prior path information.45void CXXBasePaths::clear() {46Paths.clear();47ClassSubobjects.clear();48VisitedDependentRecords.clear();49ScratchPath.clear();50DetectedVirtual = nullptr;51}5253/// Swaps the contents of this CXXBasePaths structure with the54/// contents of Other.55void CXXBasePaths::swap(CXXBasePaths &Other) {56std::swap(Origin, Other.Origin);57Paths.swap(Other.Paths);58ClassSubobjects.swap(Other.ClassSubobjects);59VisitedDependentRecords.swap(Other.VisitedDependentRecords);60std::swap(FindAmbiguities, Other.FindAmbiguities);61std::swap(RecordPaths, Other.RecordPaths);62std::swap(DetectVirtual, Other.DetectVirtual);63std::swap(DetectedVirtual, Other.DetectedVirtual);64}6566bool CXXRecordDecl::isDerivedFrom(const CXXRecordDecl *Base) const {67CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/false,68/*DetectVirtual=*/false);69return isDerivedFrom(Base, Paths);70}7172bool CXXRecordDecl::isDerivedFrom(const CXXRecordDecl *Base,73CXXBasePaths &Paths) const {74if (getCanonicalDecl() == Base->getCanonicalDecl())75return false;7677Paths.setOrigin(const_cast<CXXRecordDecl*>(this));7879const CXXRecordDecl *BaseDecl = Base->getCanonicalDecl();80return lookupInBases(81[BaseDecl](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) {82return Specifier->getType()->getAsRecordDecl() &&83FindBaseClass(Specifier, Path, BaseDecl);84},85Paths);86}8788bool CXXRecordDecl::isVirtuallyDerivedFrom(const CXXRecordDecl *Base) const {89if (!getNumVBases())90return false;9192CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/false,93/*DetectVirtual=*/false);9495if (getCanonicalDecl() == Base->getCanonicalDecl())96return false;9798Paths.setOrigin(const_cast<CXXRecordDecl*>(this));99100const CXXRecordDecl *BaseDecl = Base->getCanonicalDecl();101return lookupInBases(102[BaseDecl](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) {103return FindVirtualBaseClass(Specifier, Path, BaseDecl);104},105Paths);106}107108bool CXXRecordDecl::isProvablyNotDerivedFrom(const CXXRecordDecl *Base) const {109const CXXRecordDecl *TargetDecl = Base->getCanonicalDecl();110return forallBases([TargetDecl](const CXXRecordDecl *Base) {111return Base->getCanonicalDecl() != TargetDecl;112});113}114115bool116CXXRecordDecl::isCurrentInstantiation(const DeclContext *CurContext) const {117assert(isDependentContext());118119for (; !CurContext->isFileContext(); CurContext = CurContext->getParent())120if (CurContext->Equals(this))121return true;122123return false;124}125126bool CXXRecordDecl::forallBases(ForallBasesCallback BaseMatches) const {127SmallVector<const CXXRecordDecl*, 8> Queue;128129const CXXRecordDecl *Record = this;130while (true) {131for (const auto &I : Record->bases()) {132const RecordType *Ty = I.getType()->getAs<RecordType>();133if (!Ty)134return false;135136CXXRecordDecl *Base =137cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition());138if (!Base ||139(Base->isDependentContext() &&140!Base->isCurrentInstantiation(Record))) {141return false;142}143144Queue.push_back(Base);145if (!BaseMatches(Base))146return false;147}148149if (Queue.empty())150break;151Record = Queue.pop_back_val(); // not actually a queue.152}153154return true;155}156157bool CXXBasePaths::lookupInBases(ASTContext &Context,158const CXXRecordDecl *Record,159CXXRecordDecl::BaseMatchesCallback BaseMatches,160bool LookupInDependent) {161bool FoundPath = false;162163// The access of the path down to this record.164AccessSpecifier AccessToHere = ScratchPath.Access;165bool IsFirstStep = ScratchPath.empty();166167for (const auto &BaseSpec : Record->bases()) {168// Find the record of the base class subobjects for this type.169QualType BaseType =170Context.getCanonicalType(BaseSpec.getType()).getUnqualifiedType();171172// C++ [temp.dep]p3:173// In the definition of a class template or a member of a class template,174// if a base class of the class template depends on a template-parameter,175// the base class scope is not examined during unqualified name lookup176// either at the point of definition of the class template or member or177// during an instantiation of the class tem- plate or member.178if (!LookupInDependent && BaseType->isDependentType())179continue;180181// Determine whether we need to visit this base class at all,182// updating the count of subobjects appropriately.183IsVirtBaseAndNumberNonVirtBases &Subobjects = ClassSubobjects[BaseType];184bool VisitBase = true;185bool SetVirtual = false;186if (BaseSpec.isVirtual()) {187VisitBase = !Subobjects.IsVirtBase;188Subobjects.IsVirtBase = true;189if (isDetectingVirtual() && DetectedVirtual == nullptr) {190// If this is the first virtual we find, remember it. If it turns out191// there is no base path here, we'll reset it later.192DetectedVirtual = BaseType->getAs<RecordType>();193SetVirtual = true;194}195} else {196++Subobjects.NumberOfNonVirtBases;197}198if (isRecordingPaths()) {199// Add this base specifier to the current path.200CXXBasePathElement Element;201Element.Base = &BaseSpec;202Element.Class = Record;203if (BaseSpec.isVirtual())204Element.SubobjectNumber = 0;205else206Element.SubobjectNumber = Subobjects.NumberOfNonVirtBases;207ScratchPath.push_back(Element);208209// Calculate the "top-down" access to this base class.210// The spec actually describes this bottom-up, but top-down is211// equivalent because the definition works out as follows:212// 1. Write down the access along each step in the inheritance213// chain, followed by the access of the decl itself.214// For example, in215// class A { public: int foo; };216// class B : protected A {};217// class C : public B {};218// class D : private C {};219// we would write:220// private public protected public221// 2. If 'private' appears anywhere except far-left, access is denied.222// 3. Otherwise, overall access is determined by the most restrictive223// access in the sequence.224if (IsFirstStep)225ScratchPath.Access = BaseSpec.getAccessSpecifier();226else227ScratchPath.Access = CXXRecordDecl::MergeAccess(AccessToHere,228BaseSpec.getAccessSpecifier());229}230231// Track whether there's a path involving this specific base.232bool FoundPathThroughBase = false;233234if (BaseMatches(&BaseSpec, ScratchPath)) {235// We've found a path that terminates at this base.236FoundPath = FoundPathThroughBase = true;237if (isRecordingPaths()) {238// We have a path. Make a copy of it before moving on.239Paths.push_back(ScratchPath);240} else if (!isFindingAmbiguities()) {241// We found a path and we don't care about ambiguities;242// return immediately.243return FoundPath;244}245} else if (VisitBase) {246CXXRecordDecl *BaseRecord;247if (LookupInDependent) {248BaseRecord = nullptr;249const TemplateSpecializationType *TST =250BaseSpec.getType()->getAs<TemplateSpecializationType>();251if (!TST) {252if (auto *RT = BaseSpec.getType()->getAs<RecordType>())253BaseRecord = cast<CXXRecordDecl>(RT->getDecl());254} else {255TemplateName TN = TST->getTemplateName();256if (auto *TD =257dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl()))258BaseRecord = TD->getTemplatedDecl();259}260if (BaseRecord) {261if (!BaseRecord->hasDefinition() ||262VisitedDependentRecords.count(BaseRecord)) {263BaseRecord = nullptr;264} else {265VisitedDependentRecords.insert(BaseRecord);266}267}268} else {269BaseRecord = cast<CXXRecordDecl>(270BaseSpec.getType()->castAs<RecordType>()->getDecl());271}272if (BaseRecord &&273lookupInBases(Context, BaseRecord, BaseMatches, LookupInDependent)) {274// C++ [class.member.lookup]p2:275// A member name f in one sub-object B hides a member name f in276// a sub-object A if A is a base class sub-object of B. Any277// declarations that are so hidden are eliminated from278// consideration.279280// There is a path to a base class that meets the criteria. If we're281// not collecting paths or finding ambiguities, we're done.282FoundPath = FoundPathThroughBase = true;283if (!isFindingAmbiguities())284return FoundPath;285}286}287288// Pop this base specifier off the current path (if we're289// collecting paths).290if (isRecordingPaths()) {291ScratchPath.pop_back();292}293294// If we set a virtual earlier, and this isn't a path, forget it again.295if (SetVirtual && !FoundPathThroughBase) {296DetectedVirtual = nullptr;297}298}299300// Reset the scratch path access.301ScratchPath.Access = AccessToHere;302303return FoundPath;304}305306bool CXXRecordDecl::lookupInBases(BaseMatchesCallback BaseMatches,307CXXBasePaths &Paths,308bool LookupInDependent) const {309// If we didn't find anything, report that.310if (!Paths.lookupInBases(getASTContext(), this, BaseMatches,311LookupInDependent))312return false;313314// If we're not recording paths or we won't ever find ambiguities,315// we're done.316if (!Paths.isRecordingPaths() || !Paths.isFindingAmbiguities())317return true;318319// C++ [class.member.lookup]p6:320// When virtual base classes are used, a hidden declaration can be321// reached along a path through the sub-object lattice that does322// not pass through the hiding declaration. This is not an323// ambiguity. The identical use with nonvirtual base classes is an324// ambiguity; in that case there is no unique instance of the name325// that hides all the others.326//327// FIXME: This is an O(N^2) algorithm, but DPG doesn't see an easy328// way to make it any faster.329Paths.Paths.remove_if([&Paths](const CXXBasePath &Path) {330for (const CXXBasePathElement &PE : Path) {331if (!PE.Base->isVirtual())332continue;333334CXXRecordDecl *VBase = nullptr;335if (const RecordType *Record = PE.Base->getType()->getAs<RecordType>())336VBase = cast<CXXRecordDecl>(Record->getDecl());337if (!VBase)338break;339340// The declaration(s) we found along this path were found in a341// subobject of a virtual base. Check whether this virtual342// base is a subobject of any other path; if so, then the343// declaration in this path are hidden by that patch.344for (const CXXBasePath &HidingP : Paths) {345CXXRecordDecl *HidingClass = nullptr;346if (const RecordType *Record =347HidingP.back().Base->getType()->getAs<RecordType>())348HidingClass = cast<CXXRecordDecl>(Record->getDecl());349if (!HidingClass)350break;351352if (HidingClass->isVirtuallyDerivedFrom(VBase))353return true;354}355}356return false;357});358359return true;360}361362bool CXXRecordDecl::FindBaseClass(const CXXBaseSpecifier *Specifier,363CXXBasePath &Path,364const CXXRecordDecl *BaseRecord) {365assert(BaseRecord->getCanonicalDecl() == BaseRecord &&366"User data for FindBaseClass is not canonical!");367return Specifier->getType()->castAs<RecordType>()->getDecl()368->getCanonicalDecl() == BaseRecord;369}370371bool CXXRecordDecl::FindVirtualBaseClass(const CXXBaseSpecifier *Specifier,372CXXBasePath &Path,373const CXXRecordDecl *BaseRecord) {374assert(BaseRecord->getCanonicalDecl() == BaseRecord &&375"User data for FindBaseClass is not canonical!");376return Specifier->isVirtual() &&377Specifier->getType()->castAs<RecordType>()->getDecl()378->getCanonicalDecl() == BaseRecord;379}380381static bool isOrdinaryMember(const NamedDecl *ND) {382return ND->isInIdentifierNamespace(Decl::IDNS_Ordinary | Decl::IDNS_Tag |383Decl::IDNS_Member);384}385386static bool findOrdinaryMember(const CXXRecordDecl *RD, CXXBasePath &Path,387DeclarationName Name) {388Path.Decls = RD->lookup(Name).begin();389for (DeclContext::lookup_iterator I = Path.Decls, E = I.end(); I != E; ++I)390if (isOrdinaryMember(*I))391return true;392393return false;394}395396bool CXXRecordDecl::hasMemberName(DeclarationName Name) const {397CXXBasePath P;398if (findOrdinaryMember(this, P, Name))399return true;400401CXXBasePaths Paths(false, false, false);402return lookupInBases(403[Name](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) {404return findOrdinaryMember(Specifier->getType()->getAsCXXRecordDecl(),405Path, Name);406},407Paths);408}409410static bool411findOrdinaryMemberInDependentClasses(const CXXBaseSpecifier *Specifier,412CXXBasePath &Path, DeclarationName Name) {413const TemplateSpecializationType *TST =414Specifier->getType()->getAs<TemplateSpecializationType>();415if (!TST) {416auto *RT = Specifier->getType()->getAs<RecordType>();417if (!RT)418return false;419return findOrdinaryMember(cast<CXXRecordDecl>(RT->getDecl()), Path, Name);420}421TemplateName TN = TST->getTemplateName();422const auto *TD = dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl());423if (!TD)424return false;425CXXRecordDecl *RD = TD->getTemplatedDecl();426if (!RD)427return false;428return findOrdinaryMember(RD, Path, Name);429}430431std::vector<const NamedDecl *> CXXRecordDecl::lookupDependentName(432DeclarationName Name,433llvm::function_ref<bool(const NamedDecl *ND)> Filter) {434std::vector<const NamedDecl *> Results;435// Lookup in the class.436bool AnyOrdinaryMembers = false;437for (const NamedDecl *ND : lookup(Name)) {438if (isOrdinaryMember(ND))439AnyOrdinaryMembers = true;440if (Filter(ND))441Results.push_back(ND);442}443if (AnyOrdinaryMembers)444return Results;445446// Perform lookup into our base classes.447CXXBasePaths Paths;448Paths.setOrigin(this);449if (!lookupInBases(450[&](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) {451return findOrdinaryMemberInDependentClasses(Specifier, Path, Name);452},453Paths, /*LookupInDependent=*/true))454return Results;455for (DeclContext::lookup_iterator I = Paths.front().Decls, E = I.end();456I != E; ++I) {457if (isOrdinaryMember(*I) && Filter(*I))458Results.push_back(*I);459}460return Results;461}462463void OverridingMethods::add(unsigned OverriddenSubobject,464UniqueVirtualMethod Overriding) {465SmallVectorImpl<UniqueVirtualMethod> &SubobjectOverrides466= Overrides[OverriddenSubobject];467if (!llvm::is_contained(SubobjectOverrides, Overriding))468SubobjectOverrides.push_back(Overriding);469}470471void OverridingMethods::add(const OverridingMethods &Other) {472for (const_iterator I = Other.begin(), IE = Other.end(); I != IE; ++I) {473for (overriding_const_iterator M = I->second.begin(),474MEnd = I->second.end();475M != MEnd;476++M)477add(I->first, *M);478}479}480481void OverridingMethods::replaceAll(UniqueVirtualMethod Overriding) {482for (iterator I = begin(), IEnd = end(); I != IEnd; ++I) {483I->second.clear();484I->second.push_back(Overriding);485}486}487488namespace {489490class FinalOverriderCollector {491/// The number of subobjects of a given class type that492/// occur within the class hierarchy.493llvm::DenseMap<const CXXRecordDecl *, unsigned> SubobjectCount;494495/// Overriders for each virtual base subobject.496llvm::DenseMap<const CXXRecordDecl *, CXXFinalOverriderMap *> VirtualOverriders;497498CXXFinalOverriderMap FinalOverriders;499500public:501~FinalOverriderCollector();502503void Collect(const CXXRecordDecl *RD, bool VirtualBase,504const CXXRecordDecl *InVirtualSubobject,505CXXFinalOverriderMap &Overriders);506};507508} // namespace509510void FinalOverriderCollector::Collect(const CXXRecordDecl *RD,511bool VirtualBase,512const CXXRecordDecl *InVirtualSubobject,513CXXFinalOverriderMap &Overriders) {514unsigned SubobjectNumber = 0;515if (!VirtualBase)516SubobjectNumber517= ++SubobjectCount[cast<CXXRecordDecl>(RD->getCanonicalDecl())];518519for (const auto &Base : RD->bases()) {520if (const RecordType *RT = Base.getType()->getAs<RecordType>()) {521const CXXRecordDecl *BaseDecl = cast<CXXRecordDecl>(RT->getDecl());522if (!BaseDecl->isPolymorphic())523continue;524525if (Overriders.empty() && !Base.isVirtual()) {526// There are no other overriders of virtual member functions,527// so let the base class fill in our overriders for us.528Collect(BaseDecl, false, InVirtualSubobject, Overriders);529continue;530}531532// Collect all of the overridders from the base class subobject533// and merge them into the set of overridders for this class.534// For virtual base classes, populate or use the cached virtual535// overrides so that we do not walk the virtual base class (and536// its base classes) more than once.537CXXFinalOverriderMap ComputedBaseOverriders;538CXXFinalOverriderMap *BaseOverriders = &ComputedBaseOverriders;539if (Base.isVirtual()) {540CXXFinalOverriderMap *&MyVirtualOverriders = VirtualOverriders[BaseDecl];541BaseOverriders = MyVirtualOverriders;542if (!MyVirtualOverriders) {543MyVirtualOverriders = new CXXFinalOverriderMap;544545// Collect may cause VirtualOverriders to reallocate, invalidating the546// MyVirtualOverriders reference. Set BaseOverriders to the right547// value now.548BaseOverriders = MyVirtualOverriders;549550Collect(BaseDecl, true, BaseDecl, *MyVirtualOverriders);551}552} else553Collect(BaseDecl, false, InVirtualSubobject, ComputedBaseOverriders);554555// Merge the overriders from this base class into our own set of556// overriders.557for (CXXFinalOverriderMap::iterator OM = BaseOverriders->begin(),558OMEnd = BaseOverriders->end();559OM != OMEnd;560++OM) {561const CXXMethodDecl *CanonOM = OM->first->getCanonicalDecl();562Overriders[CanonOM].add(OM->second);563}564}565}566567for (auto *M : RD->methods()) {568// We only care about virtual methods.569if (!M->isVirtual())570continue;571572CXXMethodDecl *CanonM = M->getCanonicalDecl();573using OverriddenMethodsRange =574llvm::iterator_range<CXXMethodDecl::method_iterator>;575OverriddenMethodsRange OverriddenMethods = CanonM->overridden_methods();576577if (OverriddenMethods.begin() == OverriddenMethods.end()) {578// This is a new virtual function that does not override any579// other virtual function. Add it to the map of virtual580// functions for which we are tracking overridders.581582// C++ [class.virtual]p2:583// For convenience we say that any virtual function overrides itself.584Overriders[CanonM].add(SubobjectNumber,585UniqueVirtualMethod(CanonM, SubobjectNumber,586InVirtualSubobject));587continue;588}589590// This virtual method overrides other virtual methods, so it does591// not add any new slots into the set of overriders. Instead, we592// replace entries in the set of overriders with the new593// overrider. To do so, we dig down to the original virtual594// functions using data recursion and update all of the methods it595// overrides.596SmallVector<OverriddenMethodsRange, 4> Stack(1, OverriddenMethods);597while (!Stack.empty()) {598for (const CXXMethodDecl *OM : Stack.pop_back_val()) {599const CXXMethodDecl *CanonOM = OM->getCanonicalDecl();600601// C++ [class.virtual]p2:602// A virtual member function C::vf of a class object S is603// a final overrider unless the most derived class (1.8)604// of which S is a base class subobject (if any) declares605// or inherits another member function that overrides vf.606//607// Treating this object like the most derived class, we608// replace any overrides from base classes with this609// overriding virtual function.610Overriders[CanonOM].replaceAll(611UniqueVirtualMethod(CanonM, SubobjectNumber,612InVirtualSubobject));613614auto OverriddenMethods = CanonOM->overridden_methods();615if (OverriddenMethods.begin() == OverriddenMethods.end())616continue;617618// Continue recursion to the methods that this virtual method619// overrides.620Stack.push_back(OverriddenMethods);621}622}623624// C++ [class.virtual]p2:625// For convenience we say that any virtual function overrides itself.626Overriders[CanonM].add(SubobjectNumber,627UniqueVirtualMethod(CanonM, SubobjectNumber,628InVirtualSubobject));629}630}631632FinalOverriderCollector::~FinalOverriderCollector() {633for (llvm::DenseMap<const CXXRecordDecl *, CXXFinalOverriderMap *>::iterator634VO = VirtualOverriders.begin(), VOEnd = VirtualOverriders.end();635VO != VOEnd;636++VO)637delete VO->second;638}639640void641CXXRecordDecl::getFinalOverriders(CXXFinalOverriderMap &FinalOverriders) const {642FinalOverriderCollector Collector;643Collector.Collect(this, false, nullptr, FinalOverriders);644645// Weed out any final overriders that come from virtual base class646// subobjects that were hidden by other subobjects along any path.647// This is the final-overrider variant of C++ [class.member.lookup]p10.648for (auto &OM : FinalOverriders) {649for (auto &SO : OM.second) {650SmallVectorImpl<UniqueVirtualMethod> &Overriding = SO.second;651if (Overriding.size() < 2)652continue;653654auto IsHidden = [&Overriding](const UniqueVirtualMethod &M) {655if (!M.InVirtualSubobject)656return false;657658// We have an overriding method in a virtual base class659// subobject (or non-virtual base class subobject thereof);660// determine whether there exists an other overriding method661// in a base class subobject that hides the virtual base class662// subobject.663for (const UniqueVirtualMethod &OP : Overriding)664if (&M != &OP &&665OP.Method->getParent()->isVirtuallyDerivedFrom(666M.InVirtualSubobject))667return true;668return false;669};670671// FIXME: IsHidden reads from Overriding from the middle of a remove_if672// over the same sequence! Is this guaranteed to work?673llvm::erase_if(Overriding, IsHidden);674}675}676}677678static void679AddIndirectPrimaryBases(const CXXRecordDecl *RD, ASTContext &Context,680CXXIndirectPrimaryBaseSet& Bases) {681// If the record has a virtual primary base class, add it to our set.682const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);683if (Layout.isPrimaryBaseVirtual())684Bases.insert(Layout.getPrimaryBase());685686for (const auto &I : RD->bases()) {687assert(!I.getType()->isDependentType() &&688"Cannot get indirect primary bases for class with dependent bases.");689690const CXXRecordDecl *BaseDecl =691cast<CXXRecordDecl>(I.getType()->castAs<RecordType>()->getDecl());692693// Only bases with virtual bases participate in computing the694// indirect primary virtual base classes.695if (BaseDecl->getNumVBases())696AddIndirectPrimaryBases(BaseDecl, Context, Bases);697}698699}700701void702CXXRecordDecl::getIndirectPrimaryBases(CXXIndirectPrimaryBaseSet& Bases) const {703ASTContext &Context = getASTContext();704705if (!getNumVBases())706return;707708for (const auto &I : bases()) {709assert(!I.getType()->isDependentType() &&710"Cannot get indirect primary bases for class with dependent bases.");711712const CXXRecordDecl *BaseDecl =713cast<CXXRecordDecl>(I.getType()->castAs<RecordType>()->getDecl());714715// Only bases with virtual bases participate in computing the716// indirect primary virtual base classes.717if (BaseDecl->getNumVBases())718AddIndirectPrimaryBases(BaseDecl, Context, Bases);719}720}721722723