// A GC mixin is a class that inherits from a GC mixin base and has // has not yet been "mixed in" with another GC base class. bool RecordInfo::IsGCMixin() { if (!IsGCDerived() || base_paths_->begin() == base_paths_->end()) return false; // Get the last element of the first path. CXXBasePaths::paths_iterator it = base_paths_->begin(); const CXXBasePathElement& elem = (*it)[it->size() - 1]; CXXRecordDecl* base = elem.Base->getType()->getAsCXXRecordDecl(); // If it is not a mixin base we are done. if (!Config::IsGCMixinBase(base->getName())) return false; // This is a mixin if there are no other paths to GC bases. return ++it == base_paths_->end(); }
bool RecordInfo::IsGCFinalized() { if (!IsGCDerived()) return false; for (CXXBasePaths::paths_iterator it = base_paths_->begin(); it != base_paths_->end(); ++it) { const CXXBasePathElement& elem = (*it)[it->size() - 1]; CXXRecordDecl* base = elem.Base->getType()->getAsCXXRecordDecl(); if (Config::IsGCFinalizedBase(base->getName())) return true; } return false; }
bool RecordInfo::IsTreeShared() { if (Config::IsTreeSharedBase(name_)) return true; if (!IsGCDerived()) return false; for (CXXBasePaths::paths_iterator it = base_paths_->begin(); it != base_paths_->end(); ++it) { // TreeShared is an immediate base of GCFinalized. if (it->size() < 2) continue; const CXXBasePathElement& elem = (*it)[it->size() - 2]; CXXRecordDecl* base = elem.Base->getType()->getAsCXXRecordDecl(); if (Config::IsTreeSharedBase(base->getName())) return true; } return false; }
bool CXXRecordDecl::lookupInBases(BaseMatchesCallback *BaseMatches, void *UserData, CXXBasePaths &Paths) const { // If we didn't find anything, report that. if (!Paths.lookupInBases(getASTContext(), this, BaseMatches, UserData)) return false; // If we're not recording paths or we won't ever find ambiguities, // we're done. if (!Paths.isRecordingPaths() || !Paths.isFindingAmbiguities()) return true; // C++ [class.member.lookup]p6: // When virtual base classes are used, a hidden declaration can be // reached along a path through the sub-object lattice that does // not pass through the hiding declaration. This is not an // ambiguity. The identical use with nonvirtual base classes is an // ambiguity; in that case there is no unique instance of the name // that hides all the others. // // FIXME: This is an O(N^2) algorithm, but DPG doesn't see an easy // way to make it any faster. for (CXXBasePaths::paths_iterator P = Paths.begin(), PEnd = Paths.end(); P != PEnd; /* increment in loop */) { bool Hidden = false; for (CXXBasePath::iterator PE = P->begin(), PEEnd = P->end(); PE != PEEnd && !Hidden; ++PE) { if (PE->Base->isVirtual()) { CXXRecordDecl *VBase = 0; if (const RecordType *Record = PE->Base->getType()->getAs<RecordType>()) VBase = cast<CXXRecordDecl>(Record->getDecl()); if (!VBase) break; // The declaration(s) we found along this path were found in a // subobject of a virtual base. Check whether this virtual // base is a subobject of any other path; if so, then the // declaration in this path are hidden by that patch. for (CXXBasePaths::paths_iterator HidingP = Paths.begin(), HidingPEnd = Paths.end(); HidingP != HidingPEnd; ++HidingP) { CXXRecordDecl *HidingClass = 0; if (const RecordType *Record = HidingP->back().Base->getType()->getAs<RecordType>()) HidingClass = cast<CXXRecordDecl>(Record->getDecl()); if (!HidingClass) break; if (HidingClass->isVirtuallyDerivedFrom(VBase)) { Hidden = true; break; } } } } if (Hidden) P = Paths.Paths.erase(P); else ++P; } return true; }
/// Finds the best path from the naming class to the declaring class, /// taking friend declarations into account. /// /// C++0x [class.access.base]p5: /// A member m is accessible at the point R when named in class N if /// [M1] m as a member of N is public, or /// [M2] m as a member of N is private, and R occurs in a member or /// friend of class N, or /// [M3] m as a member of N is protected, and R occurs in a member or /// friend of class N, or in a member or friend of a class P /// derived from N, where m as a member of P is public, private, /// or protected, or /// [M4] there exists a base class B of N that is accessible at R, and /// m is accessible at R when named in class B. /// /// C++0x [class.access.base]p4: /// A base class B of N is accessible at R, if /// [B1] an invented public member of B would be a public member of N, or /// [B2] R occurs in a member or friend of class N, and an invented public /// member of B would be a private or protected member of N, or /// [B3] R occurs in a member or friend of a class P derived from N, and an /// invented public member of B would be a private or protected member /// of P, or /// [B4] there exists a class S such that B is a base class of S accessible /// at R and S is a base class of N accessible at R. /// /// Along a single inheritance path we can restate both of these /// iteratively: /// /// First, we note that M1-4 are equivalent to B1-4 if the member is /// treated as a notional base of its declaring class with inheritance /// access equivalent to the member's access. Therefore we need only /// ask whether a class B is accessible from a class N in context R. /// /// Let B_1 .. B_n be the inheritance path in question (i.e. where /// B_1 = N, B_n = B, and for all i, B_{i+1} is a direct base class of /// B_i). For i in 1..n, we will calculate ACAB(i), the access to the /// closest accessible base in the path: /// Access(a, b) = (* access on the base specifier from a to b *) /// Merge(a, forbidden) = forbidden /// Merge(a, private) = forbidden /// Merge(a, b) = min(a,b) /// Accessible(c, forbidden) = false /// Accessible(c, private) = (R is c) || IsFriend(c, R) /// Accessible(c, protected) = (R derived from c) || IsFriend(c, R) /// Accessible(c, public) = true /// ACAB(n) = public /// ACAB(i) = /// let AccessToBase = Merge(Access(B_i, B_{i+1}), ACAB(i+1)) in /// if Accessible(B_i, AccessToBase) then public else AccessToBase /// /// B is an accessible base of N at R iff ACAB(1) = public. /// /// \param FinalAccess the access of the "final step", or AS_public if /// there is no final step. /// \return null if friendship is dependent static CXXBasePath *FindBestPath(Sema &S, const EffectiveContext &EC, AccessTarget &Target, AccessSpecifier FinalAccess, CXXBasePaths &Paths) { // Derive the paths to the desired base. const CXXRecordDecl *Derived = Target.getNamingClass(); const CXXRecordDecl *Base = Target.getDeclaringClass(); // FIXME: fail correctly when there are dependent paths. bool isDerived = Derived->isDerivedFrom(const_cast<CXXRecordDecl*>(Base), Paths); assert(isDerived && "derived class not actually derived from base"); (void) isDerived; CXXBasePath *BestPath = 0; assert(FinalAccess != AS_none && "forbidden access after declaring class"); bool AnyDependent = false; // Derive the friend-modified access along each path. for (CXXBasePaths::paths_iterator PI = Paths.begin(), PE = Paths.end(); PI != PE; ++PI) { AccessTarget::SavedInstanceContext _ = Target.saveInstanceContext(); // Walk through the path backwards. AccessSpecifier PathAccess = FinalAccess; CXXBasePath::iterator I = PI->end(), E = PI->begin(); while (I != E) { --I; assert(PathAccess != AS_none); // If the declaration is a private member of a base class, there // is no level of friendship in derived classes that can make it // accessible. if (PathAccess == AS_private) { PathAccess = AS_none; break; } const CXXRecordDecl *NC = I->Class->getCanonicalDecl(); AccessSpecifier BaseAccess = I->Base->getAccessSpecifier(); PathAccess = std::max(PathAccess, BaseAccess); switch (HasAccess(S, EC, NC, PathAccess, Target)) { case AR_inaccessible: break; case AR_accessible: PathAccess = AS_public; // Future tests are not against members and so do not have // instance context. Target.suppressInstanceContext(); break; case AR_dependent: AnyDependent = true; goto Next; } } // Note that we modify the path's Access field to the // friend-modified access. if (BestPath == 0 || PathAccess < BestPath->Access) { BestPath = &*PI; BestPath->Access = PathAccess; // Short-circuit if we found a public path. if (BestPath->Access == AS_public) return BestPath; } Next: ; } assert((!BestPath || BestPath->Access != AS_public) && "fell out of loop with public path"); // We didn't find a public path, but at least one path was subject // to dependent friendship, so delay the check. if (AnyDependent) return 0; return BestPath; }