From d42742143c13cd1aca95f659bedb4a3bc8c7f76b Mon Sep 17 00:00:00 2001
From: John McCall <rjmccall@apple.com>
Date: Mon, 9 Apr 2012 20:53:23 +0000
Subject: [PATCH] Fix the access check performed as part of the determination
 of whether to define a special member function as deleted so that it properly
 establishes an object context for the accesses to the base subobject members.

llvm-svn: 154343
---
 clang/include/clang/Sema/Sema.h               |  3 +++
 clang/lib/Sema/SemaAccess.cpp                 | 23 +++++++++++++++++
 clang/lib/Sema/SemaDeclCXX.cpp                | 25 +++++++++++++++++--
 .../class.access/class.protected/p1-cxx11.cpp | 20 +++++++++++++++
 4 files changed, 69 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CXX/class.access/class.protected/p1-cxx11.cpp

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 453c7f37f4b8..eb8b7998ce9c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4129,6 +4129,9 @@ public:
                                     bool ForceUnprivileged = false);
   void CheckLookupAccess(const LookupResult &R);
   bool IsSimplyAccessible(NamedDecl *decl, DeclContext *Ctx);
+  bool isSpecialMemberAccessibleForDeletion(CXXMethodDecl *decl,
+                                            AccessSpecifier access,
+                                            QualType objectType);
 
   void HandleDependentAccessCheck(const DependentDiagnostic &DD,
                          const MultiLevelTemplateArgumentList &TemplateArgs);
diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp
index 74c4f34d75de..dea5e76d9e13 100644
--- a/clang/lib/Sema/SemaAccess.cpp
+++ b/clang/lib/Sema/SemaAccess.cpp
@@ -1507,6 +1507,29 @@ Sema::AccessResult Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E,
   return CheckAccess(*this, E->getMemberLoc(), Entity);
 }
 
+/// Is the given special member function accessible for the purposes of
+/// deciding whether to define a special member function as deleted?
+bool Sema::isSpecialMemberAccessibleForDeletion(CXXMethodDecl *decl,
+                                                AccessSpecifier access,
+                                                QualType objectType) {
+  // Fast path.
+  if (access == AS_public || !getLangOpts().AccessControl) return true;
+
+  AccessTarget entity(Context, AccessTarget::Member, decl->getParent(),
+                      DeclAccessPair::make(decl, access), objectType);
+
+  // Suppress diagnostics.
+  entity.setDiag(PDiag());
+
+  switch (CheckAccess(*this, SourceLocation(), entity)) {
+  case AR_accessible: return true;
+  case AR_inaccessible: return false;
+  case AR_dependent: llvm_unreachable("dependent for =delete computation");
+  case AR_delayed: llvm_unreachable("cannot delay =delete computation");
+  }
+  llvm_unreachable("bad access result");
+}
+
 Sema::AccessResult Sema::CheckDestructorAccess(SourceLocation Loc,
                                                CXXDestructorDecl *Dtor,
                                                const PartialDiagnostic &PDiag,
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 975ea5b7a720..704ff834b469 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -4397,9 +4397,31 @@ struct SpecialMemberDeletionInfo {
   bool shouldDeleteForSubobjectCall(Subobject Subobj,
                                     Sema::SpecialMemberOverloadResult *SMOR,
                                     bool IsDtorCallInCtor);
+
+  bool isAccessible(Subobject Subobj, CXXMethodDecl *D);
 };
 }
 
+/// Is the given special member inaccessible when used on the given
+/// sub-object.
+bool SpecialMemberDeletionInfo::isAccessible(Subobject Subobj,
+                                             CXXMethodDecl *target) {
+  /// If we're operating on a base class, the object type is the
+  /// type of this special member.
+  QualType objectTy;
+  AccessSpecifier access = target->getAccess();;
+  if (CXXBaseSpecifier *base = Subobj.dyn_cast<CXXBaseSpecifier*>()) {
+    objectTy = S.Context.getTypeDeclType(MD->getParent());
+    access = CXXRecordDecl::MergeAccess(base->getAccessSpecifier(), access);
+
+  // If we're operating on a field, the object type is the type of the field.
+  } else {
+    objectTy = S.Context.getTypeDeclType(target->getParent());
+  }
+
+  return S.isSpecialMemberAccessibleForDeletion(target, access, objectTy);
+}
+
 /// Check whether we should delete a special member due to the implicit
 /// definition containing a call to a special member of a subobject.
 bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
@@ -4414,8 +4436,7 @@ bool SpecialMemberDeletionInfo::shouldDeleteForSubobjectCall(
     DiagKind = !Decl ? 0 : 1;
   else if (SMOR->getKind() == Sema::SpecialMemberOverloadResult::Ambiguous)
     DiagKind = 2;
-  else if (S.CheckDirectMemberAccess(Loc, Decl, S.PDiag())
-             != Sema::AR_accessible)
+  else if (!isAccessible(Subobj, Decl))
     DiagKind = 3;
   else if (!IsDtorCallInCtor && Field && Field->getParent()->isUnion() &&
            !Decl->isTrivial()) {
diff --git a/clang/test/CXX/class.access/class.protected/p1-cxx11.cpp b/clang/test/CXX/class.access/class.protected/p1-cxx11.cpp
new file mode 100644
index 000000000000..dc9b20d17c0e
--- /dev/null
+++ b/clang/test/CXX/class.access/class.protected/p1-cxx11.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// PR12497
+namespace test0 {
+  class A {
+  protected:
+    A() {}
+    A(const A &) {}
+    ~A() {}
+    A &operator=(const A &a) { return *this; }
+  };
+
+  class B : public A {};
+
+  void test() {
+    B b1;
+    B b2 = b1;
+    b1 = b2;
+  }
+}