]> granicus.if.org Git - clang/commitdiff
Perform additional semantic checking of class template
authorDouglas Gregor <dgregor@apple.com>
Wed, 25 Feb 2009 22:02:03 +0000 (22:02 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 25 Feb 2009 22:02:03 +0000 (22:02 +0000)
specializations. In particular:

  - Make sure class template specializations have a "template<>"
    header, and complain if they don't.
  - Make sure class template specializations are declared/defined
    within a valid context. (e.g., you can't declare a specialization
    std::vector<MyType> in the global namespace).

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

include/clang/AST/DeclBase.h
include/clang/Basic/DiagnosticSemaKinds.def
lib/AST/DeclBase.cpp
lib/Sema/Sema.h
lib/Sema/SemaTemplate.cpp
test/SemaTemplate/class-template-spec.cpp

index 312cdf093cd41a046f4e1759d96da4e55d99f40c..1357a8eaacfdd037aefe6d31d118c9f43a471205 100644 (file)
@@ -490,6 +490,12 @@ public:
   }
   const DeclContext *getLookupContext() const;
 
+  /// \brief Retrieve the nearest enclosing namespace context.
+  DeclContext *getEnclosingNamespaceContext();
+  const DeclContext *getEnclosingNamespaceContext() const {
+    return const_cast<DeclContext *>(this)->getEnclosingNamespaceContext();
+  }
+
   /// getNextContext - If this is a DeclContext that may have other
   /// DeclContexts that are semantically connected but syntactically
   /// different, such as C++ namespaces, this routine retrieves the
