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

John McCall rjmccall at apple.com
Sat Aug 28 00:56:00 PDT 2010


Author: rjmccall
Date: Sat Aug 28 02:56:00 2010
New Revision: 112358

URL: http://llvm.org/viewvc/llvm-project?rev=112358&view=rev
Log:
When checking access control for an instance member access on
an object of type I, if the current access target is protected
when named in a class N, consider the friends of the classes P
where I <= P <= N and where a notional member of N would be
non-forbidden in P.


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

Modified: cfe/trunk/lib/Sema/SemaAccess.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaAccess.cpp?rev=112358&r1=112357&r2=112358&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaAccess.cpp (original)
+++ cfe/trunk/lib/Sema/SemaAccess.cpp Sat Aug 28 02:56:00 2010
@@ -564,6 +564,123 @@
   return OnFailure;
 }
 
+namespace {
+
+/// A helper class for checking for a friend which will grant access
+/// to a protected instance member.
+struct ProtectedFriendContext {
+  Sema &S;
+  const EffectiveContext &EC;
+  const CXXRecordDecl *NamingClass;
+  bool CheckDependent;
+  bool EverDependent;
+
+  /// The path down to the current base class.
+  llvm::SmallVector<const CXXRecordDecl*, 20> CurPath;
+
+  ProtectedFriendContext(Sema &S, const EffectiveContext &EC,
+                         const CXXRecordDecl *InstanceContext,
+                         const CXXRecordDecl *NamingClass)
+    : S(S), EC(EC), NamingClass(NamingClass),
+      CheckDependent(InstanceContext->isDependentContext() ||
+                     NamingClass->isDependentContext()),
+      EverDependent(false) {}
+
+  /// Check everything in the current path for friendship.
+  bool checkFriendshipAlongPath() {
+    for (llvm::SmallVectorImpl<const CXXRecordDecl*>::iterator
+           I = CurPath.begin(), E = CurPath.end(); I != E; ++I) {
+      switch (GetFriendKind(S, EC, *I)) {
+      case AR_accessible:   return true;
+      case AR_inaccessible: continue;
+      case AR_dependent:    EverDependent = true; continue;
+      }
+    }
+    return false;
+  }
+
+  /// Perform a search starting at the given class.
+  bool findFriendship(const CXXRecordDecl *Cur) {
+    CurPath.push_back(Cur);
+
+    // If we ever reach the naming class, check the current path for
+    // friendship.  We can also stop recursing because we obviously
+    // won't find the naming class there again.
+    if (Cur == NamingClass) {
+      bool Result = checkFriendshipAlongPath();
+      CurPath.pop_back();
+      return Result;
+    }
+
+    if (CheckDependent && MightInstantiateTo(Cur, NamingClass))
+      EverDependent = true;
+
+    // Recurse into the base classes.
+    for (CXXRecordDecl::base_class_const_iterator
+           I = Cur->bases_begin(), E = Cur->bases_end(); I != E; ++I) {
+
+      // If this base specifier has private access, and this isn't the
+      // first step in the derivation chain, then the base does not
+      // have natural access along this derivation path and we should
+      // ignore it.
+      if (I->getAccessSpecifier() == AS_private && CurPath.size() != 1)
+          continue;
+
+      const CXXRecordDecl *RD;
+
+      QualType T = I->getType();
+      if (const RecordType *RT = T->getAs<RecordType>()) {
+        RD = cast<CXXRecordDecl>(RT->getDecl());
+      } else if (const InjectedClassNameType *IT
+                   = T->getAs<InjectedClassNameType>()) {
+        RD = IT->getDecl();
+      } else {
+        assert(T->isDependentType() && "non-dependent base wasn't a record?");
+        EverDependent = true;
+        continue;
+      }
+
+      // Recurse.  We don't need to clean up if this returns true.
+      if (findFriendship(RD->getCanonicalDecl())) return true;
+    }
+
+    CurPath.pop_back();
+    return false;
+  }
+};
+}
+
+/// Search for a class P that EC is a friend of, under the constraint
+///   InstanceContext <= P <= NamingClass
+/// and with the additional restriction that a protected member of
+/// NamingClass would have some natural access in P.
+///
+/// That second condition isn't actually quite right: the condition in
+/// the standard is whether the target would have some natural access
+/// in P.  The difference is that the target might be more accessible
+/// along some path not passing through NamingClass.  Allowing that
+/// introduces two problems:
+///   - It breaks encapsulation because you can suddenly access a
+///     forbidden base class's members by subclassing it elsewhere.
+///   - It makes access substantially harder to compute because it
+///     breaks the hill-climbing algorithm: knowing that the target is
+///     accessible in some base class would no longer let you change
+///     the question solely to whether the base class is accessible,
+///     because the original target might have been more accessible
+///     because of crazy subclassing.
+/// So we don't implement that.
+static AccessResult GetProtectedFriendKind(Sema &S, const EffectiveContext &EC,
+                                           const CXXRecordDecl *InstanceContext,
+                                           const CXXRecordDecl *NamingClass) {
+  assert(InstanceContext->getCanonicalDecl() == InstanceContext);
+  assert(NamingClass->getCanonicalDecl() == NamingClass);
+
+  ProtectedFriendContext PRC(S, EC, InstanceContext, NamingClass);
+  if (PRC.findFriendship(InstanceContext)) return AR_accessible;
+  if (PRC.EverDependent) return AR_dependent;
+  return AR_inaccessible;
+}
+
 static AccessResult HasAccess(Sema &S,
                               const EffectiveContext &EC,
                               const CXXRecordDecl *NamingClass,
@@ -631,20 +748,25 @@
     }
   }
 
