]> granicus.if.org Git - clang/commitdiff
Refactor checking of the scope of explicit template specialization
authorDouglas Gregor <dgregor@apple.com>
Wed, 7 Oct 2009 00:13:32 +0000 (00:13 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 7 Oct 2009 00:13:32 +0000 (00:13 +0000)
declarations and explicit template instantiations, improving
diagnostics and making the code usable for function template
specializations (as well as class template specializations and partial
specializations).

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

include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/Sema.h
lib/Sema/SemaTemplate.cpp
test/CXX/temp/temp.spec/temp.expl.spec/p1.cpp
test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp [new file with mode: 0644]
test/SemaTemplate/class-template-spec.cpp
test/SemaTemplate/temp_class_spec_neg.cpp

index b5672fd7ca83dc156518b1cccb094e3b57e594c2..c0a8651b36acd873ae3724d8f8b2bb3e8f4b0ef8 100644 (file)
@@ -916,6 +916,34 @@ def err_template_arg_not_pointer_to_member_form : Error<
 def err_template_arg_extra_parens : Error<
   "non-type template argument cannot be surrounded by parentheses">;
 
+// C++ template specialization
+def err_template_spec_unknown_kind : Error<
+  "can only provide an explicit %select{<error>|<error>|specialization|"
+  "instantiation|instantiation}0 for a class template, function template, or "
+  "a member function, static data member, or member class of a class template">;
+def note_specialized_entity : Note<
+  "explicit %select{<error>|<error>|specialized|instantiated|instantiated}0 is "
+  "here">;
+def err_template_spec_decl_function_scope : Error<
+  "explicit %select{<error>|<error>|specialization|instantiation|"
+  "instantiation}0 of %1 in function scope">;
+def err_template_spec_decl_out_of_scope_global : Error<
+  "%select{class template|class template partial|function template|member "
+  "function|static data member|member class}0 specialization of %1 must "
+  "originally be declared in the global scope">;
+def err_template_spec_decl_out_of_scope : Error<
+  "%select{class template|class template partial|function template|member "
+  "function|static data member|member class}0 specialization of %1 must "
+  "originally be declared in namespace %2">;
+def err_template_spec_redecl_out_of_scope : Error<
+  "%select{class template|class template partial|function template|member "
+  "function|static data member|member class}0 specialization of %1 not in a "
+  "namespace enclosing %2">;
+def err_template_spec_redecl_global_scope : Error<
+  "%select{class template|class template partial|function template|member "
+  "function|static data member|member class}0 specialization of %1 must occur "
+  "at global scope">;
+
 // C++ class template specializations and out-of-line definitions
 def err_template_spec_needs_header : Error<
   "template specialization requires 'template<>'">;
@@ -931,20 +959,6 @@ def err_template_spec_extra_headers : Error<
 def err_template_qualified_declarator_no_match : Error<
   "nested name specifier %0 for declaration does not refer into a class, "
   "class template or class template partial specialization">;
-def err_template_spec_decl_out_of_scope_global : Error<
-  "class template %select{|partial }0specialization of %1 must occur in the "
-  "global scope">;
-def err_template_spec_decl_out_of_scope : Error<
-  "class template %select{|partial }0specialization of %1 not in namespace %2">;
-def err_template_spec_decl_function_scope : Error<
-  "%select{class template specialization|class template partial specialization|"
-  "explicit instantiation}0 of %1 in function scope">;
-def err_template_spec_redecl_out_of_scope : Error<
-  "%select{class template specialization|class template partial specialization|"
-  "explicit instantiation}0 of %1 not in a namespace enclosing %2">;
-def err_template_spec_redecl_global_scope : Error<
-  "%select{class template specialization|class template partial specialization|"
-  "explicit instantiation}0 of %1 must occur at global scope">;
 
 // C++ Class Template Partial Specialization
 def err_default_arg_in_partial_spec : Error<
@@ -978,7 +992,7 @@ def err_function_template_spec_ambiguous : Error<
     "arguments to identify a particular function template">;
 def note_function_template_spec_matched : Note<
     "function template matches specialization %0">;
-    
+
 // C++ Template Instantiation
 def err_template_recursion_depth_exceeded : Error<
   "recursive template instantiation exceeded maximum depth of %0">,
index 9ddabde869b4952ad8cee8690b20762ae22e75ff..2056d39132e215f0827355e02d6c97ee6b17f2d9 100644 (file)
@@ -2485,13 +2485,6 @@ public:
                                                 const CXXScopeSpec &SS,
                                                 TypeTy *ObjectType);
 
-  bool CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate,
-                                    ClassTemplateSpecializationDecl *PrevDecl,
-                                             SourceLocation TemplateNameLoc,
-                                             SourceRange ScopeSpecifierRange,
-                                             bool PartialSpecialization,
-                                             bool ExplicitInstantiation);
-
   bool CheckClassTemplatePartialSpecializationArgs(
                                         TemplateParameterList *TemplateParams,
                               const TemplateArgumentListBuilder &TemplateArgs,
index 02c3e43b333e625b71e9a25ef7c4dfdfc5bf32b1..1addd16bdf4d14233bb4e3167037074a8264ee41 100644 (file)
@@ -2345,21 +2345,76 @@ Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) {
     << TemplateParams->getSourceRange();
 }
 
-/// \brief Check whether a class template specialization or explicit
-/// instantiation in the current context is well-formed.
+/// \brief Determine what kind of template specialization the given declaration
+/// is.
+static TemplateSpecializationKind getTemplateSpecializationKind(NamedDecl *D) {
+  if (!D)
+    return TSK_Undeclared;
+  
+  if (ClassTemplateSpecializationDecl *CTS 
+        = dyn_cast<ClassTemplateSpecializationDecl>(D))
+    return CTS->getSpecializationKind();
+  if (FunctionDecl *Function = dyn_cast<FunctionDecl>(D))
+    return Function->getTemplateSpecializationKind();
+
+  // FIXME: static data members!
+  // FIXME: member classes of class templates!
+  return TSK_Undeclared;
+}
+
+/// \brief Check whether a specialization or explicit instantiation is
+/// well-formed in the current context.
 ///
-/// This routine determines whether a class template specialization or
+/// This routine determines whether a template specialization or
 /// explicit instantiation can be declared in the current context
