forked from OSchip/llvm-project
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:
parent
32a59b2315
commit
f47b911f6e
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue