[cfe-commits] r65476 - in /cfe/trunk: 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

Douglas Gregor dgregor at apple.com
Wed Feb 25 14:02:03 PST 2009


Author: dgregor
Date: Wed Feb 25 16:02:03 2009
New Revision: 65476

URL: http://llvm.org/viewvc/llvm-project?rev=65476&view=rev
Log:
Perform additional semantic checking of class template
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).


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

Modified: cfe/trunk/include/clang/AST/DeclBase.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclBase.h?rev=65476&r1=65475&r2=65476&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/DeclBase.h (original)
+++ cfe/trunk/include/clang/AST/DeclBase.h Wed Feb 25 16:02:03 2009
@@ -490,6 +490,12 @@
   }
   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

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def?rev=65476&r1=65475&r2=65476&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def Wed Feb 25 16:02:03 2009
@@ -605,6 +605,24 @@
 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,

Modified: cfe/trunk/lib/AST/DeclBase.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclBase.cpp?rev=65476&r1=65475&r2=65476&view=diff

==============================================================================
--- cfe/trunk/lib/AST/DeclBase.cpp (original)
+++ cfe/trunk/lib/AST/DeclBase.cpp Wed Feb 25 16:02:03 2009
@@ -555,6 +555,14 @@
   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

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=65476&r1=65475&r2=65476&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Wed Feb 25 16:02:03 2009
@@ -64,6 +64,7 @@
   class TemplateArgument;
   class TemplateParameterList;
   class TemplateTemplateParmDecl;
+  class ClassTemplateDecl;
   class ObjCInterfaceDecl;
   class ObjCCompatibleAliasDecl;
   class ObjCProtocolDecl;
@@ -1532,6 +1533,11 @@
                        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, 

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=65476&r1=65475&r2=65476&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Wed Feb 25 16:02:03 2009
@@ -1520,6 +1520,77 @@
     << 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 @@
                                        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 @@
 
   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

Modified: cfe/trunk/test/SemaTemplate/class-template-spec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/class-template-spec.cpp?rev=65476&r1=65475&r2=65476&view=diff

==============================================================================
--- cfe/trunk/test/SemaTemplate/class-template-spec.cpp (original)
+++ cfe/trunk/test/SemaTemplate/class-template-spec.cpp Wed Feb 25 16:02:03 2009
@@ -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 @@
 
 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 @@
 }
 
 // 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}}
+}





More information about the cfe-commits mailing list