[cfe-commits] r154248 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td include/clang/Sema/Sema.h lib/Sema/SemaAccess.cpp lib/Sema/SemaDeclCXX.cpp lib/Sema/SemaExprCXX.cpp lib/Sema/SemaInit.cpp test/CXX/class.access/class.protected/p1.cpp test/CXX/class.access/p4.cpp

John McCall rjmccall at apple.com
Fri Apr 6 20:04:20 PDT 2012


Author: rjmccall
Date: Fri Apr  6 22:04:20 2012
New Revision: 154248

URL: http://llvm.org/viewvc/llvm-project?rev=154248&view=rev
Log:
Fix several problems with protected access control:
  - The [class.protected] restriction is non-trivial for any instance
    member, even if the access lacks an object (for example, if it's
    a pointer-to-member constant).  In this case, it is equivalent to
    requiring the naming class to equal the context class.
  - The [class.protected] restriction applies to accesses to constructors
    and destructors.  A protected constructor or destructor can only be
    used to create or destroy a base subobject, as a direct result.
  - Several places were dropping or misapplying object information.

The standard could really be much clearer about what the object type is
supposed to be in some of these accesses.  Usually it's easy enough to
find a reasonable answer, but still, the standard makes a very confident
statement about accesses to instance members only being possible in
either pointer-to-member literals or member access expressions, which
just completely ignores concepts like constructor and destructor
calls, using declarations, unevaluated field references, etc.

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaAccess.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaExprCXX.cpp
    cfe/trunk/lib/Sema/SemaInit.cpp
    cfe/trunk/test/CXX/class.access/class.protected/p1.cpp
    cfe/trunk/test/CXX/class.access/p4.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=154248&r1=154247&r2=154248&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Apr  6 22:04:20 2012
@@ -884,8 +884,13 @@
 def note_access_constrained_by_path : Note<
   "constrained by %select{|implicitly }1%select{private|protected}0"
   " inheritance here">;
-def note_access_protected_restricted : Note<
-  "object type %select{|%1 }0must derive from context type %2">;
+def note_access_protected_restricted_noobject : Note<
+  "must name member using the type of the current context %0">;
+def note_access_protected_restricted_ctordtor : Note<
+  "protected %select{constructor|destructor}0 can only be used to "
+  "%select{construct|destroy}0 a base class subobject">;
+def note_access_protected_restricted_object : Note<
+  "can only access this member on an object of type %0">;
 def warn_cxx98_compat_sfinae_access_control : Warning<
   "substitution failure due to access control is incompatible with C++98">,
   InGroup<CXX98Compat>, DefaultIgnore, NoSFINAE;

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=154248&r1=154247&r2=154248&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Fri Apr  6 22:04:20 2012
@@ -4105,11 +4105,13 @@
                                       bool IsCopyBindingRefToTemp = false);
   AccessResult CheckConstructorAccess(SourceLocation Loc,
                                       CXXConstructorDecl *D,
+                                      const InitializedEntity &Entity,
                                       AccessSpecifier Access,
-                                      PartialDiagnostic PD);
+                                      const PartialDiagnostic &PDiag);
   AccessResult CheckDestructorAccess(SourceLocation Loc,
                                      CXXDestructorDecl *Dtor,
-                                     const PartialDiagnostic &PDiag);
+                                     const PartialDiagnostic &PDiag,
+                                     QualType objectType = QualType());
   AccessResult CheckDirectMemberAccess(SourceLocation Loc,
                                        NamedDecl *D,
                                        const PartialDiagnostic &PDiag);

Modified: cfe/trunk/lib/Sema/SemaAccess.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaAccess.cpp?rev=154248&r1=154247&r2=154248&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaAccess.cpp (original)
+++ cfe/trunk/lib/Sema/SemaAccess.cpp Fri Apr  6 22:04:20 2012
@@ -165,6 +165,10 @@
     initialize();
   }
 
+  bool isInstanceMember() const {
+    return (isMemberAccess() && getTargetDecl()->isCXXInstanceMember());
+  }
+
   bool hasInstanceContext() const {
     return HasInstanceContext;
   }
