erik.pilkington created this revision.
erik.pilkington added reviewers: rsmith, rjmccall.
Herald added a subscriber: dexonsmith.

Previously, we stopped early on a class-scope explicit specialization, leading 
us to lose enclosing levels of template arguments. I'm not entirely convinced 
that this is the right fix, so any insight here would be greatly appreciated!

llvm.org/PR39031

Thanks!
Erik


Repository:
  rC Clang

https://reviews.llvm.org/D52521

Files:
  clang/include/clang/AST/DeclTemplate.h
  clang/lib/AST/DeclTemplate.cpp
  clang/lib/Sema/SemaTemplate.cpp
  clang/lib/Sema/SemaTemplateInstantiate.cpp
  clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
  clang/lib/Serialization/ASTReaderDecl.cpp
  clang/lib/Serialization/ASTWriterDecl.cpp
  clang/test/SemaCXX/member-spec-dr727.cpp

Index: clang/test/SemaCXX/member-spec-dr727.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/member-spec-dr727.cpp
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -std=c++17 -verify %s -emit-llvm -o -
+
+// expected-no-diagnostics
+
+namespace PR39031 {
+template <class>
+struct S {
+  template <class>
+  struct Wrapper;
+
+  template <>
+  struct Wrapper<int> {
+    template <class T>
+    void f(T) {
+      T x;
+    }
+  };
+};
+
+void Run() {
+  S<int>::Wrapper<int>().f(1);
+}
+} // namespace PR39031
+
+template <class A, class B> struct is_same { static constexpr bool value = false; };
+template <class A> struct is_same<A, A> { static constexpr bool value = true; };
+
+namespace t1 {
+template <class Ap> struct A {
+  template <class Bp> struct B;
+
+  template <> struct B<short> {
+    template <class Cp> struct C {
+      static_assert(is_same<Cp, int>::value);
+      static_assert(is_same<Ap, char>::value);
+    };
+  };
+};
+
+A<char>::B<short>::C<int> x;
+}
+
+namespace t2 {
+template <class Ap>
+struct A {
+  template <class Bp> static constexpr int p = 0;
+  template <> static constexpr int p<int> = 1;
+};
+
+// FIXME: why aren't we selecting the specialization here?
+static_assert(A<char>::p<int> == 0);
+}
Index: clang/lib/Serialization/ASTWriterDecl.cpp
===================================================================
--- clang/lib/Serialization/ASTWriterDecl.cpp
+++ clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1452,6 +1452,7 @@
   Record.AddTemplateArgumentList(&D->getTemplateArgs());
   Record.AddSourceLocation(D->getPointOfInstantiation());
   Record.push_back(D->getSpecializationKind());
