[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