[cfe-commits] r100562 - in /cfe/trunk: include/clang/AST/Decl.h include/clang/AST/DeclAccessPair.h include/clang/AST/DeclCXX.h include/clang/AST/DependentDiagnostic.h include/clang/AST/Expr.h include/clang/AST/UnresolvedSet.h lib/AST/Decl.cpp lib/AST/Expr.cpp lib/Sema/Lookup.h lib/Sema/Sema.h lib/Sema/SemaAccess.cpp lib/Sema/SemaExpr.cpp lib/Sema/SemaOverload.cpp lib/Sema/SemaType.cpp test/CXX/class.access/class.protected/p1.cpp

John McCall rjmccall at apple.com
Tue Apr 6 14:38:20 PDT 2010


Author: rjmccall
Date: Tue Apr  6 16:38:20 2010
New Revision: 100562

URL: http://llvm.org/viewvc/llvm-project?rev=100562&view=rev
Log:
Implement the protected access restriction ([class.protected]), which requires
that protected members be used on objects of types which derive from the
naming class of the lookup.  My first N attempts at this were poorly-founded,
largely because the standard is very badly worded here.


Added:
    cfe/trunk/include/clang/AST/DeclAccessPair.h
    cfe/trunk/test/CXX/class.access/class.protected/p1.cpp
Modified:
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/AST/DeclCXX.h
    cfe/trunk/include/clang/AST/DependentDiagnostic.h
    cfe/trunk/include/clang/AST/Expr.h
    cfe/trunk/include/clang/AST/UnresolvedSet.h
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/AST/Expr.cpp
    cfe/trunk/lib/Sema/Lookup.h
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaAccess.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/lib/Sema/SemaType.cpp

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=100562&r1=100561&r2=100562&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Tue Apr  6 16:38:20 2010
@@ -196,6 +196,10 @@
     return DC->isRecord();
   }
 
+  /// \brief Given that this declaration is a C++ class member,
+  /// determine whether it's an instance member of its class.
+  bool isCXXInstanceMember() const;
+
   /// \brief Determine what kind of linkage this entity has.
   Linkage getLinkage() const;
 

Added: cfe/trunk/include/clang/AST/DeclAccessPair.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclAccessPair.h?rev=100562&view=auto
==============================================================================
--- cfe/trunk/include/clang/AST/DeclAccessPair.h (added)
+++ cfe/trunk/include/clang/AST/DeclAccessPair.h Tue Apr  6 16:38:20 2010
@@ -0,0 +1,72 @@
+//===--- DeclAccessPair.h - A decl bundled with its path access -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the DeclAccessPair class, which provides an
+//  efficient representation of a pair of a NamedDecl* and an
+//  AccessSpecifier.  Generally the access specifier gives the
+//  natural access of a declaration when named in a class, as
+//  defined in C++ [class.access.base]p1.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_DECLACCESSPAIR_H
+#define LLVM_CLANG_AST_DECLACCESSPAIR_H
+
+#include "clang/Basic/Specifiers.h"
+
+namespace clang {
+
+class NamedDecl;
+
+/// A POD class for pairing a NamedDecl* with an access specifier.
+/// Can be put into unions.
+class DeclAccessPair {
+  NamedDecl *Ptr; // we'd use llvm::PointerUnion, but it isn't trivial
+
+  enum { Mask = 0x3 };
+
+public:
+  static DeclAccessPair make(NamedDecl *D, AccessSpecifier AS) {
+    DeclAccessPair p;
+    p.set(D, AS);
+    return p;
+  }
+
+  NamedDecl *getDecl() const {
+    return (NamedDecl*) (~Mask & (uintptr_t) Ptr);
+  }
+  AccessSpecifier getAccess() const {
+    return AccessSpecifier(Mask & (uintptr_t) Ptr);
+  }
+
+  void setDecl(NamedDecl *D) {
+    set(D, getAccess());
+  }
+  void setAccess(AccessSpecifier AS) {
+    set(getDecl(), AS);
+  }
+  void set(NamedDecl *D, AccessSpecifier AS) {
+    Ptr = reinterpret_cast<NamedDecl*>(uintptr_t(AS) |
+                                       reinterpret_cast<uintptr_t>(D));
+  }
+
+  operator NamedDecl*() const { return getDecl(); }
+  NamedDecl *operator->() const { return getDecl(); }
+};
+}
+
+// Take a moment to tell SmallVector that DeclAccessPair is POD.
+namespace llvm {
+template<typename> struct isPodLike;
+template<> struct isPodLike<clang::DeclAccessPair> {
+   static const bool value = true;
+};
+}
+
+#endif

Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=100562&r1=100561&r2=100562&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Tue Apr  6 16:38:20 2010
@@ -471,6 +471,11 @@
   friend_iterator friend_end() const;
   void pushFriendDecl(FriendDecl *FD);
 
+  /// Determines whether this record has any friends.
+  bool hasFriends() const {
+    return data().FirstFriend != 0;
+  }
+
   /// hasConstCopyConstructor - Determines whether this class has a
   /// copy constructor that accepts a const-qualified argument.
   bool hasConstCopyConstructor(ASTContext &Context) const;

Modified: cfe/trunk/include/clang/AST/DependentDiagnostic.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DependentDiagnostic.h?rev=100562&r1=100561&r2=100562&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DependentDiagnostic.h (original)
+++ cfe/trunk/include/clang/AST/DependentDiagnostic.h Tue Apr  6 16:38:20 2010
@@ -22,6 +22,7 @@
 #include "clang/Basic/SourceLocation.h"
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclContextInternals.h"
+#include "clang/AST/Type.h"
 
 namespace clang {
 
@@ -42,6 +43,7 @@
                                      AccessSpecifier AS,
                                      NamedDecl *TargetDecl,
                                      CXXRecordDecl *NamingClass,
+                                     QualType BaseObjectType,
                                      const PartialDiagnostic &PDiag) {
     DependentDiagnostic *DD = Create(Context, Parent, PDiag);
     DD->AccessData.Loc = Loc.getRawEncoding();
@@ -49,6 +51,7 @@
     DD->AccessData.Access = AS;
     DD->AccessData.TargetDecl = TargetDecl;
     DD->AccessData.NamingClass = NamingClass;
+    DD->AccessData.BaseObjectType = BaseObjectType.getAsOpaquePtr();
     return DD;
   }
 
@@ -81,6 +84,11 @@
     return AccessData.NamingClass;
   }
 
+  QualType getAccessBaseObjectType() const {
+    assert(getKind() == Access);
+    return QualType::getFromOpaquePtr(AccessData.BaseObjectType);
+  }
+
   const PartialDiagnostic &getDiagnostic() const {
     return Diag;
   }
@@ -107,6 +115,7 @@
       unsigned IsMember : 1;
       NamedDecl *TargetDecl;
       CXXRecordDecl *NamingClass;
+      void *BaseObjectType;
     } AccessData;
   };
 };

Modified: cfe/trunk/include/clang/AST/Expr.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Expr.h?rev=100562&r1=100561&r2=100562&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Expr.h (original)
+++ cfe/trunk/include/clang/AST/Expr.h Tue Apr  6 16:38:20 2010
@@ -17,6 +17,7 @@
 #include "clang/AST/APValue.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/Type.h"
+#include "clang/AST/DeclAccessPair.h"
 #include "llvm/ADT/APSInt.h"
 #include "llvm/ADT/APFloat.h"
 #include "llvm/ADT/SmallVector.h"
