diff options
| -rw-r--r-- | clang/lib/Sema/SemaAccess.cpp | 329 | ||||
| -rw-r--r-- | clang/test/CXX/class.access/class.access.base/p5.cpp | 59 | 
2 files changed, 265 insertions, 123 deletions
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp index 0a3735d720e..f628884aab4 100644 --- a/clang/lib/Sema/SemaAccess.cpp +++ b/clang/lib/Sema/SemaAccess.cpp @@ -370,10 +370,6 @@ static Sema::AccessResult MatchesFriend(Sema &S,  static Sema::AccessResult GetFriendKind(Sema &S,                                          const EffectiveContext &EC,                                          const CXXRecordDecl *Class) { -  // A class always has access to its own members. -  if (EC.includesClass(Class)) -    return Sema::AR_accessible; -    Sema::AccessResult OnFailure = Sema::AR_inaccessible;    // Okay, check friends. @@ -401,9 +397,88 @@ static Sema::AccessResult GetFriendKind(Sema &S,    return OnFailure;  } +static Sema::AccessResult HasAccess(Sema &S, +                                    const EffectiveContext &EC, +                                    const CXXRecordDecl *NamingClass, +                                    AccessSpecifier Access) { +  assert(NamingClass->getCanonicalDecl() == NamingClass && +         "declaration should be canonicalized before being passed here"); + +  if (Access == AS_public) return Sema::AR_accessible; +  assert(Access == AS_private || Access == AS_protected); + +  for (EffectiveContext::record_iterator +         I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) { +    // All the declarations in EC have been canonicalized, so pointer +    // equality from this point on will work fine. +    const CXXRecordDecl *ECRecord = *I; + +    // [B2] and [M2] +    if (ECRecord == NamingClass) +      return Sema::AR_accessible; + +    // [B3] and [M3] +    if (Access == AS_protected && +        ECRecord->isDerivedFrom(const_cast<CXXRecordDecl*>(NamingClass))) +      return Sema::AR_accessible; +  } + +  return GetFriendKind(S, EC, NamingClass); +} +  /// 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_none if  ///   there is no final step.  /// \return null if friendship is dependent @@ -445,20 +520,15 @@ static CXXBasePath *FindBestPath(Sema &S,        }        AccessSpecifier BaseAccess = I->Base->getAccessSpecifier(); -      if (BaseAccess != AS_public || PathAccess != AS_public) { -        switch (GetFriendKind(S, EC, I->Class)) { -        case Sema::AR_inaccessible: -          PathAccess = CXXRecordDecl::MergeAccess(BaseAccess, PathAccess); -          break; -        case Sema::AR_accessible: -          PathAccess = AS_public; -          break; -        case Sema::AR_dependent: -          AnyDependent = true; -          goto Next; -        case Sema::AR_delayed: -          llvm_unreachable("friend resolution is never delayed"); break; -        } +      PathAccess = std::max(PathAccess, BaseAccess); +      switch (HasAccess(S, EC, I->Class, PathAccess)) { +      case Sema::AR_inaccessible: break; +      case Sema::AR_accessible: PathAccess = AS_public; break; +      case Sema::AR_dependent: +        AnyDependent = true; +        goto Next; +      case Sema::AR_delayed: +        llvm_unreachable("friend resolution is never delayed"); break;        }      } @@ -491,16 +561,27 @@ static CXXBasePath *FindBestPath(Sema &S,  /// to become inaccessible.  static void DiagnoseAccessPath(Sema &S,                                 const EffectiveContext &EC, -                               CXXRecordDecl *NamingClass, -                               CXXRecordDecl *DeclaringClass, -                               NamedDecl *D, AccessSpecifier Access) { +                               const Sema::AccessedEntity &Entity) { +  AccessSpecifier Access = Entity.getAccess(); +  CXXRecordDecl *NamingClass = Entity.getNamingClass(); +  NamingClass = NamingClass->getCanonicalDecl(); + +  NamedDecl *D; +  CXXRecordDecl *DeclaringClass; +  if (Entity.isMemberAccess()) { +    D = Entity.getTargetDecl(); +    DeclaringClass = FindDeclaringClass(D); +  } else { +    D = 0; +    DeclaringClass = Entity.getBaseClass(); +  } +  DeclaringClass = DeclaringClass->getCanonicalDecl(); +    // Easy case: the decl's natural access determined its path access.    // We have to check against AS_private here in case Access is AS_none,    // indicating a non-public member of a private base class. -  // -  // DependentFriend should be impossible here.    if (D && (Access == D->getAccess() || D->getAccess() == AS_private)) { -    switch (GetFriendKind(S, EC, DeclaringClass)) { +    switch (HasAccess(S, EC, DeclaringClass, D->getAccess())) {      case Sema::AR_inaccessible: {        S.Diag(D->getLocation(), diag::note_access_natural)          << (unsigned) (Access == AS_protected) @@ -566,101 +647,113 @@ static void DiagnoseAccessPath(Sema &S,    llvm_unreachable("access not apparently constrained by path");  } -/// Diagnose an inaccessible class member. -static void DiagnoseInaccessibleMember(Sema &S, SourceLocation Loc, -                                       const EffectiveContext &EC, -                                       CXXRecordDecl *NamingClass, -                                       AccessSpecifier Access, -                                       const Sema::AccessedEntity &Entity) { -  NamedDecl *D = Entity.getTargetDecl(); -  CXXRecordDecl *DeclaringClass = FindDeclaringClass(D); +static void DiagnoseBadAccess(Sema &S, SourceLocation Loc, +                              const EffectiveContext &EC, +                              const Sema::AccessedEntity &Entity) { +  const CXXRecordDecl *NamingClass = Entity.getNamingClass(); +  NamedDecl *D; +  const CXXRecordDecl *DeclaringClass; +  if (Entity.isMemberAccess()) { +    D = Entity.getTargetDecl(); +    DeclaringClass = FindDeclaringClass(D); +  } else { +    D = 0; +    DeclaringClass = Entity.getBaseClass(); +  }    S.Diag(Loc, Entity.getDiag()) -    << (Access == AS_protected) -    << D->getDeclName() +    << (Entity.getAccess() == AS_protected) +    << (D ? D->getDeclName() : DeclarationName())      << S.Context.getTypeDeclType(NamingClass)      << S.Context.getTypeDeclType(DeclaringClass); -  DiagnoseAccessPath(S, EC, NamingClass, DeclaringClass, D, Access); +  DiagnoseAccessPath(S, EC, Entity);  } -/// Diagnose an inaccessible hierarchy conversion. -static void DiagnoseInaccessibleBase(Sema &S, SourceLocation Loc, -                                     const EffectiveContext &EC, -                                     AccessSpecifier Access, -                                     const Sema::AccessedEntity &Entity) { -  S.Diag(Loc, Entity.getDiag()) -    << (Access == AS_protected) -    << DeclarationName() -    << S.Context.getTypeDeclType(Entity.getDerivedClass()) -    << S.Context.getTypeDeclType(Entity.getBaseClass()); -  DiagnoseAccessPath(S, EC, Entity.getDerivedClass(), -                     Entity.getBaseClass(), 0, Access); -} +/// Determines whether the accessed entity is accessible.  Public members +/// have been weeded out by this point. +static Sema::AccessResult IsAccessible(Sema &S, +                                       const EffectiveContext &EC, +                                       const Sema::AccessedEntity &Entity) { +  // Determine the actual naming class. +  CXXRecordDecl *NamingClass = Entity.getNamingClass(); +  while (NamingClass->isAnonymousStructOrUnion()) +    NamingClass = cast<CXXRecordDecl>(NamingClass->getParent()); +  NamingClass = NamingClass->getCanonicalDecl(); -static void DiagnoseBadAccess(Sema &S, SourceLocation Loc, -                              const EffectiveContext &EC, -                              CXXRecordDecl *NamingClass, -                              AccessSpecifier Access, -                              const Sema::AccessedEntity &Entity) { -  if (Entity.isMemberAccess()) -    DiagnoseInaccessibleMember(S, Loc, EC, NamingClass, Access, Entity); -  else -    DiagnoseInaccessibleBase(S, Loc, EC, Access, Entity); -} +  AccessSpecifier UnprivilegedAccess = Entity.getAccess(); +  assert(UnprivilegedAccess != AS_public && "public access not weeded out"); +  // Before we try to recalculate access paths, try to white-list +  // accesses which just trade in on the final step, i.e. accesses +  // which don't require [M4] or [B4]. These are by far the most +  // common forms of access. +  if (UnprivilegedAccess != AS_none) { +    switch (HasAccess(S, EC, NamingClass, UnprivilegedAccess)) { +    case Sema::AR_dependent: +      // This is actually an interesting policy decision.  We don't +      // *have* to delay immediately here: we can do the full access +      // calculation in the hope that friendship on some intermediate +      // class will make the declaration accessible non-dependently. +      // But that's not cheap, and odds are very good (note: assertion +      // made without data) that the friend declaration will determine +      // access. +      return Sema::AR_dependent; + +    case Sema::AR_accessible: return Sema::AR_accessible; +    case Sema::AR_inaccessible: break; +    case Sema::AR_delayed: +      llvm_unreachable("friendship never subject to contextual delay"); +    } +  } -/// Try to elevate access using friend declarations.  This is -/// potentially quite expensive. -/// -/// \return true if elevation was dependent -static bool TryElevateAccess(Sema &S, -                             const EffectiveContext &EC, -                             const Sema::AccessedEntity &Entity, -                             AccessSpecifier &Access) { +  // Determine the declaring class.    CXXRecordDecl *DeclaringClass;    if (Entity.isMemberAccess()) {      DeclaringClass = FindDeclaringClass(Entity.getTargetDecl());    } else {      DeclaringClass = Entity.getBaseClass();    } -  CXXRecordDecl *NamingClass = Entity.getNamingClass(); +  DeclaringClass = DeclaringClass->getCanonicalDecl(); + +  // We lower member accesses to base accesses by pretending that the +  // member is a base class of its declaring class. +  AccessSpecifier FinalAccess; -  // Adjust the declaration of the referred entity. -  AccessSpecifier DeclAccess = AS_public;    if (Entity.isMemberAccess()) { +    // Determine if the declaration is accessible from EC when named +    // in its declaring class.      NamedDecl *Target = Entity.getTargetDecl(); -    DeclAccess = Target->getAccess(); -    if (DeclAccess != AS_public) { -      switch (GetFriendKind(S, EC, DeclaringClass)) { -      case Sema::AR_accessible: DeclAccess = AS_public; break; -      case Sema::AR_inaccessible: break; -      case Sema::AR_dependent: return true; -      case Sema::AR_delayed: llvm_unreachable("friend status is never delayed"); -      } +    FinalAccess = Target->getAccess(); +    switch (HasAccess(S, EC, DeclaringClass, FinalAccess)) { +    case Sema::AR_accessible: FinalAccess = AS_public; break; +    case Sema::AR_inaccessible: break; +    case Sema::AR_dependent: return Sema::AR_dependent; // see above +    case Sema::AR_delayed: llvm_unreachable("friend status is never delayed");      } -    if (DeclaringClass == NamingClass) { -      Access = DeclAccess; -      return false; -    } +    if (DeclaringClass == NamingClass) +      return (FinalAccess == AS_public +              ? Sema::AR_accessible +              : Sema::AR_inaccessible); +  } else { +    FinalAccess = AS_public;    }    assert(DeclaringClass != NamingClass);    // Append the declaration's access if applicable.    CXXBasePaths Paths; -  CXXBasePath *Path = FindBestPath(S, EC, Entity.getNamingClass(), -                                   DeclaringClass, DeclAccess, Paths); +  CXXBasePath *Path = FindBestPath(S, EC, NamingClass, DeclaringClass, +                                   FinalAccess, Paths);    if (!Path) -    return true; +    return Sema::AR_dependent; -  // Grab the access along the best path (note that this includes the -  // final-step access). -  AccessSpecifier NewAccess = Path->Access; -  assert(NewAccess <= Access && "access along best path worse than direct?"); -  Access = NewAccess; -  return false; +  assert(Path->Access <= UnprivilegedAccess && +         "access along best path worse than direct?"); +  if (Path->Access == AS_public) +    return Sema::AR_accessible; +  return Sema::AR_inaccessible;  }  static void DelayAccess(Sema &S, @@ -683,47 +776,37 @@ static void DelayAccess(Sema &S,  static Sema::AccessResult CheckEffectiveAccess(Sema &S,                                                 const EffectiveContext &EC,                                                 SourceLocation Loc, -                                         Sema::AccessedEntity const &Entity) { -  AccessSpecifier Access = Entity.getAccess(); -  assert(Access != AS_public && "called for public access!"); - -  // Find a non-anonymous naming class.  For records with access, -  // there should always be one of these. -  CXXRecordDecl *NamingClass = Entity.getNamingClass(); -  while (NamingClass->isAnonymousStructOrUnion()) -    NamingClass = cast<CXXRecordDecl>(NamingClass->getParent()); +                                         const Sema::AccessedEntity &Entity) { +  assert(Entity.getAccess() != AS_public && "called for public access!"); -  // White-list accesses from classes with privileges equivalent to the -  // naming class --- but only if the access path isn't forbidden -  // (i.e. an access of a private member from a subclass). -  if (Access != AS_none && EC.includesClass(NamingClass)) -    return Sema::AR_accessible; - -  // Try to elevate access. -  // TODO: on some code, it might be better to do the protected check -  // without trying to elevate first. -  if (TryElevateAccess(S, EC, Entity, Access)) { +  switch (IsAccessible(S, EC, Entity)) { +  case Sema::AR_dependent:      DelayAccess(S, EC, Loc, Entity);      return Sema::AR_dependent; -  } -  if (Access == AS_public) return Sema::AR_accessible; +  case Sema::AR_delayed: +    llvm_unreachable("IsAccessible cannot contextually delay"); -  // Protected access. -  if (Access == AS_protected) { -    // FIXME: implement [class.protected]p1 -    for (llvm::SmallVectorImpl<CXXRecordDecl*>::const_iterator -           I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) -      if ((*I)->isDerivedFrom(NamingClass)) -        return Sema::AR_accessible; +  case Sema::AR_inaccessible: +    if (!Entity.isQuiet()) +      DiagnoseBadAccess(S, Loc, EC, Entity); +    return Sema::AR_inaccessible; -    // FIXME: delay if we can't decide class derivation yet. +  case Sema::AR_accessible: +    break;    } -  // Okay, that's it, reject it. -  if (!Entity.isQuiet()) -    DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity); -  return Sema::AR_inaccessible; +  // We only consider the natural access of the declaration when +  // deciding whether to do the protected check. +  if (Entity.isMemberAccess() && Entity.getAccess() == AS_protected) { +    NamedDecl *D = Entity.getTargetDecl(); +    if (isa<FieldDecl>(D) || +        (isa<CXXMethodDecl>(D) && cast<CXXMethodDecl>(D)->isInstance())) { +      // FIXME: implement [class.protected] +    } +  } + +  return Sema::AR_accessible;  }  static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc, diff --git a/clang/test/CXX/class.access/class.access.base/p5.cpp b/clang/test/CXX/class.access/class.access.base/p5.cpp new file mode 100644 index 00000000000..f84d3b27520 --- /dev/null +++ b/clang/test/CXX/class.access/class.access.base/p5.cpp @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -faccess-control -verify %s + +namespace test0 { +  struct A { +    static int x; +  }; +  struct B : A {}; +  struct C : B {}; + +  int test() { +    return A::x +         + B::x +         + C::x; +  } +} + +namespace test1 { +  struct A { +    private: static int x; // expected-note 5 {{declared private here}} +    static int test() { return x; } +  }; +  struct B : public A { +    static int test() { return x; } // expected-error {{private member}} +  }; +  struct C : private A { +    static int test() { return x; } // expected-error {{private member}} +  }; + +  struct D { +    public: static int x; +    static int test() { return x; } +  }; +  struct E : private D { // expected-note{{constrained by private inheritance}} +    static int test() { return x; } +  }; + +  int test() { +    return A::x // expected-error {{private member}} +         + B::x // expected-error {{private member}} +         + C::x // expected-error {{private member}} +         + D::x +         + E::x; // expected-error {{private member}} +  } +} + +namespace test2 { +  class A { +  protected: static int x; +  }; + +  class B : private A {}; // expected-note {{private inheritance}} +  class C : private A { +    int test(B *b) { +      return b->x; // expected-error {{private member}} +    } +  }; +} + +// TODO: flesh out these cases  | 

