Properly switch into the declaring scope of a template when performing
template argument deduction or (more importantly) the final substitution
required by such deduction.  Makes access control magically work in these
cases.

Fixes PR6967.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@102572 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index f6af46e..02a86cd 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -233,6 +233,30 @@
   /// CurContext - This is the current declaration context of parsing.
   DeclContext *CurContext;
 
+  /// A RAII object to temporarily push a declaration context.
+  class ContextRAII {
+  private:
+    Sema &S;
+    DeclContext *SavedContext;
+
+  public:
+    ContextRAII(Sema &S, DeclContext *ContextToPush)
+      : S(S), SavedContext(S.CurContext) {
+      assert(ContextToPush && "pushing null context");
+      S.CurContext = ContextToPush;
+    }
+
+    void pop() {
+      if (!SavedContext) return;
+      S.CurContext = SavedContext;
+      SavedContext = 0;
+    }
+
+    ~ContextRAII() {
+      pop();
+    }
+  };
+
   /// PackContext - Manages the stack for #pragma pack. An alignment
   /// of 0 indicates default alignment.
   void *PackContext; // Really a "PragmaPackStack*"
diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp
index 0a2c5ae..13bfc0f 100644
--- a/lib/Sema/SemaTemplateDeduction.cpp
+++ b/lib/Sema/SemaTemplateDeduction.cpp
@@ -988,6 +988,8 @@
   if (Inst)
     return TDK_InstantiationDepth;
 
+  ContextRAII SavedContext(*this, Partial->getDeclContext());
+
   // C++ [temp.deduct.type]p2:
   //   [...] or if any template argument remains neither deduced nor
   //   explicitly specified, template argument deduction fails.
@@ -1163,6 +1165,8 @@
   if (Inst)
     return TDK_InstantiationDepth;
 
+  ContextRAII SavedContext(*this, FunctionTemplate->getDeclContext());
+
   if (CheckTemplateArgumentList(FunctionTemplate,
                                 SourceLocation(),
                                 ExplicitTemplateArgs,
@@ -1311,6 +1315,8 @@
   if (Inst)
     return TDK_InstantiationDepth;
 
+  ContextRAII SavedContext(*this, FunctionTemplate->getDeclContext());
+
   // C++ [temp.deduct.type]p2:
   //   [...] or if any template argument remains neither deduced nor
   //   explicitly specified, template argument deduction fails.
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index 6fdf243..7dee0ad 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1167,8 +1167,7 @@
 
   // Enter the scope of this instantiation. We don't use
   // PushDeclContext because we don't have a scope.
-  DeclContext *PreviousContext = CurContext;
-  CurContext = Instantiation;
+  ContextRAII SavedContext(*this, Instantiation);
 
   // If this is an instantiation of a local class, merge this local
   // instantiation scope with the enclosing scope. Otherwise, every
@@ -1209,7 +1208,7 @@
     Invalid = true;
   
   // Exit the scope of this instantiation.
-  CurContext = PreviousContext;
+  SavedContext.pop();
 
   // If this is a polymorphic C++ class without a key function, we'll
   // have to mark all of the virtual members to allow emission of a vtable
diff --git a/test/CXX/class.access/p6.cpp b/test/CXX/class.access/p6.cpp
index d51d75d..814265d 100644
--- a/test/CXX/class.access/p6.cpp
+++ b/test/CXX/class.access/p6.cpp
@@ -52,3 +52,56 @@
     A apriv = priv; // expected-error {{private constructor}}
   }
 }
+
+// PR6967
+namespace test2 {
+  class A {
+  public:
+    template <class T> static void set(T &t, typename T::type v) {
+      t.value = v;
+    }
+    template <class T> static typename T::type get(const T &t) {
+      return t.value;
+    }
+  };
+
+  class B {
+    friend class A;
+
+  private:
+    typedef int type;
+    type value;
+  };
+
+  int test() {
+    B b;
+    A::set(b, 0);
+    return A::get(b);
+  }
+}
+
+namespace test3 {
+  class Green {}; class Blue {};
+
+  // We have to wrap this in a class because a partial specialization
+  // isn't actually in the context of the template.
+  struct Outer {
+    template <class T, class Nat> class A {
+    };
+  };
+
+  template <class T> class Outer::A<T, typename T::nature> {
+  public:
+    static void foo();
+  };
+
+  class B {
+  private: typedef Green nature;
+    friend class Outer;
+  };
+
+  void test() {
+    Outer::A<B, Green>::foo();
+    Outer::A<B, Blue>::foo(); // expected-error {{no member named 'foo'}}
+  }
+}