index e5e7f6eacf1525b3be816b1c9397c51bbd17a926..5cd8c6be967dd1ce64f0f7145d8b9d86ce3f925c 100644 (file)
@@ -605,6 +605,24 @@ DIAG(err_template_arg_not_pointer_to_member_form, ERROR,
 DIAG(err_template_arg_extra_parens, ERROR,
      "non-type template argument cannot be surrounded by parentheses")
 
+// C++ class template specialization
+DIAG(err_template_spec_needs_header, ERROR,
+     "template specialization requires 'template<>'")
+DIAG(err_template_spec_extra_headers, ERROR,
+     "template specialization must have a single 'template<>' header")
+DIAG(unsup_template_partial_spec, ERROR,
+     "class template partial specialization is not yet supported")
+DIAG(err_template_spec_decl_out_of_scope_global, ERROR,
+     "class template specialization of %0 must occur in the global scope")
+DIAG(err_template_spec_decl_out_of_scope, ERROR,
+     "class template specialization of %0 not in namespace %1")
+DIAG(err_template_spec_decl_function_scope, ERROR,
+     "class template specialization of %0 in function scope")
+DIAG(err_template_spec_redecl_out_of_scope, ERROR,
+     "class template specialization of %0 not in a namespace enclosing %1")
+DIAG(err_template_spec_redecl_global_scope, ERROR,
+     "class template specialization of %0 must occur in at global scope")
+
 DIAG(err_unexpected_typedef, ERROR,
      "unexpected type name %0: expected expression")
 DIAG(err_unexpected_namespace, ERROR,
index 18712cde35822ac6604120222aca992b8dcc2e44..20139d7bb2312a2fe92318063fed6e47c66c5b18 100644 (file)
@@ -555,6 +555,14 @@ const DeclContext *DeclContext::getLookupContext() const {
   return Ctx;
 }
 
+DeclContext *DeclContext::getEnclosingNamespaceContext() {
+  DeclContext *Ctx = this;
+  // Skip through non-namespace, non-translation-unit contexts.
+  while (!Ctx->isFileContext() || Ctx->isTransparentContext())
+    Ctx = Ctx->getParent();
+  return Ctx->getPrimaryContext();
+}
+
 void DeclContext::makeDeclVisibleInContext(NamedDecl *D) {
   // FIXME: This feels like a hack. Should DeclarationName support
   // template-ids, or is there a better way to keep specializations
index fed2bbdf7c4077744f2607a822d700a7617dc5d7..1d36d46296bb28c5fe676b9060cf8ccab1113a15 100644 (file)
@@ -64,6 +64,7 @@ namespace clang {
   class TemplateArgument;
   class TemplateParameterList;
   class TemplateTemplateParmDecl;
+  class ClassTemplateDecl;
   class ObjCInterfaceDecl;
   class ObjCCompatibleAliasDecl;
   class ObjCProtocolDecl;
@@ -1532,6 +1533,11 @@ public:
                        SourceLocation RAngleLoc,
                        const CXXScopeSpec *SS);
   
+  bool CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate,
+                                    ClassTemplateSpecializationDecl *PrevDecl,
+                                             SourceLocation TemplateNameLoc,
+                                             SourceRange ScopeSpecifierRange);
+
   virtual DeclTy *
   ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK,
                                    SourceLocation KWLoc, 
index e3538e8a4ce7c7b0ecc455c9bfebd4e268d85ef9..db582edba2ef99ed93ee3cdfd6aed44b311a6d05 100644 (file)
@@ -1520,6 +1520,77 @@ Sema::CheckTemplateDeclScope(Scope *S,
     << TemplateRange;
 }
 
+/// \brief Check whether a class template specialization in the
+/// current context is well-formed.
+///
+/// This routine determines whether a class template specialization
+/// can be declared in the current context (C++ [temp.expl.spec]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) {
+  // 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
+  //   the namespace of which the enclosing class or enclosing class
+  //   template is a member. An explicit specialization of a member
+  //   function, member class or static data member of a class
+  //   template shall be declared in the namespace of which the class
+  //   template is a member. Such a declaration may also be a
+  //   definition. If the declaration is not a definition, the
+  //   specialization may be defined later in the name- space in which
+  //   the explicit specialization was declared, or in a namespace
+  //   that encloses the one in which the explicit specialization was
+  //   declared.
+  if (CurContext->getLookupContext()->isFunctionOrMethod()) {
+    Diag(TemplateNameLoc, diag::err_template_spec_decl_function_scope)
+      << ClassTemplate;
+    return true;
+  }
+
+  DeclContext *DC = CurContext->getEnclosingNamespaceContext();
+  DeclContext *TemplateContext 
+    = ClassTemplate->getDeclContext()->getEnclosingNamespaceContext();
+  if (!PrevDecl || PrevDecl->getSpecializationKind() == TSK_Undeclared) {
+    // 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)
+          << ClassTemplate << ScopeSpecifierRange;
+      else if (isa<NamespaceDecl>(TemplateContext))
+        Diag(TemplateNameLoc, diag::err_template_spec_decl_out_of_scope)
+          << ClassTemplate << cast<NamedDecl>(TemplateContext) 
+          << ScopeSpecifierRange;
+
+      Diag(ClassTemplate->getLocation(), diag::note_template_decl_here);
+    }
+
+    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)) {
+    if (isa<TranslationUnitDecl>(TemplateContext))
+      Diag(TemplateNameLoc, diag::err_template_spec_redecl_global_scope)
+        << ClassTemplate << ScopeSpecifierRange;
+    else if (isa<NamespaceDecl>(TemplateContext))
+      Diag(TemplateNameLoc, diag::err_template_spec_redecl_out_of_scope)
+        << ClassTemplate << cast<NamedDecl>(TemplateContext) 
+        << ScopeSpecifierRange;
+    
+    Diag(ClassTemplate->getLocation(), diag::note_template_decl_here);
+  }
+
+  return false;
+}
+
 Sema::DeclTy *
 Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK,
                                        SourceLocation KWLoc, 
