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).

llvm-svn: 65476
This commit is contained in:
Douglas Gregor 2009-02-25 22:02:03 +00:00
parent 32a59b2315
commit f47b911f6e
6 changed files with 173 additions and 22 deletions

View 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

View 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,

View 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

View 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,

View 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

View 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}}
}