PR41607: Don't forget to substitute outer template arguments into a
class-scope explicit specialization of a class template.

llvm-svn: 359266
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 902e096..3ac3f37 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -1746,6 +1746,20 @@
     return getSpecializationKind() == TSK_ExplicitSpecialization;
   }
 
+  /// Is this an explicit specialization at class scope (within the class that
+  /// owns the primary template)? For example:
+  ///
+  /// \code
+  /// template<typename T> struct Outer {
+  ///   template<typename U> struct Inner;
+  ///   template<> struct Inner; // class-scope explicit specialization
+  /// };
+  /// \endcode
+  bool isClassScopeExplicitSpecialization() const {
+    return isExplicitSpecialization() &&
+           isa<CXXRecordDecl>(getLexicalDeclContext());
+  }
+
   /// True if this declaration is an explicit specialization,
   /// explicit instantiation declaration, or explicit instantiation
   /// definition.
@@ -2581,6 +2595,11 @@
     return getSpecializationKind() == TSK_ExplicitSpecialization;
   }
 
+  bool isClassScopeExplicitSpecialization() const {
+    return isExplicitSpecialization() &&
+           isa<CXXRecordDecl>(getLexicalDeclContext());
+  }
+
   /// True if this declaration is an explicit specialization,
   /// explicit instantiation declaration, or explicit instantiation
   /// definition.
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index e8f1dcc..c745735 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -66,9 +66,12 @@
   if (!Ctx) {
     Ctx = D->getDeclContext();
 
-    // Add template arguments from a variable template instantiation.
-    if (VarTemplateSpecializationDecl *Spec =
-            dyn_cast<VarTemplateSpecializationDecl>(D)) {
+    // Add template arguments from a variable template instantiation. For a
+    // class-scope explicit specialization, there are no template arguments
+    // at this level, but there may be enclosing template arguments.
+    VarTemplateSpecializationDecl *Spec =
+        dyn_cast<VarTemplateSpecializationDecl>(D);
+    if (Spec && !Spec->isClassScopeExplicitSpecialization()) {
       // We're done when we hit an explicit specialization.
       if (Spec->getSpecializationKind() == TSK_ExplicitSpecialization &&
           !isa<VarTemplatePartialSpecializationDecl>(Spec))
@@ -111,8 +114,9 @@
 
   while (!Ctx->isFileContext()) {
     // Add template arguments from a class template instantiation.
-    if (ClassTemplateSpecializationDecl *Spec
-          = dyn_cast<ClassTemplateSpecializationDecl>(Ctx)) {
+    ClassTemplateSpecializationDecl *Spec
+          = dyn_cast<ClassTemplateSpecializationDecl>(Ctx);
+    if (Spec && !Spec->isClassScopeExplicitSpecialization()) {
       // We're done when we hit an explicit specialization.
       if (Spec->getSpecializationKind() == TSK_ExplicitSpecialization &&
           !isa<ClassTemplatePartialSpecializationDecl>(Spec))
diff --git a/clang/test/SemaTemplate/explicit-specialization-member.cpp b/clang/test/SemaTemplate/explicit-specialization-member.cpp
index e8165ac..e84364a 100644
--- a/clang/test/SemaTemplate/explicit-specialization-member.cpp
+++ b/clang/test/SemaTemplate/explicit-specialization-member.cpp
@@ -62,3 +62,20 @@
   template<> float A<int>::n; // expected-error {{different type}}
   template<> void A<int>::f() throw(); // expected-error {{does not match}}
 }
+
+namespace PR41607 {
+  template<int N> struct Outer {
+    template<typename...> struct Inner;
+    template<> struct Inner<> {
+      static constexpr int f() { return N; }
+    };
+
+    template<typename...> static int a; // expected-note 2{{}}
+    template<> static constexpr int a<> = 42;
+  };
+  static_assert(Outer<123>::Inner<>::f() == 123, "");
+  static_assert(Outer<123>::Inner<>::f() != 125, "");
+  // FIXME: The class-scope explicit specialization of the variable template doesn't work!
+  static_assert(Outer<123>::a<> == 42, ""); // expected-error {{}} expected-note {{}}
+  static_assert(Outer<123>::a<> != 43, ""); // expected-error {{}} expected-note {{}}
+}