@@ -671,18 +675,25 @@
 }
 
 /// Search for a class P that EC is a friend of, under the constraint
-///   InstanceContext <= P <= NamingClass
+///   InstanceContext <= P
+/// if InstanceContext exists, or else
+///   NamingClass <= P
 /// and with the additional restriction that a protected member of
-/// NamingClass would have some natural access in P.
+/// NamingClass would have some natural access in P, which implicitly
+/// imposes the constraint that P <= NamingClass.
 ///
-/// 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
+/// This isn't quite the condition laid out in the standard.
+/// Instead of saying that a notional protected member of NamingClass
+/// would have to have some natural access in P, it says the actual
+/// target has to have some natural access in P, which opens up the
+/// possibility that the target (which is not necessarily a member
+/// of NamingClass) might be more accessible along some path not
+/// passing through it.  That's really a bad idea, though, because it
 /// 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
+///   - Most importantly, it breaks encapsulation because you can
+///     access a forbidden base class's members by directly subclassing
+///     it elsewhere.
+///   - It also 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,
@@ -692,9 +703,15 @@
 static AccessResult GetProtectedFriendKind(Sema &S, const EffectiveContext &EC,
                                            const CXXRecordDecl *InstanceContext,
                                            const CXXRecordDecl *NamingClass) {
-  assert(InstanceContext->getCanonicalDecl() == InstanceContext);
+  assert(InstanceContext == 0 ||
+         InstanceContext->getCanonicalDecl() == InstanceContext);
   assert(NamingClass->getCanonicalDecl() == NamingClass);
 
+  // If we don't have an instance context, our constraints give us
+  // that NamingClass <= P <= NamingClass, i.e. P == NamingClass.
+  // This is just the usual friendship check.
+  if (!InstanceContext) return GetFriendKind(S, EC, NamingClass);
+
   ProtectedFriendContext PRC(S, EC, InstanceContext, NamingClass);
   if (PRC.findFriendship(InstanceContext)) return AR_accessible;
   if (PRC.EverDependent) return AR_dependent;
@@ -737,15 +754,6 @@
       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
@@ -758,8 +766,49 @@
       //   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.
+      // We interpret this as a restriction on [M3].
+
+      // In this part of the code, 'C' is just our context class ECRecord.
+      
+      // These rules are different if we don't have an instance context.
+      if (!Target.hasInstanceContext()) {
+        // If it's not an instance member, these restrictions don't apply.
+        if (!Target.isInstanceMember()) return AR_accessible;
+
+        // If it's an instance member, use the pointer-to-member rule
+        // that the naming class has to be derived from the effective
+        // context.
+
+        // Despite the standard's confident wording, there is a case
+        // where you can have an instance member that's neither in a
+        // pointer-to-member expression nor in a member access:  when
+        // it names a field in an unevaluated context that can't be an
+        // implicit member.  Pending clarification, we just apply the
+        // same naming-class restriction here.
+        //   FIXME: we're probably not correctly adding the
+        //   protected-member restriction when we retroactively convert
+        //   an expression to being evaluated.
+
+        // We know that ECRecord derives from NamingClass.  The
+        // restriction says to check whether NamingClass derives from
+        // ECRecord, but that's not really necessary: two distinct
+        // classes can't be recursively derived from each other.  So
+        // along this path, we just need to check whether the classes
+        // are equal.
+        if (NamingClass == ECRecord) return AR_accessible;
+
+        // Otherwise, this context class tells us nothing;  on to the next.
+        continue;
+      }
+
+      assert(Target.isInstanceMember());
+
+      const CXXRecordDecl *InstanceContext = Target.resolveInstanceContext(S);
+      if (!InstanceContext) {
+        OnFailure = AR_dependent;
+        continue;
+      }
+
       switch (IsDerivedFromInclusive(InstanceContext, ECRecord)) {
       case AR_accessible: return AR_accessible;
       case AR_inaccessible: continue;
@@ -778,9 +827,14 @@
   // *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;
+  if (Access == AS_protected && Target.isInstanceMember()) {
+    // Compute the instance context if possible.
+    const CXXRecordDecl *InstanceContext = 0;
+    if (Target.hasInstanceContext()) {
+      InstanceContext = Target.resolveInstanceContext(S);
+      if (!InstanceContext) return AR_dependent;
+    }
+
     switch (GetProtectedFriendKind(S, EC, InstanceContext, NamingClass)) {
     case AR_accessible: return AR_accessible;
     case AR_inaccessible: return OnFailure;
@@ -950,31 +1004,46 @@
 static bool TryDiagnoseProtectedAccess(Sema &S, const EffectiveContext &EC,
                                        AccessTarget &Target) {
   // Only applies to instance accesses.
-  if (!Target.hasInstanceContext())
+  if (!Target.isInstanceMember())
     return false;
+
   assert(Target.isMemberAccess());
-  NamedDecl *D = Target.getTargetDecl();
 
-  const CXXRecordDecl *DeclaringClass = Target.getDeclaringClass();
-  DeclaringClass = DeclaringClass->getCanonicalDecl();
+  const CXXRecordDecl *NamingClass = Target.getNamingClass();
+  NamingClass = NamingClass->getCanonicalDecl();
 
   for (EffectiveContext::record_iterator
          I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) {
     const CXXRecordDecl *ECRecord = *I;
-    switch (IsDerivedFromInclusive(ECRecord, DeclaringClass)) {
+    switch (IsDerivedFromInclusive(ECRecord, NamingClass)) {
     case AR_accessible: break;
     case AR_inaccessible: continue;
     case AR_dependent: continue;
     }
 
     // The effective context is a subclass of the declaring class.
-    // If that class isn't a superclass of the instance context,
-    // then the [class.protected] restriction applies.
+    // Check whether the [class.protected] restriction is limiting
+    // access.
 
     // To get this exactly right, this might need to be checked more
     // holistically;  it's not necessarily the case that gaining
     // access here would grant us access overall.
 
+    NamedDecl *D = Target.getTargetDecl();
+
+    // If we don't have an instance context, [class.protected] says the
+    // naming class has to equal the context class.
+    if (!Target.hasInstanceContext()) {
+      // If it does, the restriction doesn't apply.
+      if (NamingClass == ECRecord) continue;
+
+      // TODO: it would be great to have a fixit here, since this is
+      // such an obvious error.
+      S.Diag(D->getLocation(), diag::note_access_protected_restricted_noobject)
+        << S.Context.getTypeDeclType(ECRecord);
+      return true;
+    }
+
     const CXXRecordDecl *InstanceContext = Target.resolveInstanceContext(S);
     assert(InstanceContext && "diagnosing dependent access");
 
@@ -982,12 +1051,25 @@
     case AR_accessible: continue;
     case AR_dependent: continue;
     case AR_inaccessible:
-      S.Diag(D->getLocation(), diag::note_access_protected_restricted)
-        << (InstanceContext != Target.getNamingClass()->getCanonicalDecl())
-        << S.Context.getTypeDeclType(InstanceContext)
-        << S.Context.getTypeDeclType(ECRecord);
+      break;
+    }
+
+    // Okay, the restriction seems to be what's limiting us.
+
+    // Use a special diagnostic for constructors and destructors.
+    if (isa<CXXConstructorDecl>(D) || isa<CXXDestructorDecl>(D) ||
+        (isa<FunctionTemplateDecl>(D) &&
+         isa<CXXConstructorDecl>(
+                cast<FunctionTemplateDecl>(D)->getTemplatedDecl()))) {
+      S.Diag(D->getLocation(), diag::note_access_protected_restricted_ctordtor)
+        << isa<CXXDestructorDecl>(D);
       return true;
     }
+
+    // Otherwise, use the generic diagnostic.
+    S.Diag(D->getLocation(), diag::note_access_protected_restricted_object)
+      << S.Context.getTypeDeclType(ECRecord);
+    return true;
   }
 
   return false;
@@ -1427,7 +1509,8 @@
 
 Sema::AccessResult Sema::CheckDestructorAccess(SourceLocation Loc,
                                                CXXDestructorDecl *Dtor,
-                                               const PartialDiagnostic &PDiag) {
+                                               const PartialDiagnostic &PDiag,
+                                               QualType ObjectTy) {
   if (!getLangOpts().AccessControl)
     return AR_accessible;
 
@@ -1437,9 +1520,11 @@
     return AR_accessible;
 
   CXXRecordDecl *NamingClass = Dtor->getParent();
+  if (ObjectTy.isNull()) ObjectTy = Context.getTypeDeclType(NamingClass);
+
   AccessTarget Entity(Context, AccessTarget::Member, NamingClass,
                       DeclAccessPair::make(Dtor, Access),
-                      QualType());
+                      ObjectTy);
   Entity.setDiag(PDiag); // TODO: avoid copy
 
   return CheckAccess(*this, Loc, Entity);
@@ -1451,14 +1536,9 @@
                                                 const InitializedEntity &Entity,
                                                 AccessSpecifier Access,
                                                 bool IsCopyBindingRefToTemp) {
-  if (!getLangOpts().AccessControl ||
-      Access == AS_public)
+  if (!getLangOpts().AccessControl || Access == AS_public)
     return AR_accessible;
 
-  CXXRecordDecl *NamingClass = Constructor->getParent();
-  AccessTarget AccessEntity(Context, AccessTarget::Member, NamingClass,
-                            DeclAccessPair::make(Constructor, Access),
-                            QualType());
   PartialDiagnostic PD(PDiag());
   switch (Entity.getKind()) {
   default:
@@ -1490,26 +1570,38 @@
 
   }
 
-  return CheckConstructorAccess(UseLoc, Constructor, Access, PD);
+  return CheckConstructorAccess(UseLoc, Constructor, Entity, Access, PD);
 }
 
 /// Checks access to a constructor.
 Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc,
                                                 CXXConstructorDecl *Constructor,
+                                                const InitializedEntity &Entity,
                                                 AccessSpecifier Access,
-                                                PartialDiagnostic PD) {
+                                                const PartialDiagnostic &PD) {
   if (!getLangOpts().AccessControl ||
       Access == AS_public)
     return AR_accessible;
 
   CXXRecordDecl *NamingClass = Constructor->getParent();
+
+  // Initializing a base sub-object is an instance method call on an
+  // object of the derived class.  Otherwise, we have an instance method
+  // call on an object of the constructed type.
+  CXXRecordDecl *ObjectClass;
+  if (Entity.getKind() == InitializedEntity::EK_Base) {
+    ObjectClass = cast<CXXConstructorDecl>(CurContext)->getParent();
+  } else {
+    ObjectClass = NamingClass;
+  }
+
   AccessTarget AccessEntity(Context, AccessTarget::Member, NamingClass,
                             DeclAccessPair::make(Constructor, Access),
-                            QualType());
+                            Context.getTypeDeclType(ObjectClass));
   AccessEntity.setDiag(PD);
 
   return CheckAccess(*this, UseLoc, AccessEntity);
-}
+} 
 
 /// Checks direct (i.e. non-inherited) access to an arbitrary class
 /// member.
@@ -1583,7 +1675,7 @@
   CXXRecordDecl *NamingClass = Ovl->getNamingClass();
 
   AccessTarget Entity(Context, AccessTarget::Member, NamingClass, Found,
-                      Context.getTypeDeclType(NamingClass));
+                      /*no instance context*/ QualType());
   Entity.setDiag(diag::err_access)
     << Ovl->getSourceRange();
 

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=154248&r1=154247&r2=154248&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Fri Apr  6 22:04:20 2012
@@ -3344,7 +3344,8 @@
     CheckDestructorAccess(Base->getLocStart(), Dtor,
                           PDiag(diag::err_access_dtor_base)
                             << Base->getType()
-                            << Base->getSourceRange());
+                            << Base->getSourceRange(),
+                          Context.getTypeDeclType(ClassDecl));
     
     MarkFunctionReferenced(Location, const_cast<CXXDestructorDecl*>(Dtor));
     DiagnoseUseOfDecl(Dtor, Location);
