[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