+  Record.push_back(D->isSpecializedMember());
   Record.push_back(D->isCanonicalDecl());
 
   if (D->isCanonicalDecl()) {
@@ -1519,6 +1520,7 @@
   Record.AddSourceLocation(D->getPointOfInstantiation());
   Record.push_back(D->getSpecializationKind());
   Record.push_back(D->IsCompleteDefinition);
+  Record.push_back(D->isSpecializedMember());
   Record.push_back(D->isCanonicalDecl());
 
   if (D->isCanonicalDecl()) {
Index: clang/lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderDecl.cpp
+++ clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2177,6 +2177,7 @@
   D->TemplateArgs = TemplateArgumentList::CreateCopy(C, TemplArgs);
   D->PointOfInstantiation = ReadSourceLocation();
   D->SpecializationKind = (TemplateSpecializationKind)Record.readInt();
+  D->IsSpecializedMember = Record.readInt();
 
   bool writtenAsCanonicalDecl = Record.readInt();
   if (writtenAsCanonicalDecl) {
@@ -2298,6 +2299,7 @@
   D->PointOfInstantiation = ReadSourceLocation();
   D->SpecializationKind = (TemplateSpecializationKind)Record.readInt();
   D->IsCompleteDefinition = Record.readInt();
+  D->IsSpecializedMember = Record.readInt();
 
   bool writtenAsCanonicalDecl = Record.readInt();
   if (writtenAsCanonicalDecl) {
Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2982,6 +2982,7 @@
   InstD->setTypeAsWritten(WrittenTy);
   InstD->setExternLoc(D->getExternLoc());
   InstD->setTemplateKeywordLoc(D->getTemplateKeywordLoc());
+  InstD->setIsSpecializedMember(D->isSpecializedMember());
 
   Owner->addDecl(InstD);
 
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -70,27 +70,27 @@
     if (VarTemplateSpecializationDecl *Spec =
             dyn_cast<VarTemplateSpecializationDecl>(D)) {
       // We're done when we hit an explicit specialization.
-      if (Spec->getSpecializationKind() == TSK_ExplicitSpecialization &&
-          !isa<VarTemplatePartialSpecializationDecl>(Spec))
+      if (Spec->getSpecializationKind() != TSK_ExplicitSpecialization ||
+          isa<VarTemplatePartialSpecializationDecl>(Spec)) {
+        Result.addOuterTemplateArguments(&Spec->getTemplateInstantiationArgs());
+
+        // If this variable template specialization was instantiated from a
+        // specialized member that is a variable template, we're done.
+        assert(Spec->getSpecializedTemplate() && "No variable template?");
+        llvm::PointerUnion<VarTemplateDecl*,
+                           VarTemplatePartialSpecializationDecl*> Specialized
+            = Spec->getSpecializedTemplateOrPartial();
+        if (VarTemplatePartialSpecializationDecl *Partial =
+            Specialized.dyn_cast<VarTemplatePartialSpecializationDecl *>()) {
+          if (Partial->isMemberSpecialization())
+            return Result;
+        } else {
+          VarTemplateDecl *Tmpl = Specialized.get<VarTemplateDecl *>();
+          if (Tmpl->isMemberSpecialization())
+            return Result;
+        }
+      } else if (!Spec->isSpecializedMember())
         return Result;
-
-      Result.addOuterTemplateArguments(&Spec->getTemplateInstantiationArgs());
-
-      // If this variable template specialization was instantiated from a
-      // specialized member that is a variable template, we're done.
-      assert(Spec->getSpecializedTemplate() && "No variable template?");
-      llvm::PointerUnion<VarTemplateDecl*,
-                         VarTemplatePartialSpecializationDecl*> Specialized
-                             = Spec->getSpecializedTemplateOrPartial();
-      if (VarTemplatePartialSpecializationDecl *Partial =
-              Specialized.dyn_cast<VarTemplatePartialSpecializationDecl *>()) {
-        if (Partial->isMemberSpecialization())
-          return Result;
-      } else {
-        VarTemplateDecl *Tmpl = Specialized.get<VarTemplateDecl *>();
-        if (Tmpl->isMemberSpecialization())
-          return Result;
-      }
     }
 
     // If we have a template template parameter with translation unit context,
@@ -113,10 +113,19 @@
     // Add template arguments from a class template instantiation.
     if (ClassTemplateSpecializationDecl *Spec
           = dyn_cast<ClassTemplateSpecializationDecl>(Ctx)) {
-      // We're done when we hit an explicit specialization.
+      // We're done when we hit an explicit specialization ...
       if (Spec->getSpecializationKind() == TSK_ExplicitSpecialization &&
-          !isa<ClassTemplatePartialSpecializationDecl>(Spec))
+          !isa<ClassTemplatePartialSpecializationDecl>(Spec)) {
+        // ... unless this explicit specialization is a member of a template
+        // (DR727), in which case we need to skip past it to gather the
+        // enclosing template argument lists.
+        if (Spec->isSpecializedMember()) {
+          Ctx = Spec->getDeclContext();
+          RelativeToPrimary = false;
+          continue;
+        }
         break;
+      }
 
       Result.addOuterTemplateArguments(&Spec->getTemplateInstantiationArgs());
 
Index: clang/lib/Sema/SemaTemplate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplate.cpp
+++ clang/lib/Sema/SemaTemplate.cpp
@@ -3820,6 +3820,10 @@
       VarTemplate->AddSpecialization(Specialization, InsertPos);
   }
 
+  // Record that this specialization was declared at class scope.
+  if (!CurContext->getRedeclContext()->isFileContext())
+    Specialization->setIsSpecializedMember();
+
   // C++ [temp.expl.spec]p6:
   //   If a template, a member template or the member of a class template is
   //   explicitly specialized then that specialization shall be declared
@@ -7662,8 +7666,13 @@
     } else {
       CanonType = Context.getTypeDeclType(Specialization);
     }
+
   }
 
+  // Record that this specialization was declared at class scope.
+  if (!CurContext->getRedeclContext()->isFileContext())
+    Specialization->setIsSpecializedMember();
+
   // C++ [temp.expl.spec]p6:
   //   If a template, a member template or the member of a class template is
   //   explicitly specialized then that specialization shall be declared
Index: clang/lib/AST/DeclTemplate.cpp
===================================================================
--- clang/lib/AST/DeclTemplate.cpp
+++ clang/lib/AST/DeclTemplate.cpp
@@ -691,14 +691,14 @@
                     SpecializedTemplate->getIdentifier(), PrevDecl),
     SpecializedTemplate(SpecializedTemplate),
     TemplateArgs(TemplateArgumentList::CreateCopy(Context, Args)),
