]> granicus.if.org Git - clang/commitdiff
Fix several problems with protected access control:
authorJohn McCall <rjmccall@apple.com>
Sat, 7 Apr 2012 03:04:20 +0000 (03:04 +0000)
committerJohn McCall <rjmccall@apple.com>
Sat, 7 Apr 2012 03:04:20 +0000 (03:04 +0000)
  - 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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@154248 91177308-0d34-0410-b5e6-96231b3b80d8

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

index ffe7be300a3bf81143d170820b0cbe89142088e7..a832087e27147f6f9901afe2458c394f8993c930 100644 (file)
@@ -884,8 +884,13 @@ def note_access_natural : Note<
 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;
index 20b4a311cd14e5d0f48beaed91edd39f94d8d97b..453c7f37f4b8c183b4c00350f07e6b6ba96bc2ce 100644 (file)
@@ -4105,11 +4105,13 @@ public:
                                       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);
index ea74346c163bf22c653e87dd18a7cbb14b6f99a5..74c4f34d75de0b712bb7ab80dc7c6376bd93723e 100644 (file)
@@ -165,6 +165,10 @@ struct AccessTarget : public AccessedEntity {
     initialize();
   }
 
+  bool isInstanceMember() const {
+    return (isMemberAccess() && getTargetDecl()->isCXXInstanceMember());
+  }
+
   bool hasInstanceContext() const {
     return HasInstanceContext;
   }
@@ -671,18 +675,25 @@ struct ProtectedFriendContext {
 }
 
 /// 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 @@ struct ProtectedFriendContext {
 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 @@ static AccessResult HasAccess(Sema &S,
       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 @@ static AccessResult HasAccess(Sema &S,
       //   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 @@ static AccessResult HasAccess(Sema &S,
   // *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 CXXBasePath *FindBestPath(Sema &S,
 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 @@ static bool TryDiagnoseProtectedAccess(Sema &S, const EffectiveContext &EC,
     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::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E,
 
 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 @@ Sema::AccessResult Sema::CheckDestructorAccess(SourceLocation Loc,
     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 @@ Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc,
                                                 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 @@ Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc,
 
   }
 
-  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 @@ Sema::AccessResult Sema::CheckAddressOfMemberAccess(Expr *OvlExpr,
   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();
 
index fb34126943b135f2ec3bbb4971311587720d8d19..975ea5b7a7202889586a45889607bcc4dd0fea15 100644 (file)
@@ -3344,7 +3344,8 @@ Sema::MarkBaseAndMemberDestructorsReferenced(SourceLocation Location,
     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 @@ NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS,
   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()) {
index 4692bf8b4ba6a25ff433037dc2374473c2123234..41f2116600b92dfc37f067b99dfb59ce1f86fa2c 100644 (file)
@@ -2276,8 +2276,9 @@ static ExprResult BuildCXXCastArgument(Sema &S,
                                   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),
index be48c7ea18946754be4d7141f7fc7783767a2067..ee051428da8ee187fec95e90bcc521b9f8ce5c06 100644 (file)
@@ -4584,7 +4584,7 @@ static void CheckCXX98CompatAccessibleCopy(Sema &S,
   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;
 
index 79bb6cd67eab805400919eaa1b104e39619874c8..75c3ffffe82ec6bd7e59080ef67c41a41703baec 100644 (file)
@@ -68,7 +68,7 @@ namespace test1 {
 
 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 test2 {
 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 @@ namespace test3 {
     (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 test3 {
 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 @@ namespace test6 {
   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 @@ namespace test7 {
   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 @@ namespace test8 {
   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 test8 {
 
 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 @@ namespace test12 {
 // 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 @@ namespace test13 {
     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}}
+    }
+  };
+}
index 058a158beb69ded0da21cc29c2721d0693fbce33..5ad738bebf9cc384ed45a84566e1c1deed2bb630 100644 (file)
@@ -372,7 +372,7 @@ namespace test15 {
     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) {