//===---- SemaAccess.cpp - C++ Access Control -------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file provides Sema routines for C++ access control semantics. // //===----------------------------------------------------------------------===// #include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclFriend.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DependentDiagnostic.h" #include "clang/AST/ExprCXX.h" #include "clang/Basic/Specifiers.h" #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/SemaInternal.h" #include "llvm/ADT/STLForwardCompat.h" usingnamespaceclang; usingnamespacesema; /// A copy of Sema's enum without AR_delayed. enum AccessResult { … }; bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl, NamedDecl *PrevMemberDecl, AccessSpecifier LexicalAS) { … } static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) { … } namespace { struct EffectiveContext { … }; /// Like sema::AccessedEntity, but kindly lets us scribble all over /// it. struct AccessTarget : public AccessedEntity { … }; } /// Checks whether one class might instantiate to the other. static bool MightInstantiateTo(const CXXRecordDecl *From, const CXXRecordDecl *To) { … } /// Checks whether one class is derived from another, inclusively. /// Properly indicates when it couldn't be determined due to /// dependence. /// /// This should probably be donated to AST or at least Sema. static AccessResult IsDerivedFromInclusive(const CXXRecordDecl *Derived, const CXXRecordDecl *Target) { … } static bool MightInstantiateTo(Sema &S, DeclContext *Context, DeclContext *Friend) { … } // Asks whether the type in 'context' can ever instantiate to the type // in 'friend'. static bool MightInstantiateTo(Sema &S, CanQualType Context, CanQualType Friend) { … } static bool MightInstantiateTo(Sema &S, FunctionDecl *Context, FunctionDecl *Friend) { … } static bool MightInstantiateTo(Sema &S, FunctionTemplateDecl *Context, FunctionTemplateDecl *Friend) { … } static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, const CXXRecordDecl *Friend) { … } static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, CanQualType Friend) { … } /// Determines whether the given friend class template matches /// anything in the effective context. static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, ClassTemplateDecl *Friend) { … } /// Determines whether the given friend function matches anything in /// the effective context. static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, FunctionDecl *Friend) { … } /// Determines whether the given friend function template matches /// anything in the effective context. static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, FunctionTemplateDecl *Friend) { … } /// Determines whether the given friend declaration matches anything /// in the effective context. static AccessResult MatchesFriend(Sema &S, const EffectiveContext &EC, FriendDecl *FriendD) { … } static AccessResult GetFriendKind(Sema &S, const EffectiveContext &EC, const CXXRecordDecl *Class) { … } namespace { /// A helper class for checking for a friend which will grant access /// to a protected instance member. struct ProtectedFriendContext { … }; } /// Search for a class P that EC is a friend of, under the constraint /// InstanceContext <= P /// if InstanceContext exists, or else /// NamingClass <= P /// and with the additional restriction that a protected member of /// NamingClass would have some natural access in P, which implicitly /// imposes the constraint that P <= NamingClass. /// /// This isn't quite the condition laid out in the standard. /// Instead of saying that a notional protected member of NamingClass /// would have to have some natural access in P, it says the actual /// target has to have some natural access in P, which opens up the /// possibility that the target (which is not necessarily a member /// of NamingClass) might be more accessible along some path not /// passing through it. That's really a bad idea, though, because it /// introduces two problems: /// - Most importantly, it breaks encapsulation because you can /// access a forbidden base class's members by directly subclassing /// it elsewhere. /// - It also makes access substantially harder to compute because it /// breaks the hill-climbing algorithm: knowing that the target is /// accessible in some base class would no longer let you change /// the question solely to whether the base class is accessible, /// because the original target might have been more accessible /// because of crazy subclassing. /// So we don't implement that. static AccessResult GetProtectedFriendKind(Sema &S, const EffectiveContext &EC, const CXXRecordDecl *InstanceContext, const CXXRecordDecl *NamingClass) { … } static AccessResult HasAccess(Sema &S, const EffectiveContext &EC, const CXXRecordDecl *NamingClass, AccessSpecifier Access, const AccessTarget &Target) { … } /// 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) { … } /// Given that an entity has protected natural access, check whether /// access might be denied because of the protected member access /// restriction. /// /// \return true if a note was emitted static bool TryDiagnoseProtectedAccess(Sema &S, const EffectiveContext &EC, AccessTarget &Target) { … } /// We are unable to access a given declaration due to its direct /// access control; diagnose that. static void diagnoseBadDirectAccess(Sema &S, const EffectiveContext &EC, AccessTarget &entity) { … } /// Diagnose the path which caused the given declaration or base class /// to become inaccessible. static void DiagnoseAccessPath(Sema &S, const EffectiveContext &EC, AccessTarget &entity) { … } static void DiagnoseBadAccess(Sema &S, SourceLocation Loc, const EffectiveContext &EC, AccessTarget &Entity) { … } /// MSVC has a bug where if during an using declaration name lookup, /// the declaration found is unaccessible (private) and that declaration /// was bring into scope via another using declaration whose target /// declaration is accessible (public) then no error is generated. /// Example: /// class A { /// public: /// int f(); /// }; /// class B : public A { /// private: /// using A::f; /// }; /// class C : public B { /// private: /// using B::f; /// }; /// /// Here, B::f is private so this should fail in Standard C++, but /// because B::f refers to A::f which is public MSVC accepts it. static bool IsMicrosoftUsingDeclarationAccessBug(Sema& S, SourceLocation AccessLoc, AccessTarget &Entity) { … } /// Determines whether the accessed entity is accessible. Public members /// have been weeded out by this point. static AccessResult IsAccessible(Sema &S, const EffectiveContext &EC, AccessTarget &Entity) { … } static void DelayDependentAccess(Sema &S, const EffectiveContext &EC, SourceLocation Loc, const AccessTarget &Entity) { … } /// Checks access to an entity from the given effective context. static AccessResult CheckEffectiveAccess(Sema &S, const EffectiveContext &EC, SourceLocation Loc, AccessTarget &Entity) { … } static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc, AccessTarget &Entity) { … } void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *D) { … } void Sema::HandleDependentAccessCheck(const DependentDiagnostic &DD, const MultiLevelTemplateArgumentList &TemplateArgs) { … } Sema::AccessResult Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E, DeclAccessPair Found) { … } Sema::AccessResult Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E, DeclAccessPair Found) { … } bool Sema::isMemberAccessibleForDeletion(CXXRecordDecl *NamingClass, DeclAccessPair Found, QualType ObjectType, SourceLocation Loc, const PartialDiagnostic &Diag) { … } Sema::AccessResult Sema::CheckDestructorAccess(SourceLocation Loc, CXXDestructorDecl *Dtor, const PartialDiagnostic &PDiag, QualType ObjectTy) { … } Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc, CXXConstructorDecl *Constructor, DeclAccessPair Found, const InitializedEntity &Entity, bool IsCopyBindingRefToTemp) { … } Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc, CXXConstructorDecl *Constructor, DeclAccessPair Found, const InitializedEntity &Entity, const PartialDiagnostic &PD) { … } Sema::AccessResult Sema::CheckAllocationAccess(SourceLocation OpLoc, SourceRange PlacementRange, CXXRecordDecl *NamingClass, DeclAccessPair Found, bool Diagnose) { … } Sema::AccessResult Sema::CheckMemberAccess(SourceLocation UseLoc, CXXRecordDecl *NamingClass, DeclAccessPair Found) { … } Sema::AccessResult Sema::CheckStructuredBindingMemberAccess(SourceLocation UseLoc, CXXRecordDecl *DecomposedClass, DeclAccessPair Field) { … } Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc, Expr *ObjectExpr, const SourceRange &Range, DeclAccessPair Found) { … } Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc, Expr *ObjectExpr, Expr *ArgExpr, DeclAccessPair Found) { … } Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc, Expr *ObjectExpr, ArrayRef<Expr *> ArgExprs, DeclAccessPair FoundDecl) { … } Sema::AccessResult Sema::CheckFriendAccess(NamedDecl *target) { … } Sema::AccessResult Sema::CheckAddressOfMemberAccess(Expr *OvlExpr, DeclAccessPair Found) { … } Sema::AccessResult Sema::CheckBaseClassAccess(SourceLocation AccessLoc, QualType Base, QualType Derived, const CXXBasePath &Path, unsigned DiagID, bool ForceCheck, bool ForceUnprivileged) { … } void Sema::CheckLookupAccess(const LookupResult &R) { … } bool Sema::IsSimplyAccessible(NamedDecl *Target, CXXRecordDecl *NamingClass, QualType BaseType) { … }