@@ -1274,7 +1275,7 @@
 class MemberExpr : public Expr {
   /// Extra data stored in some member expressions.
   struct MemberNameQualifier : public NameQualifier {
-    NamedDecl *FoundDecl;
+    DeclAccessPair FoundDecl;
   };
 
   /// Base - the expression for the base pointer or structure references.  In
@@ -1349,7 +1350,7 @@
 
   static MemberExpr *Create(ASTContext &C, Expr *base, bool isarrow,
                             NestedNameSpecifier *qual, SourceRange qualrange,
-                            ValueDecl *memberdecl, NamedDecl *founddecl,
+                            ValueDecl *memberdecl, DeclAccessPair founddecl,
                             SourceLocation l,
                             const TemplateArgumentListInfo *targs,
                             QualType ty);
@@ -1365,9 +1366,10 @@
   void setMemberDecl(ValueDecl *D) { MemberDecl = D; }
 
   /// \brief Retrieves the declaration found by lookup.
-  NamedDecl *getFoundDecl() const {
+  DeclAccessPair getFoundDecl() const {
     if (!HasQualifierOrFoundDecl)
-      return getMemberDecl();
+      return DeclAccessPair::make(getMemberDecl(),
+                                  getMemberDecl()->getAccess());
     return getMemberQualifier()->FoundDecl;
   }
 

Modified: cfe/trunk/include/clang/AST/UnresolvedSet.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/UnresolvedSet.h?rev=100562&r1=100561&r2=100562&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/UnresolvedSet.h (original)
+++ cfe/trunk/include/clang/AST/UnresolvedSet.h Tue Apr  6 16:38:20 2010
@@ -17,56 +17,7 @@
 
 #include <iterator>
 #include "llvm/ADT/SmallVector.h"
-#include "clang/Basic/Specifiers.h"
-
-namespace clang {
-
-class NamedDecl;
-
-/// A POD class for pairing a NamedDecl* with an access specifier.
-/// Can be put into unions.
-class DeclAccessPair {
-  NamedDecl *Ptr; // we'd use llvm::PointerUnion, but it isn't trivial
-
-  enum { Mask = 0x3 };
-
-public:
-  static DeclAccessPair make(NamedDecl *D, AccessSpecifier AS) {
-    DeclAccessPair p;
-    p.set(D, AS);
-    return p;
-  }
-
-  NamedDecl *getDecl() const {
-    return (NamedDecl*) (~Mask & (uintptr_t) Ptr);
-  }
-  AccessSpecifier getAccess() const {
-    return AccessSpecifier(Mask & (uintptr_t) Ptr);
-  }
-
-  void setDecl(NamedDecl *D) {
-    set(D, getAccess());
-  }
-  void setAccess(AccessSpecifier AS) {
-    set(getDecl(), AS);
-  }
-  void set(NamedDecl *D, AccessSpecifier AS) {
-    Ptr = reinterpret_cast<NamedDecl*>(uintptr_t(AS) |
-                                       reinterpret_cast<uintptr_t>(D));
-  }
-
-  operator NamedDecl*() const { return getDecl(); }
-  NamedDecl *operator->() const { return getDecl(); }
-};
-}
-
-// Take a moment to tell SmallVector that this is POD.
-namespace llvm {
-template<typename> struct isPodLike;
-template<> struct isPodLike<clang::DeclAccessPair> {
-   static const bool value = true;
-};
-}
+#include "clang/AST/DeclAccessPair.h"
 
 namespace clang {
 

Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=100562&r1=100561&r2=100562&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Tue Apr  6 16:38:20 2010
@@ -494,6 +494,24 @@
   }
 }
 
+bool NamedDecl::isCXXInstanceMember() const {
+  assert(isCXXClassMember() &&
+         "checking whether non-member is instance member");
+
+  const NamedDecl *D = this;
+  if (isa<UsingShadowDecl>(D))
+    D = cast<UsingShadowDecl>(D)->getTargetDecl();
+
+  if (isa<FieldDecl>(D))
+    return true;
+  if (isa<CXXMethodDecl>(D))
+    return cast<CXXMethodDecl>(D)->isInstance();
+  if (isa<FunctionTemplateDecl>(D))
+    return cast<CXXMethodDecl>(cast<FunctionTemplateDecl>(D)
+                                 ->getTemplatedDecl())->isInstance();
+  return false;
+}
+
 //===----------------------------------------------------------------------===//
 // DeclaratorDecl Implementation
 //===----------------------------------------------------------------------===//

Modified: cfe/trunk/lib/AST/Expr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Expr.cpp?rev=100562&r1=100561&r2=100562&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Expr.cpp (original)
+++ cfe/trunk/lib/AST/Expr.cpp Tue Apr  6 16:38:20 2010
@@ -496,13 +496,15 @@
                                NestedNameSpecifier *qual,
                                SourceRange qualrange,
                                ValueDecl *memberdecl,
-                               NamedDecl *founddecl,
+                               DeclAccessPair founddecl,
                                SourceLocation l,
                                const TemplateArgumentListInfo *targs,
                                QualType ty) {
   std::size_t Size = sizeof(MemberExpr);
 
-  bool hasQualOrFound = (qual != 0 || founddecl != memberdecl);
+  bool hasQualOrFound = (qual != 0 ||
+                         founddecl.getDecl() != memberdecl ||
+                         founddecl.getAccess() != memberdecl->getAccess());
   if (hasQualOrFound)
     Size += sizeof(MemberNameQualifier);
 

Modified: cfe/trunk/lib/Sema/Lookup.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Lookup.h?rev=100562&r1=100561&r2=100562&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Lookup.h (original)
+++ cfe/trunk/lib/Sema/Lookup.h Tue Apr  6 16:38:20 2010
@@ -282,6 +282,18 @@
     NamingClass = Record;
   }
 
+  /// \brief Returns the base object type associated with this lookup;
+  /// important for [class.protected].  Most lookups do not have an
+  /// associated base object.
+  QualType getBaseObjectType() const {
+    return BaseObjectType;
+  }
+
+  /// \brief Sets the base object type for this lookup.
+  void setBaseObjectType(QualType T) {
+    BaseObjectType = T;
+  }
+
   /// \brief Add a declaration to these results with its natural access.
   /// Does not test the acceptance criteria.
   void addDecl(NamedDecl *D) {
@@ -550,6 +562,7 @@
   UnresolvedSet<8> Decls;
   CXXBasePaths *Paths;
   CXXRecordDecl *NamingClass;
+  QualType BaseObjectType;
 
   // Parameters.
   Sema &SemaRef;

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=100562&r1=100561&r2=100562&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Tue Apr  6 16:38:20 2010
@@ -315,20 +315,11 @@
     AccessedEntity(ASTContext &Context, 
                    MemberNonce _,
                    CXXRecordDecl *NamingClass,
-                   AccessSpecifier Access,
-                   NamedDecl *Target)
-      : Access(Access), IsMember(true), 
-        Target(Target), NamingClass(NamingClass),
-        Diag(0, Context.getDiagAllocator()) {
-    }
-
-    AccessedEntity(ASTContext &Context, 
-                   MemberNonce _,
-                   CXXRecordDecl *NamingClass,
-                   DeclAccessPair FoundDecl)
+                   DeclAccessPair FoundDecl,
+                   QualType BaseObjectType)
       : Access(FoundDecl.getAccess()), IsMember(true), 
         Target(FoundDecl.getDecl()), NamingClass(NamingClass),