@@ -6277,6 +6278,13 @@
   if (!IsInstantiation)
     R.setHideTags(false);
 
+  // For the purposes of this lookup, we have a base object type
+  // equal to that of the current context.
+  if (CurContext->isRecord()) {
+    R.setBaseObjectType(
+                   Context.getTypeDeclType(cast<CXXRecordDecl>(CurContext)));
+  }
+
   LookupQualifiedName(R, LookupContext);
 
   if (R.empty()) {

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=154248&r1=154247&r2=154248&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Fri Apr  6 22:04:20 2012
@@ -2276,8 +2276,9 @@
                                   CastLoc, ConstructorArgs))
       return ExprError();
 
-    S.CheckConstructorAccess(CastLoc, Constructor, Constructor->getAccess(),
-                             S.PDiag(diag::err_access_ctor));
+    S.CheckConstructorAccess(CastLoc, Constructor,
+                             InitializedEntity::InitializeTemporary(Ty),
+                             Constructor->getAccess());
     
     ExprResult Result
       = S.BuildCXXConstructExpr(CastLoc, Ty, cast<CXXConstructorDecl>(Method),

Modified: cfe/trunk/lib/Sema/SemaInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=154248&r1=154247&r2=154248&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaInit.cpp (original)
+++ cfe/trunk/lib/Sema/SemaInit.cpp Fri Apr  6 22:04:20 2012
@@ -4584,7 +4584,7 @@
   switch (OR) {
   case OR_Success:
     S.CheckConstructorAccess(Loc, cast<CXXConstructorDecl>(Best->Function),
-                             Best->FoundDecl.getAccess(), Diag);
+                             Entity, Best->FoundDecl.getAccess(), Diag);
     // FIXME: Check default arguments as far as that's possible.
     break;
 

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=154248&r1=154247&r2=154248&view=diff
==============================================================================
--- cfe/trunk/test/CXX/class.access/class.protected/p1.cpp (original)
+++ cfe/trunk/test/CXX/class.access/class.protected/p1.cpp Fri Apr  6 22:04:20 2012
@@ -68,7 +68,7 @@
 
 namespace test2 {
   class A {
-    protected: int x; // expected-note 3 {{object type must derive}}
+    protected: int x; // expected-note 3 {{can only access this member on an object of type}}
     static int sx;
     static void test(A&);
   };
@@ -103,7 +103,7 @@
 namespace test3 {
   class B;
   class A {
-    protected: int x; // expected-note {{object type must derive}}
+    protected: int x; //expected-note {{declared protected}} // expected-note {{can only access this member on an object of type}}
     static int sx;
     static void test(B&);
   };
@@ -130,7 +130,7 @@
     (void) b.sx;
   }
   void D::test(B &b) {
-    (void) b.x;
+    (void) b.x; // expected-error {{'x' is a protected member}}
     (void) b.sx;
   }
 }
@@ -138,7 +138,7 @@
 namespace test4 {
   class C;
   class A {
-    protected: int x; // expected-note {{declared}} expected-note 2 {{object type must derive}}
+    protected: int x; // expected-note 2{{declared protected here}} expected-note{{member is declared here}}
     static int sx;    // expected-note 3{{member is declared here}}
     static void test(C&);
   };
@@ -215,7 +215,7 @@
   class Static {};
   class A {
   protected:
-    void foo(int); // expected-note 3 {{object type must derive}}
+    void foo(int); // expected-note 3 {{can only access this member on an object of type}}
     void foo(long);
     static void foo(Static);
 
@@ -253,7 +253,7 @@
   class Static {};
   class A {
     protected:
-    void foo(int); // expected-note 3 {{object type must derive}}
+    void foo(int); // expected-note 3 {{must name member using the type of the current context}}
     void foo(long);
     static void foo(Static);
 
@@ -291,7 +291,7 @@
   class Static {};
   class A {
     protected:
-    void foo(int); // expected-note 3 {{object type must derive}}
+    void foo(int); // expected-note 3 {{must name member using the type of the current context}}
     void foo(long);
     static void foo(Static);
 
@@ -329,7 +329,7 @@
 
 namespace test9 {
   class A { // expected-note {{member is declared here}}
-  protected: int foo(); // expected-note 4 {{declared}} expected-note 2 {{object type must derive}} expected-note {{object type 'test9::A' must derive}}
+  protected: int foo(); // expected-note 4 {{declared}} expected-note 2 {{can only access this member on an object of type}} expected-note {{member is declared here}}
   };
 
   class B : public A { // expected-note {{member is declared here}}
@@ -423,7 +423,7 @@
 // This friendship is not considered because a public member of A is
 // inaccessible in C.
 namespace test13 {
-  class A { protected: int foo(); }; // expected-note {{object type 'test13::D' must derive from context type 'test13::C'}}
+  class A { protected: int foo(); }; // expected-note {{can only access this member on an object of type}}
   class B : private virtual A {};
   class C : private B { friend void test(); };
   class D : public virtual A {};
@@ -433,3 +433,71 @@
     d.A::foo(); // expected-error {{protected member}}
   }
 }
+
+// PR8058
+namespace test14 {
+  class A {
+  protected:
+    template <class T> void temp(T t); // expected-note {{must name member using the type of the current context}}
+
+    void nontemp(int); // expected-note {{must name member using the type of the current context}}
+
+    template <class T> void ovl_temp(T t); // expected-note {{must name member using the type of the current context}}
+    void ovl_temp(float);
+
+    void ovl_nontemp(int); // expected-note {{must name member using the type of the current context}}
+    void ovl_nontemp(float);
+
+    template <class T> void ovl_withtemp(T);
+    void ovl_withtemp(int); // expected-note {{must name member using the type of the current context}}
+  };
+
+  class B : public A {
+    void use() {
+      void (A::*ptr)(int);
+      ptr = &A::temp; // expected-error {{protected member}}
+      ptr = &A::nontemp; // expected-error {{protected member}}
+      ptr = &A::ovl_temp; // expected-error {{protected member}}
+      ptr = &A::ovl_nontemp; // expected-error {{protected member}}
+      ptr = &A::ovl_withtemp; // expected-error {{protected member}}
+    }
+  };
+}
+
+namespace test15 {
+  class A {
+  protected:
+    A(); // expected-note 2 {{protected constructor can only be used to construct a base class subobject}}
+    A(const A &); // expected-note {{protected constructor can only be used to construct a base class subobject}}
+    ~A(); // expected-note 3 {{protected destructor can only be used to destroy a base class subobject}}
+  };
+
+  class B : public A {
+    // The uses here are fine.
+    B() {}
+    B(int i) : A() {}
+    ~B() {}
+
+    // All these uses are bad.
+
+    void test0() {
+      A a; // expected-error {{protected constructor}} expected-error {{protected destructor}}
+    }
+
+    A *test1() {
+      return new A(); // expected-error {{protected constructor}}
+    }
+
+    void test2(A *a) {
+      delete a; // expected-error {{protected destructor}}
+    }
+
+    A test3(A *a) {
+      return *a; // expected-error {{protected constructor}}
+    }
+
+    void test4(A *a) {
+      a->~A(); // expected-error {{protected member}}
+    }
+  };
+}

Modified: cfe/trunk/test/CXX/class.access/p4.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/class.access/p4.cpp?rev=154248&r1=154247&r2=154248&view=diff
==============================================================================
--- cfe/trunk/test/CXX/class.access/p4.cpp (original)
+++ cfe/trunk/test/CXX/class.access/p4.cpp Fri Apr  6 22:04:20 2012
@@ -372,7 +372,7 @@
     int private_foo; // expected-note {{declared private here}}
     static int private_sfoo; // expected-note {{declared private here}}
   protected:
-    int protected_foo; // expected-note 3 {{declared protected here}} // expected-note {{object type must derive from context type 'test15::B<int>'}}
+    int protected_foo; // expected-note 3 {{declared protected here}} // expected-note {{can only access this member on an object of type 'test15::B<int>'}}
     static int protected_sfoo; // expected-note 3 {{declared protected here}}
 
     int test1(A<int> &a) {





More information about the cfe-commits mailing list