@@ -1532,23 +1603,36 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK,
                                        SourceLocation RAngleLoc,
                                        AttributeList *Attr,
                                MultiTemplateParamsArg TemplateParameterLists) {
-  // FIXME: We need to match up the scope-specifier with the template
-  // parameter lists, and will eventually end up with a single
-  // template parameter list remaining (which applies to
-  // TemplateIdType).
-  assert(TemplateParameterLists.size() == 1 && 
-         "Clang doesn't handle with ill-formed specializations yet.");
-
-  assert(static_cast<TemplateParameterList*>(*TemplateParameterLists.get())
-         ->size() == 0 &&
-         "Clang doesn't handle class template partial specializations yet");
-
   // Find the class template we're specializing
   ClassTemplateDecl *ClassTemplate 
     = dyn_cast_or_null<ClassTemplateDecl>(static_cast<Decl *>(TemplateD));
   if (!ClassTemplate)
     return 0;
 
+  // Check the validity of the template headers that introduce this
+  // template.
+  if (TemplateParameterLists.size() == 0) {
+    // FIXME: It would be very nifty if we could introduce some kind
+    // of "code insertion hint" that could show the code that needs to
+    // be added.
+    Diag(KWLoc, diag::err_template_spec_needs_header);
+  } else {
+    TemplateParameterList *TemplateParams 
+      = static_cast<TemplateParameterList*>(*TemplateParameterLists.get());
+    if (TemplateParameterLists.size() > 1) {
+      Diag(TemplateParams->getTemplateLoc(),
+           diag::err_template_spec_extra_headers);
+      return 0;
+    }
+
+    if (TemplateParams->size() > 0) {
+      // FIXME: No support for class template partial specialization.
+      Diag(TemplateParams->getTemplateLoc(), 
+           diag::unsup_template_partial_spec);
+      return 0;
+    }
+  }
+
   // Check that the specialization uses the same tag kind as the
   // original template.
   TagDecl::TagKind Kind;
@@ -1588,6 +1672,13 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK,
 
   ClassTemplateSpecializationDecl *Specialization = 0;
 
+  // Check whether we can declare a class template specialization in
+  // the current scope.
+  if (CheckClassTemplateSpecializationScope(ClassTemplate, PrevDecl,
+                                            TemplateNameLoc, 
+                                            SS.getRange()))
+    return 0;
+
   if (PrevDecl && PrevDecl->getSpecializationKind() == TSK_Undeclared) {
     // Since the only prior class template specialization with these
     // arguments was referenced but not declared, reuse that
index 4de220a77cb58b327561e96ae3abe78f2618fce7..4ae1b9bdc9789afff0fbf528ed70b7c61eab5b2b 100644 (file)
@@ -1,13 +1,13 @@
 // RUN: clang -fsyntax-only -verify %s
-template<typename T, typename U = int> class A;
+template<typename T, typename U = int> struct A; // expected-note{{template is declared here}}
 
-template<> class A<double, double>; // expected-note{{forward declaration}}
+template<> struct A<double, double>; // expected-note{{forward declaration}}
 
-template<> class A<float, float> {  // expected-note{{previous definition}}
+template<> struct A<float, float> {  // expected-note{{previous definition}}
   int x;
 };
 
-template<> class A<float> { // expected-note{{previous definition}}
+template<> struct A<float> { // expected-note{{previous definition}}
   int y;
 };
 
@@ -24,16 +24,16 @@ int test_incomplete_specs(A<double, double> *a1,
 
 typedef float FLOAT;
 
-template<> class A<float, FLOAT>;
+template<> struct A<float, FLOAT>;
 
-template<> class A<FLOAT, float> { }; // expected-error{{redefinition}}
+template<> struct A<FLOAT, float> { }; // expected-error{{redefinition}}
 
-template<> class A<float, int> { }; // expected-error{{redefinition}}
+template<> struct A<float, int> { }; // expected-error{{redefinition}}
 
-template<typename T, typename U = int> class X;
+template<typename T, typename U = int> struct X;
 
-template <> class X<int, int> { int foo(); }; // #1
-template <> class X<float> { int bar(); };  // #2
+template <> struct X<int, int> { int foo(); }; // #1
+template <> struct X<float> { int bar(); };  // #2
 
 typedef int int_type;
 void testme(X<int_type> *x1, X<float, int> *x2) { 
@@ -42,4 +42,26 @@ void testme(X<int_type> *x1, X<float, int> *x2) {
 }
 
 // Diagnose specializations in a different namespace
-class A<double> { };
+struct A<double> { }; // expected-error{{template specialization requires 'template<>'}}
+
+template<typename T> // expected-error{{class template partial specialization is not yet supported}}
+struct A<T*> { };
+
+template<> struct ::A<double>;
+
+namespace N {
+  template<typename T> struct B; // expected-note 2{{template is declared here}}
+
+  template<> struct ::N::B<short>; // okay
+  template<> struct ::N::B<int>; // okay
+}
+
+template<> struct N::B<int> { }; // okay
+
+template<> struct N::B<float> { }; // expected-error{{class template specialization of 'B' not in namespace 'N'}}
+
+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}}
+}