-        Diag(0, Context.getDiagAllocator()) {
+        BaseObjectType(BaseObjectType), Diag(0, Context.getDiagAllocator()) {
     }
 
     AccessedEntity(ASTContext &Context, 
@@ -353,6 +344,10 @@
     CXXRecordDecl *getBaseClass() const { return cast<CXXRecordDecl>(Target); }
     CXXRecordDecl *getDerivedClass() const { return NamingClass; }
 
+    /// Retrieves the base object type, important when accessing
+    /// an instance member.
+    QualType getBaseObjectType() const { return BaseObjectType; }
+
     /// Sets a diagnostic to be performed.  The diagnostic is given
     /// four (additional) arguments:
     ///   %0 - 0 if the entity was private, 1 if protected
@@ -378,6 +373,7 @@
     bool IsMember;
     NamedDecl *Target;
     CXXRecordDecl *NamingClass;    
+    QualType BaseObjectType;
     PartialDiagnostic Diag;
   };
 
@@ -1254,10 +1250,10 @@
   FunctionDecl *ResolveSingleFunctionTemplateSpecialization(Expr *From);
 
   Expr *FixOverloadedFunctionReference(Expr *E,
-                                       NamedDecl *FoundDecl,
+                                       DeclAccessPair FoundDecl,
                                        FunctionDecl *Fn);
   OwningExprResult FixOverloadedFunctionReference(OwningExprResult, 
-                                                  NamedDecl *FoundDecl,
+                                                  DeclAccessPair FoundDecl,
                                                   FunctionDecl *Fn);
 
   void AddOverloadedCallCandidates(UnresolvedLookupExpr *ULE,

Modified: cfe/trunk/lib/Sema/SemaAccess.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaAccess.cpp?rev=100562&r1=100561&r2=100562&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaAccess.cpp (original)
+++ cfe/trunk/lib/Sema/SemaAccess.cpp Tue Apr  6 16:38:20 2010
@@ -22,6 +22,13 @@
 
 using namespace clang;
 
+/// A copy of Sema's enum without AR_delayed.
+enum AccessResult {
+  AR_accessible,
+  AR_inaccessible,
+  AR_dependent
+};
+
 /// SetMemberAccessSpecifier - Set the access specifier of a member.
 /// Returns true on error (when the previous member decl access specifier
 /// is different from the new member decl access specifier).
@@ -51,6 +58,20 @@
   return false;
 }
 
+static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) {
+  DeclContext *DC = D->getDeclContext();
+
+  // This can only happen at top: enum decls only "publish" their
+  // immediate members.
+  if (isa<EnumDecl>(DC))
+    DC = cast<EnumDecl>(DC)->getDeclContext();
+
+  CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(DC);
+  while (DeclaringClass->isAnonymousStructOrUnion())
+    DeclaringClass = cast<CXXRecordDecl>(DeclaringClass->getDeclContext());
+  return DeclaringClass;
+}
+
 namespace {
 struct EffectiveContext {
   EffectiveContext() : Inner(0), Dependent(false) {}
@@ -109,22 +130,145 @@
   llvm::SmallVector<CXXRecordDecl*, 4> Records;
   bool Dependent;
 };
+
+/// Like Sema's AccessedEntity, but kindly lets us scribble all over
+/// it.
+struct AccessTarget : public Sema::AccessedEntity {
+  AccessTarget(const Sema::AccessedEntity &Entity)
+    : AccessedEntity(Entity) {
+    initialize();
+  }
+    
+  AccessTarget(ASTContext &Context, 
+               MemberNonce _,
+               CXXRecordDecl *NamingClass,
+               DeclAccessPair FoundDecl,
+               QualType BaseObjectType)
+    : AccessedEntity(Context, Member, NamingClass, FoundDecl, BaseObjectType) {
+    initialize();
+  }
+
+  AccessTarget(ASTContext &Context, 
+               BaseNonce _,
+               CXXRecordDecl *BaseClass,
+               CXXRecordDecl *DerivedClass,
+               AccessSpecifier Access)
+    : AccessedEntity(Context, Base, BaseClass, DerivedClass, Access) {
+    initialize();
+  }
+
+  bool hasInstanceContext() const {
+    return HasInstanceContext;
+  }
+
+  class SavedInstanceContext {
+  public:
+    ~SavedInstanceContext() {
+      Target.HasInstanceContext = Has;
+    }
+
+  private:
+    friend class AccessTarget;
+    explicit SavedInstanceContext(AccessTarget &Target)
+      : Target(Target), Has(Target.HasInstanceContext) {}
+    AccessTarget &Target;
+    bool Has;
+  };
+
+  SavedInstanceContext saveInstanceContext() {
+    return SavedInstanceContext(*this);
+  }
+
+  void suppressInstanceContext() {
+    HasInstanceContext = false;
+  }
+
+  const CXXRecordDecl *resolveInstanceContext(Sema &S) const {
+    assert(HasInstanceContext);
+    if (CalculatedInstanceContext)
+      return InstanceContext;
+
+    CalculatedInstanceContext = true;
+    DeclContext *IC = S.computeDeclContext(getBaseObjectType());
+    InstanceContext = (IC ? cast<CXXRecordDecl>(IC)->getCanonicalDecl() : 0);
+    return InstanceContext;
+  }
+
+  const CXXRecordDecl *getDeclaringClass() const {
+    return DeclaringClass;
+  }
+
+private:
+  void initialize() {
+    HasInstanceContext = (isMemberAccess() &&
+                          !getBaseObjectType().isNull() &&
+                          getTargetDecl()->isCXXInstanceMember());
+    CalculatedInstanceContext = false;
+    InstanceContext = 0;
+
+    if (isMemberAccess())
+      DeclaringClass = FindDeclaringClass(getTargetDecl());
+    else
+      DeclaringClass = getBaseClass();
+    DeclaringClass = DeclaringClass->getCanonicalDecl();
+  }
+
+  bool HasInstanceContext : 1;
+  mutable bool CalculatedInstanceContext : 1;
+  mutable const CXXRecordDecl *InstanceContext;
+  const CXXRecordDecl *DeclaringClass;
+};
+
 }
 
-static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) {
-  DeclContext *DC = D->getDeclContext();
+/// 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) {
+  assert(Derived->getCanonicalDecl() == Derived);
+  assert(Target->getCanonicalDecl() == Target);
+
+  if (Derived == Target) return AR_accessible;
+
+  AccessResult OnFailure = AR_inaccessible;
+  llvm::SmallVector<const CXXRecordDecl*, 8> Queue; // actually a stack
+
+  while (true) {
+    for (CXXRecordDecl::base_class_const_iterator
+           I = Derived->bases_begin(), E = Derived->bases_end(); I != E; ++I) {
+
+      const CXXRecordDecl *RD;
+
+      QualType T = I->getType();
+      if (const RecordType *RT = T->getAs<RecordType>()) {
+        RD = cast<CXXRecordDecl>(RT->getDecl());
+      } else {
+        // It's possible for a base class to be the current
+        // instantiation of some enclosing template, but I'm guessing
+        // nobody will ever care that we just dependently delay here.
+        assert(T->isDependentType() && "non-dependent base wasn't a record?");
+        OnFailure = AR_dependent;
+        continue;
+      }
 
-  // This can only happen at top: enum decls only "publish" their
-  // immediate members.
-  if (isa<EnumDecl>(DC))
-    DC = cast<EnumDecl>(DC)->getDeclContext();
+      RD = RD->getCanonicalDecl();
+      if (RD == Target) return AR_accessible;
+      Queue.push_back(RD);
+    }
 
-  CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(DC);
-  while (DeclaringClass->isAnonymousStructOrUnion())
-    DeclaringClass = cast<CXXRecordDecl>(DeclaringClass->getDeclContext());
-  return DeclaringClass;
+    if (Queue.empty()) break;
+
+    Derived = Queue.back();
+    Queue.pop_back();
+  }
+
+  return OnFailure;
 }
 
+
 static bool MightInstantiateTo(Sema &S, DeclContext *Context,
                                DeclContext *Friend) {
   if (Friend == Context)
@@ -204,11 +348,11 @@
                             Friend->getTemplatedDecl());
 }
 
-static Sema::AccessResult MatchesFriend(Sema &S,
-                                        const EffectiveContext &EC,
-                                        const CXXRecordDecl *Friend) {
+static AccessResult MatchesFriend(Sema &S,
+                                  const EffectiveContext &EC,
+                                  const CXXRecordDecl *Friend) {
   if (EC.includesClass(Friend))
-    return Sema::AR_accessible;
+    return AR_accessible;
 
   if (EC.isDependent()) {
     CanQualType FriendTy
@@ -219,32 +363,32 @@
       CanQualType ContextTy
         = S.Context.getCanonicalType(S.Context.getTypeDeclType(*I));
       if (MightInstantiateTo(S, ContextTy, FriendTy))
-        return Sema::AR_dependent;
+        return AR_dependent;
     }
   }
 
-  return Sema::AR_inaccessible;
+  return AR_inaccessible;
 }
 
-static Sema::AccessResult MatchesFriend(Sema &S,
-                                        const EffectiveContext &EC,
-                                        CanQualType Friend) {
+static AccessResult MatchesFriend(Sema &S,
+                                  const EffectiveContext &EC,
+                                  CanQualType Friend) {
   if (const RecordType *RT = Friend->getAs<RecordType>())
     return MatchesFriend(S, EC, cast<CXXRecordDecl>(RT->getDecl()));
 
   // TODO: we can do better than this
   if (Friend->isDependentType())
-    return Sema::AR_dependent;
+    return AR_dependent;
 
-  return Sema::AR_inaccessible;
+  return AR_inaccessible;
 }
 
 /// Determines whether the given friend class template matches
 /// anything in the effective context.
-static Sema::AccessResult MatchesFriend(Sema &S,
-                                        const EffectiveContext &EC,
-                                        ClassTemplateDecl *Friend) {
-  Sema::AccessResult OnFailure = Sema::AR_inaccessible;
+static AccessResult MatchesFriend(Sema &S,
+                                  const EffectiveContext &EC,
+                                  ClassTemplateDecl *Friend) {
+  AccessResult OnFailure = AR_inaccessible;
 
   // Check whether the friend is the template of a class in the
   // context chain.
@@ -268,7 +412,7 @@
 
     // It's a match.
     if (Friend == CTD->getCanonicalDecl())
-      return Sema::AR_accessible;
+      return AR_accessible;
 
     // If the context isn't dependent, it can't be a dependent match.
     if (!EC.isDependent())
@@ -286,7 +430,7 @@
       continue;
 
     // Otherwise, it's a dependent match.
-    OnFailure = Sema::AR_dependent;
+    OnFailure = AR_dependent;
   }
 
   return OnFailure;
@@ -294,18 +438,18 @@
 
 /// Determines whether the given friend function matches anything in
 /// the effective context.
-static Sema::AccessResult MatchesFriend(Sema &S,
-                                        const EffectiveContext &EC,
-                                        FunctionDecl *Friend) {
-  Sema::AccessResult OnFailure = Sema::AR_inaccessible;
+static AccessResult MatchesFriend(Sema &S,
+                                  const EffectiveContext &EC,
+                                  FunctionDecl *Friend) {
+  AccessResult OnFailure = AR_inaccessible;
 
   for (llvm::SmallVectorImpl<FunctionDecl*>::const_iterator
          I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) {
     if (Friend == *I)
-      return Sema::AR_accessible;
+      return AR_accessible;
 
     if (EC.isDependent() && MightInstantiateTo(S, *I, Friend))
-      OnFailure = Sema::AR_dependent;
+      OnFailure = AR_dependent;
   }
 
   return OnFailure;
@@ -313,12 +457,12 @@
 
 /// Determines whether the given friend function template matches
 /// anything in the effective context.
-static Sema::AccessResult MatchesFriend(Sema &S,
-                                        const EffectiveContext &EC,
-                                        FunctionTemplateDecl *Friend) {
-  if (EC.Functions.empty()) return Sema::AR_inaccessible;
+static AccessResult MatchesFriend(Sema &S,
+                                  const EffectiveContext &EC,
+                                  FunctionTemplateDecl *Friend) {
+  if (EC.Functions.empty()) return AR_inaccessible;
 
-  Sema::AccessResult OnFailure = Sema::AR_inaccessible;
+  AccessResult OnFailure = AR_inaccessible;
 
   for (llvm::SmallVectorImpl<FunctionDecl*>::const_iterator
          I = EC.Functions.begin(), E = EC.Functions.end(); I != E; ++I) {
@@ -332,10 +476,10 @@
     FTD = FTD->getCanonicalDecl();
 
     if (Friend == FTD)
-      return Sema::AR_accessible;
+      return AR_accessible;
 
     if (EC.isDependent() && MightInstantiateTo(S, FTD, Friend))
-      OnFailure = Sema::AR_dependent;
+      OnFailure = AR_dependent;
   }
 
   return OnFailure;
@@ -343,9 +487,9 @@
 
 /// Determines whether the given friend declaration matches anything
 /// in the effective context.
-static Sema::AccessResult MatchesFriend(Sema &S,
-                                        const EffectiveContext &EC,
-                                        FriendDecl *FriendD) {
+static AccessResult MatchesFriend(Sema &S,
+                                  const EffectiveContext &EC,
+                                  FriendDecl *FriendD) {
   if (TypeSourceInfo *T = FriendD->getFriendType())
     return MatchesFriend(S, EC, T->getType()->getCanonicalTypeUnqualified());
 
@@ -367,10 +511,10 @@
   return MatchesFriend(S, EC, cast<FunctionDecl>(Friend));
 }
 
-static Sema::AccessResult GetFriendKind(Sema &S,
-                                        const EffectiveContext &EC,
-                                        const CXXRecordDecl *Class) {
-  Sema::AccessResult OnFailure = Sema::AR_inaccessible;
+static AccessResult GetFriendKind(Sema &S,
+                                  const EffectiveContext &EC,
+                                  const CXXRecordDecl *Class) {
+  AccessResult OnFailure = AR_inaccessible;
 
   // Okay, check friends.
   for (CXXRecordDecl::friend_iterator I = Class->friend_begin(),
@@ -378,18 +522,15 @@
     FriendDecl *Friend = *I;
 
     switch (MatchesFriend(S, EC, Friend)) {
-    case Sema::AR_accessible:
-      return Sema::AR_accessible;
+    case AR_accessible:
+      return AR_accessible;
 
-    case Sema::AR_inaccessible:
-      break;
+    case AR_inaccessible:
+      continue;
 
-    case Sema::AR_dependent:
-      OnFailure = Sema::AR_dependent;
+    case AR_dependent:
+      OnFailure = AR_dependent;
       break;
-
-    case Sema::AR_delayed:
-      llvm_unreachable("cannot get delayed answer from MatchesFriend");
     }
   }
 
@@ -397,16 +538,19 @@
   return OnFailure;
 }
 
-static Sema::AccessResult HasAccess(Sema &S,
-                                    const EffectiveContext &EC,
-                                    const CXXRecordDecl *NamingClass,
-                                    AccessSpecifier Access) {
+static AccessResult HasAccess(Sema &S,
+                              const EffectiveContext &EC,
+                              const CXXRecordDecl *NamingClass,
+                              AccessSpecifier Access,
+                              const AccessTarget &Target) {
   assert(NamingClass->getCanonicalDecl() == NamingClass &&
          "declaration should be canonicalized before being passed here");
 
-  if (Access == AS_public) return Sema::AR_accessible;
+  if (Access == AS_public) return AR_accessible;
   assert(Access == AS_private || Access == AS_protected);
 
+  AccessResult OnFailure = AR_inaccessible;
+
   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
@@ -414,16 +558,75 @@
     const CXXRecordDecl *ECRecord = *I;
 
     // [B2] and [M2]
-    if (ECRecord == NamingClass)
-      return Sema::AR_accessible;
+    if (Access == AS_private) {
+      if (ECRecord == NamingClass)
+        return AR_accessible;
 
     // [B3] and [M3]
-    if (Access == AS_protected &&
-        ECRecord->isDerivedFrom(const_cast<CXXRecordDecl*>(NamingClass)))
-      return Sema::AR_accessible;
+    } else {
+      assert(Access == AS_protected);
+      switch (IsDerivedFromInclusive(ECRecord, NamingClass)) {
+      case AR_accessible: break;
+      case AR_inaccessible: continue;
+      case AR_dependent: OnFailure = AR_dependent; continue;
+      }
+
+      if (!Target.hasInstanceContext())
+        return AR_accessible;
+
+      const CXXRecordDecl *InstanceContext = Target.resolveInstanceContext(S);
+      if (!InstanceContext) {
+        OnFailure = AR_dependent;
+        continue;
+      }
+
+      // C++ [class.protected]p1:
+      //   An additional access check beyond those described earlier in
+      //   [class.access] is applied when a non-static data member or
+      //   non-static member function is a protected member of its naming
+      //   class.  As described earlier, access to a protected member is
+      //   granted because the reference occurs in a friend or member of
+      //   some class C.  If the access is to form a pointer to member,
+      //   the nested-name-specifier shall name C or a class derived from
+      //   C. All other accesses involve a (possibly implicit) object
+      //   expression. In this case, the class of the object expression
+      //   shall be C or a class derived from C.
+      //
+      // We interpret this as a restriction on [M3].  Most of the
+      // conditions are encoded by not having any instance context.
+      switch (IsDerivedFromInclusive(InstanceContext, ECRecord)) {
+      case AR_accessible: return AR_accessible;
+      case AR_inaccessible: continue;
+      case AR_dependent: OnFailure = AR_dependent; continue;
+      }
+    }
   }
 
-  return GetFriendKind(S, EC, NamingClass);
+  if (!NamingClass->hasFriends())
+    return OnFailure;
+
+  // Don't consider friends if we're under the [class.protected]
+  // restriction, above.
+  if (Access == AS_protected && Target.hasInstanceContext()) {
+    const CXXRecordDecl *InstanceContext = Target.resolveInstanceContext(S);
+    if (!InstanceContext) return AR_dependent;
+
+    switch (IsDerivedFromInclusive(InstanceContext, NamingClass)) {
+    case AR_accessible: break;
+    case AR_inaccessible: return OnFailure;
+    case AR_dependent: return AR_dependent;
+    }
+  }
+
+  switch (GetFriendKind(S, EC, NamingClass)) {
+  case AR_accessible: return AR_accessible;
+  case AR_inaccessible: return OnFailure;
+  case AR_dependent: return AR_dependent;
+  }
+
+  // Silence bogus warnings
+  llvm_unreachable("impossible friendship kind");
+  return OnFailure;
 }
 
 /// Finds the best path from the naming class to the declaring class,
@@ -479,17 +682,21 @@
 ///
 /// 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
+/// \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,
-                                 CXXRecordDecl *Derived,
-                                 CXXRecordDecl *Base,
+                                 AccessTarget &Target,
                                  AccessSpecifier FinalAccess,
                                  CXXBasePaths &Paths) {
   // Derive the paths to the desired base.
-  bool isDerived = Derived->isDerivedFrom(Base, Paths);
+  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;
 
@@ -502,6 +709,7 @@
   // 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;
@@ -519,16 +727,23 @@
         break;
       }
 
+      const CXXRecordDecl *NC = I->Class->getCanonicalDecl();
+
       AccessSpecifier BaseAccess = I->Base->getAccessSpecifier();
       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:
+
+      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;
-      case Sema::AR_delayed:
-        llvm_unreachable("friend resolution is never delayed"); break;
       }
     }
 
@@ -561,46 +776,36 @@
 /// to become inaccessible.
 static void DiagnoseAccessPath(Sema &S,
                                const EffectiveContext &EC,
-                               const Sema::AccessedEntity &Entity) {
+                               AccessTarget &Entity) {
   AccessSpecifier Access = Entity.getAccess();
-  CXXRecordDecl *NamingClass = Entity.getNamingClass();
+  const 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();
+  NamedDecl *D = (Entity.isMemberAccess() ? Entity.getTargetDecl() : 0);
+  const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass();
 
   // 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.
   if (D && (Access == D->getAccess() || D->getAccess() == AS_private)) {
-    switch (HasAccess(S, EC, DeclaringClass, D->getAccess())) {
-    case Sema::AR_inaccessible: {
+    switch (HasAccess(S, EC, DeclaringClass, D->getAccess(), Entity)) {
+    case AR_inaccessible: {
       S.Diag(D->getLocation(), diag::note_access_natural)
         << (unsigned) (Access == AS_protected)
         << /*FIXME: not implicitly*/ 0;
       return;
     }
 
-    case Sema::AR_accessible: break;
+    case AR_accessible: break;
 
-    case Sema::AR_dependent:
-    case Sema::AR_delayed:
-      llvm_unreachable("dependent/delayed not allowed");
+    case AR_dependent:
+      llvm_unreachable("can't diagnose dependent access failures");
       return;
     }
   }
 
   CXXBasePaths Paths;
-  CXXBasePath &Path = *FindBestPath(S, EC, NamingClass, DeclaringClass,
-                                    AS_public, Paths);
+  CXXBasePath &Path = *FindBestPath(S, EC, Entity, AS_public, Paths);
 
   CXXBasePath::iterator I = Path.end(), E = Path.begin();
   while (I != E) {
@@ -615,12 +820,10 @@
       continue;
 
     switch (GetFriendKind(S, EC, I->Class)) {
-    case Sema::AR_accessible: continue;
-    case Sema::AR_inaccessible: break;
-
-    case Sema::AR_dependent:
-    case Sema::AR_delayed:
-      llvm_unreachable("dependent friendship, should not be diagnosing");
+    case AR_accessible: continue;
+    case AR_inaccessible: break;
+    case AR_dependent:
+      llvm_unreachable("can't diagnose dependent access failures");
     }
 
     // Check whether this base specifier is the tighest point
@@ -649,17 +852,10 @@
 
 static void DiagnoseBadAccess(Sema &S, SourceLocation Loc,
                               const EffectiveContext &EC,
-                              const Sema::AccessedEntity &Entity) {
+                              AccessTarget &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();
-  }
+  const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass();
+  NamedDecl *D = (Entity.isMemberAccess() ? Entity.getTargetDecl() : 0);
 
   S.Diag(Loc, Entity.getDiag())
     << (Entity.getAccess() == AS_protected)
@@ -671,9 +867,9 @@
 
 /// 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) {
+static AccessResult IsAccessible(Sema &S,
+                                 const EffectiveContext &EC,
+                                 AccessTarget &Entity) {
   // Determine the actual naming class.
   CXXRecordDecl *NamingClass = Entity.getNamingClass();
   while (NamingClass->isAnonymousStructOrUnion())
@@ -686,10 +882,10 @@
   // 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.
+  // common forms of privileged access.
   if (UnprivilegedAccess != AS_none) {
-    switch (HasAccess(S, EC, NamingClass, UnprivilegedAccess)) {
-    case Sema::AR_dependent:
+    switch (HasAccess(S, EC, NamingClass, UnprivilegedAccess, Entity)) {
+    case 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
@@ -697,23 +893,14 @@
       // 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;
+      return 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");
+    case AR_accessible: return AR_accessible;
+    case AR_inaccessible: break;
     }
   }
 
-  // Determine the declaring class.
-  CXXRecordDecl *DeclaringClass;
-  if (Entity.isMemberAccess()) {
-    DeclaringClass = FindDeclaringClass(Entity.getTargetDecl());
-  } else {
-    DeclaringClass = Entity.getBaseClass();
-  }
-  DeclaringClass = DeclaringClass->getCanonicalDecl();
+  AccessTarget::SavedInstanceContext _ = Entity.saveInstanceContext();
 
   // We lower member accesses to base accesses by pretending that the
   // member is a base class of its declaring class.
@@ -723,43 +910,44 @@
     // Determine if the declaration is accessible from EC when named
     // in its declaring class.
     NamedDecl *Target = Entity.getTargetDecl();
+    const CXXRecordDecl *DeclaringClass = Entity.getDeclaringClass();
 
     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");
+    switch (HasAccess(S, EC, DeclaringClass, FinalAccess, Entity)) {
+    case AR_accessible:
+      FinalAccess = AS_public;
+      break;
+    case AR_inaccessible: break;
+    case AR_dependent: return AR_dependent; // see above
     }
 
     if (DeclaringClass == NamingClass)
-      return (FinalAccess == AS_public
-              ? Sema::AR_accessible
-              : Sema::AR_inaccessible);
+      return (FinalAccess == AS_public ? AR_accessible : AR_inaccessible);
+
+    Entity.suppressInstanceContext();
   } else {
     FinalAccess = AS_public;
   }
 
-  assert(DeclaringClass != NamingClass);
+  assert(Entity.getDeclaringClass() != NamingClass);
 
   // Append the declaration's access if applicable.
   CXXBasePaths Paths;
-  CXXBasePath *Path = FindBestPath(S, EC, NamingClass, DeclaringClass,
-                                   FinalAccess, Paths);
+  CXXBasePath *Path = FindBestPath(S, EC, Entity, FinalAccess, Paths);
   if (!Path)
-    return Sema::AR_dependent;
+    return AR_dependent;
 
   assert(Path->Access <= UnprivilegedAccess &&
          "access along best path worse than direct?");
   if (Path->Access == AS_public)
-    return Sema::AR_accessible;
-  return Sema::AR_inaccessible;
+    return AR_accessible;
+  return AR_inaccessible;
 }
 
-static void DelayAccess(Sema &S,
-                        const EffectiveContext &EC,
-                        SourceLocation Loc,
-                        const Sema::AccessedEntity &Entity) {
+static void DelayDependentAccess(Sema &S,
+                                 const EffectiveContext &EC,
+                                 SourceLocation Loc,
+                                 const AccessTarget &Entity) {
   assert(EC.isDependent() && "delaying non-dependent access");
   DeclContext *DC = EC.getInnerContext();
   assert(DC->isDependentContext() && "delaying non-dependent access");
@@ -769,48 +957,38 @@
                               Entity.getAccess(),
                               Entity.getTargetDecl(),
                               Entity.getNamingClass(),
+                              Entity.getBaseObjectType(),
                               Entity.getDiag());
 }
 
 /// Checks access to an entity from the given effective context.
-static Sema::AccessResult CheckEffectiveAccess(Sema &S,
-                                               const EffectiveContext &EC,
-                                               SourceLocation Loc,
-                                         const Sema::AccessedEntity &Entity) {
+static AccessResult CheckEffectiveAccess(Sema &S,
+                                         const EffectiveContext &EC,
+                                         SourceLocation Loc,
+                                         AccessTarget &Entity) {
   assert(Entity.getAccess() != AS_public && "called for public access!");
 
   switch (IsAccessible(S, EC, Entity)) {
-  case Sema::AR_dependent:
-    DelayAccess(S, EC, Loc, Entity);
-    return Sema::AR_dependent;
-
-  case Sema::AR_delayed:
-    llvm_unreachable("IsAccessible cannot contextually delay");
+  case AR_dependent:
+    DelayDependentAccess(S, EC, Loc, Entity);
+    return AR_dependent;
 
-  case Sema::AR_inaccessible:
+  case AR_inaccessible:
     if (!Entity.isQuiet())
       DiagnoseBadAccess(S, Loc, EC, Entity);
-    return Sema::AR_inaccessible;
+    return AR_inaccessible;
 
-  case Sema::AR_accessible:
-    break;
-  }
-
-  // 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]
-    }
+  case AR_accessible:
+    return AR_accessible;
   }
 
-  return Sema::AR_accessible;
+  // silence unnecessary warning
+  llvm_unreachable("invalid access result");
+  return AR_accessible;
 }
 
 static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
-                                      const Sema::AccessedEntity &Entity) {
+                                      AccessTarget &Entity) {
   // If the access path is public, it's accessible everywhere.
   if (Entity.getAccess() == AS_public)
     return Sema::AR_accessible;
@@ -825,8 +1003,14 @@
     return Sema::AR_delayed;
   }
 
-  return CheckEffectiveAccess(S, EffectiveContext(S.CurContext),
-                              Loc, Entity);
+  EffectiveContext EC(S.CurContext);
+  switch (CheckEffectiveAccess(S, EC, Loc, Entity)) {
+  case AR_accessible: return Sema::AR_accessible;
+  case AR_inaccessible: return Sema::AR_inaccessible;
+  case AR_dependent: return Sema::AR_dependent;
+  }
+  llvm_unreachable("falling off end");
+  return Sema::AR_accessible;
 }
 
 void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx) {
@@ -834,7 +1018,9 @@
   // declaration.
   EffectiveContext EC(Ctx->getDeclContext());
 
-  if (CheckEffectiveAccess(*this, EC, DD.Loc, DD.getAccessData()))
+  AccessTarget Target(DD.getAccessData());
+
+  if (CheckEffectiveAccess(*this, EC, DD.Loc, Target) == ::AR_inaccessible)
     DD.Triggered = true;
 }
 
@@ -851,19 +1037,28 @@
   if (!TargetD) return;
 
   if (DD.isAccessToMember()) {
-    AccessedEntity Entity(Context,
-                          AccessedEntity::Member,
-                          cast<CXXRecordDecl>(NamingD),
-                          Access,
-                          cast<NamedDecl>(TargetD));
+    CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(NamingD);
+    NamedDecl *TargetDecl = cast<NamedDecl>(TargetD);
+    QualType BaseObjectType = DD.getAccessBaseObjectType();
+    if (!BaseObjectType.isNull()) {
+      BaseObjectType = SubstType(BaseObjectType, TemplateArgs, Loc,
+                                 DeclarationName());
+      if (BaseObjectType.isNull()) return;
+    }
+
+    AccessTarget Entity(Context,
+                        AccessTarget::Member,
+                        NamingClass,
+                        DeclAccessPair::make(TargetDecl, Access),
+                        BaseObjectType);
     Entity.setDiag(DD.getDiagnostic());
     CheckAccess(*this, Loc, Entity);
   } else {
-    AccessedEntity Entity(Context,
-                          AccessedEntity::Base,
-                          cast<CXXRecordDecl>(TargetD),
-                          cast<CXXRecordDecl>(NamingD),
-                          Access);
+    AccessTarget Entity(Context,
+                        AccessTarget::Base,
+                        cast<CXXRecordDecl>(TargetD),
+                        cast<CXXRecordDecl>(NamingD),
+                        Access);
     Entity.setDiag(DD.getDiagnostic());
     CheckAccess(*this, Loc, Entity);
   }
@@ -876,8 +1071,8 @@
       Found.getAccess() == AS_public)
     return AR_accessible;
 
-  AccessedEntity Entity(Context, AccessedEntity::Member, E->getNamingClass(), 
-                        Found);
+  AccessTarget Entity(Context, AccessTarget::Member, E->getNamingClass(), 
+                      Found, QualType());
   Entity.setDiag(diag::err_access) << E->getSourceRange();
 
   return CheckAccess(*this, E->getNameLoc(), Entity);
@@ -891,8 +1086,12 @@
       Found.getAccess() == AS_public)
     return AR_accessible;
 
-  AccessedEntity Entity(Context, AccessedEntity::Member, E->getNamingClass(), 
-                        Found);
+  QualType BaseType = E->getBaseType();
+  if (E->isArrow())
+    BaseType = BaseType->getAs<PointerType>()->getPointeeType();
+
+  AccessTarget Entity(Context, AccessTarget::Member, E->getNamingClass(),
+                      Found, BaseType);
   Entity.setDiag(diag::err_access) << E->getSourceRange();
 
   return CheckAccess(*this, E->getMemberLoc(), Entity);
@@ -910,8 +1109,9 @@
     return AR_accessible;
 
   CXXRecordDecl *NamingClass = Dtor->getParent();
-  AccessedEntity Entity(Context, AccessedEntity::Member, NamingClass,
-                        DeclAccessPair::make(Dtor, Access));
+  AccessTarget Entity(Context, AccessTarget::Member, NamingClass,
+                      DeclAccessPair::make(Dtor, Access),
+                      QualType());
   Entity.setDiag(PDiag); // TODO: avoid copy
 
   return CheckAccess(*this, Loc, Entity);
@@ -926,8 +1126,9 @@
     return AR_accessible;
 
   CXXRecordDecl *NamingClass = Constructor->getParent();
-  AccessedEntity Entity(Context, AccessedEntity::Member, NamingClass,
-                        DeclAccessPair::make(Constructor, Access));
+  AccessTarget Entity(Context, AccessTarget::Member, NamingClass,
+                      DeclAccessPair::make(Constructor, Access),
+                      QualType());
   Entity.setDiag(diag::err_access_ctor);
 
   return CheckAccess(*this, UseLoc, Entity);
@@ -944,8 +1145,9 @@
     return AR_accessible;
 
   CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(Target->getDeclContext());
-  AccessedEntity Entity(Context, AccessedEntity::Member, NamingClass,
-                        DeclAccessPair::make(Target, Access));
+  AccessTarget Entity(Context, AccessTarget::Member, NamingClass,
+                      DeclAccessPair::make(Target, Access),
+                      QualType());
   Entity.setDiag(Diag);
   return CheckAccess(*this, UseLoc, Entity);
 }
@@ -961,7 +1163,8 @@
       Found.getAccess() == AS_public)
     return AR_accessible;
 
-  AccessedEntity Entity(Context, AccessedEntity::Member, NamingClass, Found);
+  AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found,
+                      QualType());
   Entity.setDiag(diag::err_access)
     << PlacementRange;
 
@@ -982,7 +1185,8 @@
   assert(RT && "found member operator but object expr not of record type");
   CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl());
 
-  AccessedEntity Entity(Context, AccessedEntity::Member, NamingClass, Found);
+  AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found,
+                      ObjectExpr->getType());
   Entity.setDiag(diag::err_access)
     << ObjectExpr->getSourceRange()
     << (ArgExpr ? ArgExpr->getSourceRange() : SourceRange());
@@ -1008,14 +1212,14 @@
   assert(DC && DC->isRecord() && "scope did not resolve to record");
   CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(DC);
 
-  AccessedEntity Entity(Context, AccessedEntity::Member, NamingClass, Found);
+  AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found,
+                      Context.getTypeDeclType(NamingClass));
   Entity.setDiag(diag::err_access)
     << Ovl->getSourceRange();
 
   return CheckAccess(*this, Ovl->getNameLoc(), Entity);
 }
 
-
 /// Checks access for a hierarchy conversion.
 ///
 /// \param IsBaseToDerived whether this is a base-to-derived conversion (true)
@@ -1042,13 +1246,20 @@
   BaseD = cast<CXXRecordDecl>(Base->getAs<RecordType>()->getDecl());
   DerivedD = cast<CXXRecordDecl>(Derived->getAs<RecordType>()->getDecl());
 
