[cfe-commits] r98710 - in /cfe/trunk: lib/Sema/SemaAccess.cpp test/CXX/class.access/class.access.nest/p1.cpp

John McCall rjmccall at apple.com
Tue Mar 16 21:58:56 PDT 2010


Author: rjmccall
Date: Tue Mar 16 23:58:56 2010
New Revision: 98710

URL: http://llvm.org/viewvc/llvm-project?rev=98710&view=rev
Log:
Grant nested classes the access privileges of their enclosing classes.


Added:
    cfe/trunk/test/CXX/class.access/class.access.nest/p1.cpp
Modified:
    cfe/trunk/lib/Sema/SemaAccess.cpp

Modified: cfe/trunk/lib/Sema/SemaAccess.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaAccess.cpp?rev=98710&r1=98709&r2=98710&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaAccess.cpp (original)
+++ cfe/trunk/lib/Sema/SemaAccess.cpp Tue Mar 16 23:58:56 2010
@@ -52,7 +52,7 @@
 
 namespace {
 struct EffectiveContext {
-  EffectiveContext() : Record(0), Function(0) {}
+  EffectiveContext() : Function(0) {}
 
   explicit EffectiveContext(DeclContext *DC) {
     if (isa<FunctionDecl>(DC)) {
@@ -60,18 +60,28 @@
       DC = Function->getDeclContext();
     } else
       Function = 0;
-    
-    if (isa<CXXRecordDecl>(DC))
-      Record = cast<CXXRecordDecl>(DC)->getCanonicalDecl();
-    else
-      Record = 0;
+
+    // C++ [class.access.nest]p1:
+    //   A nested class is a member and as such has the same access
+    //   rights as any other member.
+    // C++ [class.access]p2:
+    //   A member of a class can also access all the names to which
+    //   the class has access.
+    // This implies that the privileges of nesting are transitive.
+    while (isa<CXXRecordDecl>(DC)) {
+      CXXRecordDecl *Record = cast<CXXRecordDecl>(DC)->getCanonicalDecl();
+      Records.push_back(Record);
+      DC = Record->getDeclContext();
+    }
   }
 
-  bool isClass(const CXXRecordDecl *R) const {
-    return R->getCanonicalDecl() == Record;
+  bool includesClass(const CXXRecordDecl *R) const {
+    R = R->getCanonicalDecl();
+    return std::find(Records.begin(), Records.end(), R)
+             != Records.end();
   }
 
-  CXXRecordDecl *Record;
+  llvm::SmallVector<CXXRecordDecl*, 4> Records;
   FunctionDecl *Function;
 };
 }
@@ -87,26 +97,32 @@
                                         const EffectiveContext &EC,
                                         const CXXRecordDecl *Class) {
   // A class always has access to its own members.
-  if (EC.isClass(Class))
+  if (EC.includesClass(Class))
     return Sema::AR_accessible;
 
+  Sema::AccessResult OnFailure = Sema::AR_inaccessible;
+
   // Okay, check friends.
   for (CXXRecordDecl::friend_iterator I = Class->friend_begin(),
          E = Class->friend_end(); I != E; ++I) {
     FriendDecl *Friend = *I;
 
     if (Type *T = Friend->getFriendType()) {
-      if (EC.Record &&
-          S.Context.hasSameType(QualType(T, 0),
-                                S.Context.getTypeDeclType(EC.Record)))
-        return Sema::AR_accessible;
+      CanQualType CT = T->getCanonicalTypeUnqualified();
+      if (const RecordType *RT = CT->getAs<RecordType>())
+        if (EC.includesClass(cast<CXXRecordDecl>(RT->getDecl())))
+          return Sema::AR_accessible;
     } else {
       NamedDecl *D
         = cast<NamedDecl>(Friend->getFriendDecl()->getCanonicalDecl());
 
       // The decl pointers in EC have been canonicalized, so pointer
       // equality is sufficient.
-      if (D == EC.Function || D == EC.Record)
+      if (D == EC.Function)
+        return Sema::AR_accessible;
+
+      if (isa<CXXRecordDecl>(D) &&
+          EC.includesClass(cast<CXXRecordDecl>(D)))
         return Sema::AR_accessible;
     }
 
@@ -114,7 +130,7 @@
   }
 
   // That's it, give up.
-  return Sema::AR_inaccessible;
+  return OnFailure;
 }
 
 /// Finds the best path from the naming class to the declaring class,
@@ -350,51 +366,38 @@
                                                SourceLocation Loc,
                                          Sema::AccessedEntity const &Entity) {
   AccessSpecifier Access = Entity.getAccess();
-  assert(Access != AS_public);
+  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())
-    // This should be guaranteed by the fact that the decl has
-    // non-public access.  If not, we should make it guaranteed!
     NamingClass = cast<CXXRecordDecl>(NamingClass->getParent());
 
-  if (!EC.Record) {
-    TryElevateAccess(S, EC, Entity, Access);
-    if (Access == AS_public) return Sema::AR_accessible;
-
-    if (!Entity.isQuiet())
-      DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity);
-    return Sema::AR_inaccessible;
-  }
-
-  // White-list accesses from within the declaring class.
-  if (Access != AS_none && EC.isClass(NamingClass))
+  // 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;
-  
-  // If the access is worse than 'protected', try to promote to it using
-  // friend declarations.
-  bool TriedElevation = false;
-  if (Access != AS_protected) {
-    TryElevateAccess(S, EC, Entity, Access);
-    if (Access == AS_public) return Sema::AR_accessible;
-    TriedElevation = true;
-  }
+
+  // Try to elevate access.
+  // FIXME: delay if elevation was dependent?
+  // TODO: on some code, it might be better to do the protected check
+  // without trying to elevate first.
+  TryElevateAccess(S, EC, Entity, Access);
+  if (Access == AS_public) return Sema::AR_accessible;
 
   // Protected access.
   if (Access == AS_protected) {
     // FIXME: implement [class.protected]p1
-    if (EC.Record->isDerivedFrom(NamingClass))
-      return Sema::AR_accessible;
+    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;
 
-    // FIXME: delay dependent classes
+    // FIXME: delay if we can't decide class derivation yet.
   }
 
-  // We're about to reject;  one last chance to promote access.
-  if (!TriedElevation) {
-    TryElevateAccess(S, EC, Entity, Access);
-    if (Access == AS_public) return Sema::AR_accessible;
-  }
-    
   // Okay, that's it, reject it.
   if (!Entity.isQuiet())
     DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity);

Added: cfe/trunk/test/CXX/class.access/class.access.nest/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class.access/class.access.nest/p1.cpp?rev=98710&view=auto
==============================================================================
--- cfe/trunk/test/CXX/class.access/class.access.nest/p1.cpp (added)
+++ cfe/trunk/test/CXX/class.access/class.access.nest/p1.cpp Tue Mar 16 23:58:56 2010
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -fsyntax-only -faccess-control -verify %s
+
+// Derived from GNU's std::string
+namespace test0 {
+  class A {
+    struct B {
+      unsigned long length;
+    };
+    struct C : B {
+      static const unsigned long max_length;
+    };
+  };
+  
+  const unsigned long A::C::max_length = sizeof(B);
+}
+
+// Example from the standard.
+namespace test1 {
+  class E {
+    int x;
+    class B {};
+
+    class I {
+      B b;
+      int y; // expected-note {{declared private here}}
+      void f(E* p, int i) {
+        p->x = i;
+      }
+    };
+
+    int g(I* p) { return p->y; } // expected-error {{'y' is a private member of 'test1::E::I'}}
+  };
+}





More information about the cfe-commits mailing list