-  if (!NamingClass->hasFriends())
-    return OnFailure;
-
-  // Don't consider friends if we're under the [class.protected]
-  // restriction, above.
+  // [M3] and [B3] say that, if the target is protected in N, we grant
+  // access if the access occurs in a friend or member of some class P
+  // that's a subclass of N and where the target has some natural
+  // access in P.  The 'member' aspect is easy to handle because P
+  // would necessarily be one of the effective-context records, and we
+  // address that above.  The 'friend' aspect is completely ridiculous
+  // to implement because there are no restrictions at all on P
+  // *unless* the [class.protected] restriction applies.  If it does,
+  // however, we should ignore whether the naming class is a friend,
+  // and instead rely on whether any potential P is a friend.
   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;
+    switch (GetProtectedFriendKind(S, EC, InstanceContext, NamingClass)) {
+    case AR_accessible: return AR_accessible;
     case AR_inaccessible: return OnFailure;
     case AR_dependent: return AR_dependent;
     }
+    llvm::unreachable("impossible friendship kind");
   }
 
   switch (GetFriendKind(S, EC, NamingClass)) {

Modified: 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=112358&r1=112357&r2=112358&view=diff
==============================================================================
--- cfe/trunk/test/CXX/class.access/class.protected/p1.cpp (original)
+++ cfe/trunk/test/CXX/class.access/class.protected/p1.cpp Sat Aug 28 02:56:00 2010
@@ -329,8 +329,7 @@
 
 namespace test9 {
   class A { // expected-note {{member is declared here}}
-  protected: int foo(); // expected-note 8 {{declared}} \
-    // expected-note {{member is declared here}}
+  protected: int foo(); // expected-note 7 {{declared}}
   };
 
   class B : public A { // expected-note {{member is declared here}}
@@ -338,7 +337,7 @@
   };
 
   class C : protected B { // expected-note {{declared}} \
-                          // expected-note 6 {{constrained}}
+                          // expected-note 9 {{constrained}}
   };
 
   class D : public A {
@@ -351,7 +350,7 @@
 
     static void test(B &b) {
       b.foo();
-      b.A::foo(); // expected-error {{'foo' is a protected member}}
+      b.A::foo();
       b.B::foo();
       b.C::foo(); // expected-error {{'foo' is a protected member}}
     }
@@ -359,8 +358,7 @@
     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}} \
+      c.A::foo(); // 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}}
@@ -388,3 +386,22 @@
 
   template class A<int>;
 }
+
+// rdar://problem/8360285: class.protected friendship
+namespace test11 {
+  class A {
+  protected:
+    int foo();
+  };
+
+  class B : public A {
+    friend class C;
+  };
+
+  class C {
+    void test() {
+      B b;
+      b.A::foo();
+    }
+  };
+}





More information about the cfe-commits mailing list