-/// (C++ [temp.expl.spec]p2, C++0x [temp.explicit]p2) and emits
-/// appropriate diagnostics if there was an error. It returns true if
-// there was an error that we cannot recover from, and false otherwise.
-bool
-Sema::CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate,
-                                   ClassTemplateSpecializationDecl *PrevDecl,
-                                            SourceLocation TemplateNameLoc,
-                                            SourceRange ScopeSpecifierRange,
-                                            bool PartialSpecialization,
-                                            bool ExplicitInstantiation) {
+/// (C++ [temp.expl.spec]p2, C++0x [temp.explicit]p2).
+///
+/// \param S the semantic analysis object for which this check is being
+/// performed.
+///
+/// \param Specialized the entity being specialized or instantiated, which
+/// may be a kind of template (class template, function template, etc.) or
+/// a member of a class template (member function, static data member, 
+/// member class).
+///
+/// \param PrevDecl the previous declaration of this entity, if any.
+///
+/// \param Loc the location of the explicit specialization or instantiation of
+/// this entity.
+///
+/// \param IsPartialSpecialization whether this is a partial specialization of
+/// a class template.
+///
+/// \param TSK the kind of specialization or implicit instantiation being 
+/// performed.
+///
+/// \returns true if there was an error that we cannot recover from, false
+/// otherwise.
+static bool CheckTemplateSpecializationScope(Sema &S,
+                                             NamedDecl *Specialized,
+                                             NamedDecl *PrevDecl,
+                                             SourceLocation Loc,
+                                             bool IsPartialSpecialization,
+                                             TemplateSpecializationKind TSK) {
+  // Keep these "kind" numbers in sync with the %select statements in the
+  // various diagnostics emitted by this routine.
+  int EntityKind = 0;
+  if (isa<ClassTemplateDecl>(Specialized))
+    EntityKind = IsPartialSpecialization? 1 : 0;
+  else if (isa<FunctionTemplateDecl>(Specialized))
+    EntityKind = 2;
+  else if (isa<CXXMethodDecl>(Specialized))
+    EntityKind = 3;
+  else if (isa<VarDecl>(Specialized))
+    EntityKind = 4;
+  else if (isa<RecordDecl>(Specialized))
+    EntityKind = 5;
+  else {
+    S.Diag(Loc, diag::err_template_spec_unknown_kind) << TSK;
+    S.Diag(Specialized->getLocation(), diag::note_specialized_entity) << TSK;
+    return true;
+  }
+
   // C++ [temp.expl.spec]p2:
   //   An explicit specialization shall be declared in the namespace
   //   of which the template is a member, or, for member templates, in
@@ -2373,66 +2428,68 @@ Sema::CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate,
   //   the explicit specialization was declared, or in a namespace
   //   that encloses the one in which the explicit specialization was
   //   declared.
-  if (CurContext->getLookupContext()->isFunctionOrMethod()) {
-    int Kind = ExplicitInstantiation? 2 : PartialSpecialization? 1 : 0;
-    Diag(TemplateNameLoc, diag::err_template_spec_decl_function_scope)
-      << Kind << ClassTemplate;
+  if (S.CurContext->getLookupContext()->isFunctionOrMethod()) {
+    S.Diag(Loc, diag::err_template_spec_decl_function_scope)
+      << TSK << Specialized;
     return true;
   }
-
-  DeclContext *DC = CurContext->getEnclosingNamespaceContext();
-  DeclContext *TemplateContext
-    = ClassTemplate->getDeclContext()->getEnclosingNamespaceContext();
-  if ((!PrevDecl || PrevDecl->getSpecializationKind() == TSK_Undeclared) &&
-      !ExplicitInstantiation) {
-    // There is no prior declaration of this entity, so this
-    // specialization must be in the same context as the template
-    // itself.
-    if (DC != TemplateContext) {
-      if (isa<TranslationUnitDecl>(TemplateContext))
-        Diag(TemplateNameLoc, diag::err_template_spec_decl_out_of_scope_global)
-          << PartialSpecialization
-          << ClassTemplate << ScopeSpecifierRange;
-      else if (isa<NamespaceDecl>(TemplateContext))
-        Diag(TemplateNameLoc, diag::err_template_spec_decl_out_of_scope)
-          << PartialSpecialization << ClassTemplate
-          << cast<NamedDecl>(TemplateContext) << ScopeSpecifierRange;
-
-      Diag(ClassTemplate->getLocation(), diag::note_template_decl_here);
+  
+  bool ComplainedAboutScope = false;
+  DeclContext *SpecializedContext
+    = Specialized->getDeclContext()->getEnclosingNamespaceContext();
+  if (TSK == TSK_ExplicitSpecialization) {
+    DeclContext *DC = S.CurContext->getEnclosingNamespaceContext();
+    if ((!PrevDecl || 
+         getTemplateSpecializationKind(PrevDecl) == TSK_Undeclared ||
+         getTemplateSpecializationKind(PrevDecl) == TSK_ImplicitInstantiation)){
+      // There is no prior declaration of this entity, so this
+      // specialization must be in the same context as the template
+      // itself.
+      if (!DC->Equals(SpecializedContext)) {
+        if (isa<TranslationUnitDecl>(SpecializedContext))
+          S.Diag(Loc, diag::err_template_spec_decl_out_of_scope_global)
+          << EntityKind << Specialized;
+        else if (isa<NamespaceDecl>(SpecializedContext))
+          S.Diag(Loc, diag::err_template_spec_decl_out_of_scope)
+          << EntityKind << Specialized
+          << cast<NamedDecl>(SpecializedContext);
+        
+        S.Diag(Specialized->getLocation(), diag::note_template_decl_here);
+        ComplainedAboutScope = true;
+      }
     }
-
-    return false;
   }
-
-  // We have a previous declaration of this entity. Make sure that
-  // this redeclaration (or definition) occurs in an enclosing namespace.
-  if (!CurContext->Encloses(TemplateContext)) {
-    // FIXME:  In C++98,  we  would like  to  turn these  errors into  warnings,
-    // dependent on a -Wc++0x flag.
-    bool SuppressedDiag = false;
-    int Kind = ExplicitInstantiation? 2 : PartialSpecialization? 1 : 0;
-    if (isa<TranslationUnitDecl>(TemplateContext)) {
-      if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x)
-        Diag(TemplateNameLoc, diag::err_template_spec_redecl_global_scope)
-          << Kind << ClassTemplate << ScopeSpecifierRange;
-      else
-        SuppressedDiag = true;
-    } else if (isa<NamespaceDecl>(TemplateContext)) {
-      if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x)
-        Diag(TemplateNameLoc, diag::err_template_spec_redecl_out_of_scope)
-          << Kind << ClassTemplate
-          << cast<NamedDecl>(TemplateContext) << ScopeSpecifierRange;
-      else
-        SuppressedDiag = true;
-    }
-
-    if (!SuppressedDiag)
-      Diag(ClassTemplate->getLocation(), diag::note_template_decl_here);
+  
+  // Make sure that this redeclaration (or definition) occurs in an enclosing 
+  // namespace. We perform this check for explicit specializations and, in 
+  // C++0x, for explicit instantiations as well (per DR275).
+  // FIXME: -Wc++0x should make these warnings.
+  // Note that HandleDeclarator() performs this check for explicit 
+  // specializations of function templates, static data members, and member
+  // functions, so we skip the check here for those kinds of entities.
+  // FIXME: HandleDeclarator's diagnostics aren't quite as good, though.
+  // Should we refactor the check, so that it occurs later?
+  if (!ComplainedAboutScope && !S.CurContext->Encloses(SpecializedContext) &&
+      ((TSK == TSK_ExplicitSpecialization &&
+        !(isa<FunctionTemplateDecl>(Specialized) || isa<VarDecl>(Specialized) ||
+          isa<FunctionDecl>(Specialized))) || 
+        S.getLangOptions().CPlusPlus0x)) {
+    if (isa<TranslationUnitDecl>(SpecializedContext))
+      S.Diag(Loc, diag::err_template_spec_redecl_global_scope)
+        << EntityKind << Specialized;
+    else if (isa<NamespaceDecl>(SpecializedContext))
+      S.Diag(Loc, diag::err_template_spec_redecl_out_of_scope)
+        << EntityKind << Specialized
+        << cast<NamedDecl>(SpecializedContext);
+  
+    S.Diag(Specialized->getLocation(), diag::note_template_decl_here);
   }
-
+  
+  // FIXME: check for specialization-after-instantiation errors and such.
+  
   return false;
 }
-
+                                             
 /// \brief Check the non-type template arguments of a class template
 /// partial specialization according to C++ [temp.class.spec]p9.
 ///
@@ -2714,11 +2771,9 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec,
   // Check whether we can declare a class template specialization in
   // the current scope.
   if (TUK != TUK_Friend &&
-      CheckClassTemplateSpecializationScope(ClassTemplate, PrevDecl,
-                                            TemplateNameLoc,
-                                            SS.getRange(),
-                                            isPartialSpecialization,
-                                            /*ExplicitInstantiation=*/false))
+      CheckTemplateSpecializationScope(*this, ClassTemplate, PrevDecl, 
+                                       TemplateNameLoc, isPartialSpecialization,
+                                       TSK_ExplicitSpecialization))
     return true;
 
   // The canonical type
@@ -3008,6 +3063,13 @@ Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD,
   // FIXME: Check if the prior specialization has a point of instantiation.
   // If so, we have run afoul of C++ [temp.expl.spec]p6.
   
+  // Check the scope of this explicit specialization.
+  if (CheckTemplateSpecializationScope(*this, 
+                                       Specialization->getPrimaryTemplate(),
+                                       Specialization, FD->getLocation(), 
+                                       false, TSK_ExplicitSpecialization))
+    return true;
+  
   // Mark the prior declaration as an explicit specialization, so that later
   // clients know that this is an explicit specialization.
   // FIXME: Check for prior explicit instantiations?
@@ -3071,18 +3133,10 @@ Sema::ActOnExplicitInstantiation(Scope *S,
     Kind = ClassTemplate->getTemplatedDecl()->getTagKind();
   }
 
-  // C++0x [temp.explicit]p2:
-  //   [...] An explicit instantiation shall appear in an enclosing
-  //   namespace of its template. [...]
-  //
-  // This is C++ DR 275.
-  if (CheckClassTemplateSpecializationScope(ClassTemplate, 0,
-                                            TemplateNameLoc,
-                                            SS.getRange(),
-                                            /*PartialSpecialization=*/false,
-                                            /*ExplicitInstantiation=*/true))
-    return true;
-
+  TemplateSpecializationKind TSK
+    = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition
+                           : TSK_ExplicitInstantiationDeclaration;
+  
   // Translate the parser's template argument list in our AST format.
   llvm::SmallVector<TemplateArgument, 16> TemplateArgs;
   translateTemplateArguments(TemplateArgsIn, TemplateArgLocs, TemplateArgs);
@@ -3111,6 +3165,16 @@ Sema::ActOnExplicitInstantiation(Scope *S,
   ClassTemplateSpecializationDecl *PrevDecl
     = ClassTemplate->getSpecializations().FindNodeOrInsertPos(ID, InsertPos);
 
+  // C++0x [temp.explicit]p2:
+  //   [...] An explicit instantiation shall appear in an enclosing
+  //   namespace of its template. [...]
+  //
+  // This is C++ DR 275.
+  if (CheckTemplateSpecializationScope(*this, ClassTemplate, PrevDecl,
+                                       TemplateNameLoc, false, 
+                                       TSK))
+    return true;
+  
   ClassTemplateSpecializationDecl *Specialization = 0;
 
   bool SpecializationRequiresInstantiation = true;
@@ -3224,9 +3288,6 @@ Sema::ActOnExplicitInstantiation(Scope *S,
   //
   // This check comes when we actually try to perform the
   // instantiation.
-  TemplateSpecializationKind TSK
-    = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition
-                           : TSK_ExplicitInstantiationDeclaration;
   if (SpecializationRequiresInstantiation)
     InstantiateClassTemplateSpecialization(Specialization, TSK);
   else // Instantiate the members of this class template specialization.
index f3d0709ef824b220774d7fdc28fcb85dbb1e74c4..239b8aeb04e404e91d8c0349213dc7a5326a33d6 100644 (file)
@@ -1,8 +1,10 @@
-// RUN: clang-cc -fsyntax-only %s
+// RUN: clang-cc -fsyntax-only -verify %s
 
 // This test creates cases where implicit instantiations of various entities
 // would cause a diagnostic, but provides expliict specializations for those
 // entities that avoid the diagnostic. The intent is to verify that 
+// implicit instantiations do not occur (because the explicit specialization 
+// is used instead).
 struct NonDefaultConstructible {
   NonDefaultConstructible(int);
 };
diff --git a/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp b/test/CXX/temp/temp.spec/temp.expl.spec/p2.cpp
new file mode 100644 (file)
index 0000000..3bc0a07
--- /dev/null
@@ -0,0 +1,114 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+// This test creates cases where implicit instantiations of various entities
+// would cause a diagnostic, but provides expliict specializations for those
+// entities that avoid the diagnostic. The specializations are alternately
+// declarations and definitions, and the intent of this test is to verify
+// that we allow specializations only in the appropriate namespaces (and
+// nowhere else).
+struct NonDefaultConstructible {
+  NonDefaultConstructible(int);
+};
+
+
+// C++ [temp.expl.spec]p1:
+//   An explicit specialization of any of the following:
+
+//     -- function template
+namespace N0 {
+  template<typename T> void f0(T) { // expected-note{{here}}
+    T t;
+  }
+
+  template<> void f0(NonDefaultConstructible) { }
+
+  void test_f0(NonDefaultConstructible NDC) {
+    f0(NDC);
+  }
+  
+  template<> void f0(int);
+  template<> void f0(long);
+}
+
+template<> void N0::f0(int) { } // okay
+
+namespace N1 {
+  template<> void N0::f0(long) { } // expected-error{{not in a namespace enclosing}}
+}
+
+template<> void N0::f0(double) { } // expected-error{{originally be declared}}
+
+// FIXME: update the remainder of this test to check for scopes properly.
+//     -- class template
+template<typename T>
+struct X0 {
+  static T member;
+  
+  void f1(T t) {
+    t = 17;
+  }
+  
+  struct Inner : public T { };
+  
+  template<typename U>
+  struct InnerTemplate : public T { };
+  
+  template<typename U>
+  void ft1(T t, U u);
+};
+
+template<typename T> 
+template<typename U>
+void X0<T>::ft1(T t, U u) {
+  t = u;
+}
+
+template<typename T> T X0<T>::member;
+
+template<> struct X0<void> { };
+X0<void> test_X0;
+
+
+//     -- member function of a class template
+template<> void X0<void*>::f1(void *) { }
+
+void test_spec(X0<void*> xvp, void *vp) {
+  xvp.f1(vp);
+}
+
+//     -- static data member of a class template
+template<> 
+NonDefaultConstructible X0<NonDefaultConstructible>::member = 17;
+
+NonDefaultConstructible &get_static_member() {
+  return X0<NonDefaultConstructible>::member;
+}
+
+//    -- member class of a class template
+template<>
+struct X0<void*>::Inner { };
+
+X0<void*>::Inner inner0;
+
+//    -- member class template of a class template
+template<>
+template<>
+struct X0<void*>::InnerTemplate<int> { };
+
+X0<void*>::InnerTemplate<int> inner_template0;
+
+//    -- member function template of a class template
+template<>
+template<>
+void X0<void*>::ft1(void*, const void*) { }
+
+void test_func_template(X0<void *> xvp, void *vp, const void *cvp) {
+  xvp.ft1(vp, cvp);
+}
+
+// example from the standard:
+template<class T> class stream;
+template<> class stream<char> { /* ... */ };
+template<class T> class Array { /* ... */ }; 
+template<class T> void sort(Array<T>& v) { /* ... */ }
+template<> void sort<char*>(Array<char*>&) ;
index 34c616cc2f42dd42edef3454b983f0945c17a358..e4d917f7756d5f2a9925e87c161e930934322ec1 100644 (file)
@@ -85,12 +85,12 @@ namespace N {
 
 template<> struct N::B<int> { }; // okay
 
-template<> struct N::B<float> { }; // expected-error{{class template specialization of 'B' not in namespace 'N'}}
+template<> struct N::B<float> { }; // expected-error{{originally}}
 
 namespace M {
   template<> struct ::N::B<short> { }; // expected-error{{class template specialization of 'B' not in a namespace enclosing 'N'}}
 
-  template<> struct ::A<long double>; // expected-error{{class template specialization of 'A' must occur in the global scope}}
+  template<> struct ::A<long double>; // expected-error{{originally}}
 }
 
 template<> struct N::B<char> { 
index b50bd8f634aaa1ef8c27f6d22095e43eb33a27b2..a029f474fc58179f51a37f2e1f43e070192b53ed 100644 (file)
@@ -9,7 +9,7 @@ namespace N {
 }
 
 template<typename T>
-struct N::M::A<T*> { }; // expected-error{{not in namespace}}
+struct N::M::A<T*> { }; // expected-error{{originally}}
 
 // C++ [temp.class.spec]p9
 //   bullet 1
@@ -25,7 +25,7 @@ template <class T, T* t> struct C<T*, t>; // okay
 
 template< int X, int (*array_ptr)[X] > class A2 {}; // expected-note{{here}}
 int array[5]; 
-template< int X > class A2<X,&array> { }; // expected-error{{specializes}}
+template< int X > class A2<X, &array> { }; // expected-error{{specializes}}
 
 template<typename T, int N, template<typename X> class TT>
 struct Test0;