-  AccessedEntity Entity(Context, AccessedEntity::Base, BaseD, DerivedD, 
-                        Path.Access);
+  AccessTarget Entity(Context, AccessTarget::Base, BaseD, DerivedD, 
+                      Path.Access);
   if (DiagID)
     Entity.setDiag(DiagID) << Derived << Base;
 
-  if (ForceUnprivileged)
-    return CheckEffectiveAccess(*this, EffectiveContext(), AccessLoc, Entity);
+  if (ForceUnprivileged) {
+    switch (CheckEffectiveAccess(*this, EffectiveContext(),
+                                 AccessLoc, Entity)) {
+    case ::AR_accessible: return Sema::AR_accessible;
+    case ::AR_inaccessible: return Sema::AR_inaccessible;
+    case ::AR_dependent: return Sema::AR_dependent;
+    }
+    llvm_unreachable("unexpected result from CheckEffectiveAccess");
+  }
   return CheckAccess(*this, AccessLoc, Entity);
 }
 
@@ -1060,9 +1271,9 @@
 
   for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) {
     if (I.getAccess() != AS_public) {
-      AccessedEntity Entity(Context, AccessedEntity::Member,
-                            R.getNamingClass(),
-                            I.getPair());
+      AccessTarget Entity(Context, AccessedEntity::Member,
+                          R.getNamingClass(), I.getPair(),
+                          R.getBaseObjectType());
       Entity.setDiag(diag::err_access);
 
       CheckAccess(*this, R.getNameLoc(), Entity);

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=100562&r1=100561&r2=100562&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Tue Apr  6 16:38:20 2010
@@ -769,24 +769,6 @@
   return true;
 }
 
-/// Determines if this is an instance member of a class.
-static bool IsInstanceMember(NamedDecl *D) {
-  assert(D->isCXXClassMember() &&
-         "checking whether non-member is instance member");
-
-  if (isa<FieldDecl>(D)) return true;
-
-  if (isa<CXXMethodDecl>(D))
-    return !cast<CXXMethodDecl>(D)->isStatic();
-
-  if (isa<FunctionTemplateDecl>(D)) {
-    D = cast<FunctionTemplateDecl>(D)->getTemplatedDecl();
-    return !cast<CXXMethodDecl>(D)->isStatic();
-  }
-
-  return false;
-}
-
 enum IMAKind {
   /// The reference is definitely not an instance member access.
   IMA_Static,
@@ -846,8 +828,8 @@
   bool hasNonInstance = false;
   llvm::SmallPtrSet<CXXRecordDecl*, 4> Classes;
   for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) {
-    NamedDecl *D = (*I)->getUnderlyingDecl();
-    if (IsInstanceMember(D)) {
+    NamedDecl *D = *I;
+    if (D->isCXXInstanceMember()) {
       CXXRecordDecl *R = cast<CXXRecordDecl>(D->getDeclContext());
 
       // If this is a member of an anonymous record, move out to the
@@ -1517,8 +1499,8 @@
 /// \brief Build a MemberExpr AST node.
 static MemberExpr *BuildMemberExpr(ASTContext &C, Expr *Base, bool isArrow,
                                    const CXXScopeSpec &SS, ValueDecl *Member,
-                                   NamedDecl *FoundDecl, SourceLocation Loc,
-                                   QualType Ty,
+                                   DeclAccessPair FoundDecl,
+                                   SourceLocation Loc, QualType Ty,
                           const TemplateArgumentListInfo *TemplateArgs = 0) {
   NestedNameSpecifier *Qualifier = 0;
   SourceRange QualifierRange;
@@ -2545,7 +2527,7 @@
   for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) {
     // If this is an implicit member reference and we find a
     // non-instance member, it's not an error.
-    if (!BaseExpr && !IsInstanceMember((*I)->getUnderlyingDecl()))
+    if (!BaseExpr && !(*I)->isCXXInstanceMember())
       return false;
 
     // Note that we use the DC of the decl, not the underlying decl.
@@ -2682,6 +2664,7 @@
     assert(BaseType->isPointerType());
     BaseType = BaseType->getAs<PointerType>()->getPointeeType();
   }
+  R.setBaseObjectType(BaseType);
 
   NestedNameSpecifier *Qualifier =
     static_cast<NestedNameSpecifier*>(SS.getScopeRep());
@@ -2742,7 +2725,7 @@
   }
 
   assert(R.isSingleResult());
-  NamedDecl *FoundDecl = *R.begin();
+  DeclAccessPair FoundDecl = R.begin().getPair();
   NamedDecl *MemberDecl = R.getFoundDecl();
 
   // FIXME: diagnose the presence of template arguments now.
@@ -2756,7 +2739,7 @@
   // Handle the implicit-member-access case.
   if (!BaseExpr) {
     // If this is not an instance member, convert to a non-member access.
-    if (!IsInstanceMember(MemberDecl))
+    if (!MemberDecl->isCXXInstanceMember())
       return BuildDeclarationNameExpr(SS, R.getNameLoc(), MemberDecl);
 
     SourceLocation Loc = R.getNameLoc();

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=100562&r1=100561&r2=100562&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Tue Apr  6 16:38:20 2010
@@ -6003,7 +6003,7 @@
   
   MemberExpr *MemExpr;
   CXXMethodDecl *Method = 0;
-  NamedDecl *FoundDecl = 0;
+  DeclAccessPair FoundDecl;
   NestedNameSpecifier *Qualifier = 0;
   if (isa<MemberExpr>(NakedMemExpr)) {
     MemExpr = cast<MemberExpr>(NakedMemExpr);
@@ -6486,7 +6486,7 @@
 /// perhaps a '&' around it). We have resolved the overloaded function
 /// to the function declaration Fn, so patch up the expression E to
 /// refer (possibly indirectly) to Fn. Returns the new expr.
-Expr *Sema::FixOverloadedFunctionReference(Expr *E, NamedDecl *Found,
+Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
                                            FunctionDecl *Fn) {
   if (ParenExpr *PE = dyn_cast<ParenExpr>(E)) {
     Expr *SubExpr = FixOverloadedFunctionReference(PE->getSubExpr(),
@@ -6619,7 +6619,7 @@
 }
 
 Sema::OwningExprResult Sema::FixOverloadedFunctionReference(OwningExprResult E, 
-                                                            NamedDecl *Found,
+                                                          DeclAccessPair Found,
                                                             FunctionDecl *Fn) {
   return Owned(FixOverloadedFunctionReference((Expr *)E.get(), Found, Fn));
 }

Modified: cfe/trunk/lib/Sema/SemaType.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=100562&r1=100561&r2=100562&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaType.cpp (original)
+++ cfe/trunk/lib/Sema/SemaType.cpp Tue Apr  6 16:38:20 2010
@@ -2029,7 +2029,10 @@
     // function template specialization wherever deduction cannot occur.
     if (FunctionDecl *Specialization
         = ResolveSingleFunctionTemplateSpecialization(E)) {
-      E = FixOverloadedFunctionReference(E, Specialization, Specialization);
+      // The access doesn't really matter in this case.
+      DeclAccessPair Found = DeclAccessPair::make(Specialization,
+                                                  Specialization->getAccess());
+      E = FixOverloadedFunctionReference(E, Found, Specialization);
       if (!E)
         return QualType();      
     } else {
@@ -2049,7 +2052,10 @@
     // function template specialization wherever deduction cannot occur.
     if (FunctionDecl *Specialization
           = ResolveSingleFunctionTemplateSpecialization(E)) {
-      E = FixOverloadedFunctionReference(E, Specialization, Specialization);
+      // The access doesn't really matter in this case.
+      DeclAccessPair Found = DeclAccessPair::make(Specialization,
+                                                  Specialization->getAccess());
+      E = FixOverloadedFunctionReference(E, Found, Specialization);
       if (!E)
         return QualType();      
     } else {

Added: cfe/trunk/test/CXX/class.access/class.protected/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class.access/class.protected/p1.cpp?rev=100562&view=auto
==============================================================================
--- cfe/trunk/test/CXX/class.access/class.protected/p1.cpp (added)
+++ cfe/trunk/test/CXX/class.access/class.protected/p1.cpp Tue Apr  6 16:38:20 2010
@@ -0,0 +1,387 @@
+// RUN: %clang_cc1 -fsyntax-only -faccess-control -verify %s
+
+namespace test0 {
+  class A {
+    protected: int x; // expected-note 3 {{declared}}
+    static int sx; // expected-note 3 {{declared}}
+  };
+  class B : public A {
+  };
+  class C : protected A { // expected-note {{declared}}
+  };
+  class D : private B { // expected-note 3 {{constrained}}
+  };
+
+  void test(A &a) {
+    (void) a.x; // expected-error {{'x' is a protected member}}
+    (void) a.sx; // expected-error {{'sx' is a protected member}}
+  }
+  void test(B &b) {
+    (void) b.x; // expected-error {{'x' is a protected member}}
+    (void) b.sx; // expected-error {{'sx' is a protected member}}
+  }
+  void test(C &c) {
+    (void) c.x; // expected-error {{'x' is a protected member}} expected-error {{protected base class}}
+    (void) c.sx; // expected-error {{'sx' is a protected member}}
+  }
+  void test(D &d) {
+    (void) d.x; // expected-error {{'x' is a private member}} expected-error {{private base class}}
+    (void) d.sx; // expected-error {{'sx' is a private member}}
+  }
+}
+
+namespace test1 {
+  class A {
+    protected: int x;
+    static int sx;
+    static void test(A&);
+  };
+  class B : public A {
+    static void test(B&);
+  };
+  class C : protected A {
+    static void test(C&);
+  };
+  class D : private B {
+    static void test(D&);
+  };
+
+  void A::test(A &a) {
+    (void) a.x;
+    (void) a.sx;
+  }
+  void B::test(B &b) {
+    (void) b.x;
+    (void) b.sx;
+  }
+  void C::test(C &c) {
+    (void) c.x;
+    (void) c.sx;
+  }
+  void D::test(D &d) {
+    (void) d.x;
+    (void) d.sx;
+  }
+}
+
+namespace test2 {
+  class A {
+    protected: int x; // expected-note 3 {{declared}}
+    static int sx;
+    static void test(A&);
+  };
+  class B : public A {
+    static void test(A&);
+  };
+  class C : protected A {
+    static void test(A&);
+  };
+  class D : private B {
+    static void test(A&);
+  };
+
+  void A::test(A &a) {
+    (void) a.x;
+    (void) a.sx;
+  }
+  void B::test(A &a) {
+    (void) a.x; // expected-error {{'x' is a protected member}}
+    (void) a.sx;
+  }
+  void C::test(A &a) {
+    (void) a.x; // expected-error {{'x' is a protected member}}
+    (void) a.sx;
+  }
+  void D::test(A &a) {
+    (void) a.x; // expected-error {{'x' is a protected member}}
+    (void) a.sx;
+  }
+}
+
+namespace test3 {
+  class B;
+  class A {
+    protected: int x; // expected-note {{declared}}
+    static int sx;
+    static void test(B&);
+  };
+  class B : public A {
+    static void test(B&);
+  };
+  class C : protected A {
+    static void test(B&);
+  };
+  class D : private B {
+    static void test(B&);
+  };
+
+  void A::test(B &b) {
+    (void) b.x;
+    (void) b.sx;
+  }
+  void B::test(B &b) {
+    (void) b.x;
+    (void) b.sx;
+  }
+  void C::test(B &b) {
+    (void) b.x; // expected-error {{'x' is a protected member}}
+    (void) b.sx;
+  }
+  void D::test(B &b) {
+    (void) b.x;
+    (void) b.sx;
+  }
+}
+
+namespace test4 {
+  class C;
+  class A {
+    protected: int x; // expected-note 2 {{declared}}
+    static int sx;
+    static void test(C&);
+  };
+  class B : public A {
+    static void test(C&);
+  };
+  class C : protected A { // expected-note 4 {{constrained}} expected-note 3 {{declared}}
+    static void test(C&);
+  };
+  class D : private B {
+    static void test(C&);
+  };
+
+  void A::test(C &c) {
+    (void) c.x;  // expected-error {{'x' is a protected member}} \
+                 // expected-error {{protected base class}}
+    (void) c.sx; // expected-error {{'sx' is a protected member}}
+  }
+  void B::test(C &c) {
+    (void) c.x;  // expected-error {{'x' is a protected member}} \
+                 // expected-error {{protected base class}}
+    (void) c.sx; // expected-error {{'sx' is a protected member}}
+  }
+  void C::test(C &c) {
+    (void) c.x;
+    (void) c.sx;
+  }
+  void D::test(C &c) {
+    (void) c.x;  // expected-error {{'x' is a protected member}} \
+                 // expected-error {{protected base class}}
+    (void) c.sx; // expected-error {{'sx' is a protected member}}
+  }
+}
+
+namespace test5 {
+  class D;
+  class A {
+    protected: int x;
+    static int sx;
+    static void test(D&);
+  };
+  class B : public A {
+    static void test(D&);
+  };
+  class C : protected A {
+    static void test(D&);
+  };
+  class D : private B { // expected-note 9 {{constrained}}
+    static void test(D&);
+  };
+
+  void A::test(D &d) {
+    (void) d.x;  // expected-error {{'x' is a private member}} \
+                 // expected-error {{cannot cast}}
+    (void) d.sx; // expected-error {{'sx' is a private member}}
+  }
+  void B::test(D &d) {
+    (void) d.x;  // expected-error {{'x' is a private member}} \
+                 // expected-error {{cannot cast}}
+    (void) d.sx; // expected-error {{'sx' is a private member}}
+  }
+  void C::test(D &d) {
+    (void) d.x;  // expected-error {{'x' is a private member}} \
+                 // expected-error {{cannot cast}}
+    (void) d.sx; // expected-error {{'sx' is a private member}}
+  }
+  void D::test(D &d) {
+    (void) d.x;
+    (void) d.sx;
+  }
+}
+
+namespace test6 {
+  class Static {};
+  class A {
+  protected:
+    void foo(int); // expected-note 3 {{declared}}
+    void foo(long);
+    static void foo(Static);
+
+    static void test(A&);
+  };
+  class B : public A {
+    static void test(A&);
+  };
+  class C : protected A {
+    static void test(A&);
+  };
+  class D : private B {
+    static void test(A&);
+  };
+
+  void A::test(A &a) {
+    a.foo(10);
+    a.foo(Static());
+  }
+  void B::test(A &a) {
+    a.foo(10); // expected-error {{'foo' is a protected member}}
+    a.foo(Static());
+  }
+  void C::test(A &a) {
+    a.foo(10); // expected-error {{'foo' is a protected member}}
+    a.foo(Static());
+  }
+  void D::test(A &a) {
+    a.foo(10); // expected-error {{'foo' is a protected member}}
+    a.foo(Static());
+  }
+}
+
+namespace test7 {
+  class Static {};
+  class A {
+    protected:
+    void foo(int); // expected-note 3 {{declared}}
+    void foo(long);
+    static void foo(Static);
+
+    static void test();
+  };
+  class B : public A {
+    static void test();
+  };
+  class C : protected A {
+    static void test();
+  };
+  class D : private B {
+    static void test();
+  };
+
+  void A::test() {
+    void (A::*x)(int) = &A::foo;
+    void (*sx)(Static) = &A::foo;
+  }
+  void B::test() {
+    void (A::*x)(int) = &A::foo; // expected-error {{'foo' is a protected member}}
+    void (*sx)(Static) = &A::foo;
+  }
+  void C::test() {
+    void (A::*x)(int) = &A::foo; // expected-error {{'foo' is a protected member}}
+    void (*sx)(Static) = &A::foo;
+  }
+  void D::test() {
+    void (A::*x)(int) = &A::foo; // expected-error {{'foo' is a protected member}}
+    void (*sx)(Static) = &A::foo;
+  }
+}
+
+namespace test8 {
+  class Static {};
+  class A {
+    protected:
+    void foo(int); // expected-note 3 {{declared}}
+    void foo(long);
+    static void foo(Static);
+
+    static void test();
+  };
+  class B : public A {
+    static void test();
+  };
+  class C : protected A {
+    static void test();
+  };
+  class D : private B {
+    static void test();
+  };
+  void call(void (A::*)(int));
+  void calls(void (*)(Static));
+
+  void A::test() {
+    call(&A::foo);
+    calls(&A::foo);
+  }
+  void B::test() {
+    call(&A::foo); // expected-error {{'foo' is a protected member}}
+    calls(&A::foo);
+  }
+  void C::test() {
+    call(&A::foo); // expected-error {{'foo' is a protected member}}
+    calls(&A::foo);
+  }
+  void D::test() {
+    call(&A::foo); // expected-error {{'foo' is a protected member}}
+    calls(&A::foo);
+  }
+}
+
+namespace test9 {
+  class A {
+  protected: int foo(); // expected-note 8 {{declared}}
+  };
+
+  class B : public A {
+    friend class D;
+  };
+
+  class C : protected B { // expected-note {{declared}} \
+                          // expected-note 6 {{constrained}}
+  };
+
+  class D : public A {
+    static void test(A &a) {
+      a.foo(); // expected-error {{'foo' is a protected member}}
+      a.A::foo(); // expected-error {{'foo' is a protected member}}
+      a.B::foo();
+      a.C::foo(); // expected-error {{'foo' is a protected member}}
+    }
+
+    static void test(B &b) {
+      b.foo();
+      b.A::foo(); // expected-error {{'foo' is a protected member}}
+      b.B::foo();
+      b.C::foo(); // expected-error {{'foo' is a protected member}}
+    }
+
+    static void test(C &c) {
+      c.foo();    // expected-error {{'foo' is a protected member}} \
+                  // expected-error {{cannot cast}}
+      c.A::foo(); // expected-error {{'foo' is a protected member}} \
+                  // expected-error {{'A' is a protected member}} \
+                  // expected-error {{cannot cast}}
+      c.B::foo(); // expected-error {{'B' is a protected member}} \
+                  // expected-error {{cannot cast}}
+      c.C::foo(); // expected-error {{'foo' is a protected member}} \
+                  // expected-error {{cannot cast}}
+    }
+
+    static void test(D &d) {
+      d.foo();
+      d.A::foo();
+      d.B::foo();
+      d.C::foo(); // expected-error {{'foo' is a protected member}}
+    }
+  };
+}
+
+namespace test10 {
+  template<typename T> class A {
+  protected:
+    int foo();
+    int foo() const;
+
+    ~A() { foo(); }
+  };
+
+  template class A<int>;
+}





More information about the cfe-commits mailing list