-    SpecializationKind(TSK_Undeclared) {
+    SpecializationKind(TSK_Undeclared), IsSpecializedMember(0) {
 }
 
 ClassTemplateSpecializationDecl::ClassTemplateSpecializationDecl(ASTContext &C,
                                                                  Kind DK)
     : CXXRecordDecl(DK, TTK_Struct, C, nullptr, SourceLocation(),
                     SourceLocation(), nullptr, nullptr),
-      SpecializationKind(TSK_Undeclared) {}
+      SpecializationKind(TSK_Undeclared), IsSpecializedMember(0) {}
 
 ClassTemplateSpecializationDecl *
 ClassTemplateSpecializationDecl::Create(ASTContext &Context, TagKind TK,
@@ -1019,13 +1019,15 @@
               SpecializedTemplate->getIdentifier(), T, TInfo, S),
       SpecializedTemplate(SpecializedTemplate),
       TemplateArgs(TemplateArgumentList::CreateCopy(Context, Args)),
-      SpecializationKind(TSK_Undeclared), IsCompleteDefinition(false) {}
+      SpecializationKind(TSK_Undeclared), IsCompleteDefinition(false),
+      IsSpecializedMember(0) {}
 
 VarTemplateSpecializationDecl::VarTemplateSpecializationDecl(Kind DK,
                                                              ASTContext &C)
     : VarDecl(DK, C, nullptr, SourceLocation(), SourceLocation(), nullptr,
               QualType(), nullptr, SC_None),
-      SpecializationKind(TSK_Undeclared), IsCompleteDefinition(false) {}
+      SpecializationKind(TSK_Undeclared), IsCompleteDefinition(false),
+      IsSpecializedMember(0) {}
 
 VarTemplateSpecializationDecl *VarTemplateSpecializationDecl::Create(
     ASTContext &Context, DeclContext *DC, SourceLocation StartLoc,
Index: clang/include/clang/AST/DeclTemplate.h
===================================================================
--- clang/include/clang/AST/DeclTemplate.h
+++ clang/include/clang/AST/DeclTemplate.h
@@ -1688,6 +1688,9 @@
   /// Really a value of type TemplateSpecializationKind.
   unsigned SpecializationKind : 3;
 
+  /// If this class template specialization was declared at class scope (DR727).
+  unsigned IsSpecializedMember : 1;
+
 protected:
   ClassTemplateSpecializationDecl(ASTContext &Context, Kind DK, TagKind TK,
                                   DeclContext *DC, SourceLocation StartLoc,
@@ -1765,6 +1768,16 @@
     PointOfInstantiation = Loc;
   }
 
+  void setIsSpecializedMember(bool V = true) { IsSpecializedMember = V; }
+
+  /// If this class template specialization was declared at class scope (DR727).
+  /// For example, the specialization G<int> below:
+  ///   struct S {
+  ///     template <class> struct G {};
+  ///     template <> struct G<int> {};
+  ///   };
+  bool isSpecializedMember() const { return IsSpecializedMember; }
+
   /// If this class template specialization is an instantiation of
   /// a template (rather than an explicit specialization), return the
   /// class template or class template partial specialization from which it
@@ -2523,6 +2536,10 @@
   /// no initializer.
   unsigned IsCompleteDefinition : 1;
 
+  /// If this variable template specialization was declared at class scope
+  /// (DR727).
+  unsigned IsSpecializedMember : 1;
+
 protected:
   VarTemplateSpecializationDecl(Kind DK, ASTContext &Context, DeclContext *DC,
                                 SourceLocation StartLoc, SourceLocation IdLoc,
@@ -2704,6 +2721,17 @@
     return ExplicitInfo ? ExplicitInfo->TemplateKeywordLoc : SourceLocation();
   }
 
+  void setIsSpecializedMember(bool V = true) { IsSpecializedMember = V; }
+
+  /// If this variable template specialization was declared at class scope
+  /// (DR727). For example, the specialization v<int> below:
+  ///   struct S {
+  ///     template <class> static int v;
+  ///     template <> static int v<int>;
+  ///   };
+  bool isSpecializedMember() const { return IsSpecializedMember; }
+
+
   void Profile(llvm::FoldingSetNodeID &ID) const {
     Profile(ID, TemplateArgs->asArray(), getASTContext());
   }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D52521: [... Erik Pilkington via Phabricator via cfe-commits

Reply via email to