Refactor checking of the scope of explicit template specialization

declarations and explicit template instantiations, improving
diagnostics and making the code usable for function template
specializations (as well as class template specializations and partial
specializations). 

llvm-svn: 83436
This commit is contained in:
Douglas Gregor 2009-10-07 00:13:32 +00:00
parent 75b59fb055
commit 548886518d
7 changed files with 297 additions and 113 deletions

View File

@ -916,6 +916,34 @@ def err_template_arg_not_pointer_to_member_form : Error<
def err_template_arg_extra_parens : Error<
"non-type template argument cannot be surrounded by parentheses">;
// C++ template specialization
def err_template_spec_unknown_kind : Error<
"can only provide an explicit %select{<error>|<error>|specialization|"
"instantiation|instantiation}0 for a class template, function template, or "
"a member function, static data member, or member class of a class template">;
def note_specialized_entity : Note<
"explicit %select{<error>|<error>|specialized|instantiated|instantiated}0 is "
"here">;
def err_template_spec_decl_function_scope : Error<
"explicit %select{<error>|<error>|specialization|instantiation|"
"instantiation}0 of %1 in function scope">;
def err_template_spec_decl_out_of_scope_global : Error<
"%select{class template|class template partial|function template|member "
"function|static data member|member class}0 specialization of %1 must "
"originally be declared in the global scope">;
def err_template_spec_decl_out_of_scope : Error<
"%select{class template|class template partial|function template|member "
"function|static data member|member class}0 specialization of %1 must "
"originally be declared in namespace %2">;
def err_template_spec_redecl_out_of_scope : Error<
"%select{class template|class template partial|function template|member "
"function|static data member|member class}0 specialization of %1 not in a "
"namespace enclosing %2">;
def err_template_spec_redecl_global_scope : Error<
"%select{class template|class template partial|function template|member "
"function|static data member|member class}0 specialization of %1 must occur "
"at global scope">;
// C++ class template specializations and out-of-line definitions
def err_template_spec_needs_header : Error<
"template specialization requires 'template<>'">;
@ -931,20 +959,6 @@ def err_template_spec_extra_headers : Error<
def err_template_qualified_declarator_no_match : Error<
"nested name specifier %0 for declaration does not refer into a class, "
"class template or class template partial specialization">;
def err_template_spec_decl_out_of_scope_global : Error<
"class template %select{|partial }0specialization of %1 must occur in the "
"global scope">;
def err_template_spec_decl_out_of_scope : Error<
"class template %select{|partial }0specialization of %1 not in namespace %2">;
def err_template_spec_decl_function_scope : Error<
"%select{class template specialization|class template partial specialization|"
"explicit instantiation}0 of %1 in function scope">;
def err_template_spec_redecl_out_of_scope : Error<
"%select{class template specialization|class template partial specialization|"
"explicit instantiation}0 of %1 not in a namespace enclosing %2">;
def err_template_spec_redecl_global_scope : Error<
"%select{class template specialization|class template partial specialization|"
"explicit instantiation}0 of %1 must occur at global scope">;
// C++ Class Template Partial Specialization
def err_default_arg_in_partial_spec : Error<
@ -978,7 +992,7 @@ def err_function_template_spec_ambiguous : Error<
"arguments to identify a particular function template">;
def note_function_template_spec_matched : Note<
"function template matches specialization %0">;
// C++ Template Instantiation
def err_template_recursion_depth_exceeded : Error<
"recursive template instantiation exceeded maximum depth of %0">,

View File

@ -2485,13 +2485,6 @@ public:
const CXXScopeSpec &SS,
TypeTy *ObjectType);
bool CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate,
ClassTemplateSpecializationDecl *PrevDecl,
SourceLocation TemplateNameLoc,
SourceRange ScopeSpecifierRange,
bool PartialSpecialization,
bool ExplicitInstantiation);
bool CheckClassTemplatePartialSpecializationArgs(
TemplateParameterList *TemplateParams,
const TemplateArgumentListBuilder &TemplateArgs,

View File

@ -2345,21 +2345,76 @@ Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) {
<< TemplateParams->getSourceRange();
}
/// \brief Check whether a class template specialization or explicit
/// instantiation in the current context is well-formed.
/// \brief Determine what kind of template specialization the given declaration
/// is.
static TemplateSpecializationKind getTemplateSpecializationKind(NamedDecl *D) {
if (!D)
return TSK_Undeclared;
if (ClassTemplateSpecializationDecl *CTS
= dyn_cast<ClassTemplateSpecializationDecl>(D))
return CTS->getSpecializationKind();
if (FunctionDecl *Function = dyn_cast<FunctionDecl>(D))
return Function->getTemplateSpecializationKind();
// FIXME: static data members!
// FIXME: member classes of class templates!
return TSK_Undeclared;
}
/// \brief Check whether a specialization or explicit instantiation is
/// well-formed in the current context.
///
/// This routine determines whether a class template specialization or
/// This routine determines whether a template specialization or
/// explicit instantiation can be declared in the current context
/// (C++ [temp.expl.spec]p2, C++0x [temp.explicit]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,
bool PartialSpecialization,
bool ExplicitInstantiation) {
/// (C++ [temp.expl.spec]p2, C++0x [temp.explicit]p2).
///
/// \param S the semantic analysis object for which this check is being
/// performed.
///
/// \param Specialized the entity being specialized or instantiated, which
/// may be a kind of template (class template, function template, etc.) or
/// a member of a class template (member function, static data member,
/// member class).
///
/// \param PrevDecl the previous declaration of this entity, if any.
///
/// \param Loc the location of the explicit specialization or instantiation of
/// this entity.
///
/// \param IsPartialSpecialization whether this is a partial specialization of
/// a class template.
///
/// \param TSK the kind of specialization or implicit instantiation being
/// performed.
///
/// \returns true if there was an error that we cannot recover from, false
/// otherwise.
static bool CheckTemplateSpecializationScope(Sema &S,
NamedDecl *Specialized,
NamedDecl *PrevDecl,
SourceLocation Loc,
bool IsPartialSpecialization,
TemplateSpecializationKind TSK) {
// Keep these "kind" numbers in sync with the %select statements in the
// various diagnostics emitted by this routine.
int EntityKind = 0;
if (isa<ClassTemplateDecl>(Specialized))
EntityKind = IsPartialSpecialization? 1 : 0;
else if (isa<FunctionTemplateDecl>(Specialized))
EntityKind = 2;
else if (isa<CXXMethodDecl>(Specialized))
EntityKind = 3;
else if (isa<VarDecl>(Specialized))
EntityKind = 4;
else if (isa<RecordDecl>(Specialized))
EntityKind = 5;
else {
S.Diag(Loc, diag::err_template_spec_unknown_kind) << TSK;
S.Diag(Specialized->getLocation(), diag::note_specialized_entity) << TSK;
return true;
}
// 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
@ -2373,66 +2428,68 @@ Sema::CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate,
// the explicit specialization was declared, or in a namespace
// that encloses the one in which the explicit specialization was
// declared.
if (CurContext->getLookupContext()->isFunctionOrMethod()) {
int Kind = ExplicitInstantiation? 2 : PartialSpecialization? 1 : 0;
Diag(TemplateNameLoc, diag::err_template_spec_decl_function_scope)
<< Kind << ClassTemplate;
if (S.CurContext->getLookupContext()->isFunctionOrMethod()) {
S.Diag(Loc, diag::err_template_spec_decl_function_scope)
<< TSK << Specialized;
return true;
}
DeclContext *DC = CurContext->getEnclosingNamespaceContext();
DeclContext *TemplateContext
= ClassTemplate->getDeclContext()->getEnclosingNamespaceContext();
if ((!PrevDecl || PrevDecl->getSpecializationKind() == TSK_Undeclared) &&
!ExplicitInstantiation) {
// 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)
<< PartialSpecialization
<< ClassTemplate << ScopeSpecifierRange;
else if (isa<NamespaceDecl>(TemplateContext))
Diag(TemplateNameLoc, diag::err_template_spec_decl_out_of_scope)
<< PartialSpecialization << ClassTemplate
<< cast<NamedDecl>(TemplateContext) << ScopeSpecifierRange;
Diag(ClassTemplate->getLocation(), diag::note_template_decl_here);
bool ComplainedAboutScope = false;
DeclContext *SpecializedContext
= Specialized->getDeclContext()->getEnclosingNamespaceContext();
if (TSK == TSK_ExplicitSpecialization) {
DeclContext *DC = S.CurContext->getEnclosingNamespaceContext();
if ((!PrevDecl ||
getTemplateSpecializationKind(PrevDecl) == TSK_Undeclared ||
getTemplateSpecializationKind(PrevDecl) == TSK_ImplicitInstantiation)){
// There is no prior declaration of this entity, so this
// specialization must be in the same context as the template
// itself.
if (!DC->Equals(SpecializedContext)) {
if (isa<TranslationUnitDecl>(SpecializedContext))
S.Diag(Loc, diag::err_template_spec_decl_out_of_scope_global)
<< EntityKind << Specialized;
else if (isa<NamespaceDecl>(SpecializedContext))
S.Diag(Loc, diag::err_template_spec_decl_out_of_scope)
<< EntityKind << Specialized
<< cast<NamedDecl>(SpecializedContext);
S.Diag(Specialized->getLocation(), diag::note_template_decl_here);
ComplainedAboutScope = true;
}
}
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)) {
// FIXME: In C++98, we would like to turn these errors into warnings,
// dependent on a -Wc++0x flag.
bool SuppressedDiag = false;
int Kind = ExplicitInstantiation? 2 : PartialSpecialization? 1 : 0;
if (isa<TranslationUnitDecl>(TemplateContext)) {
if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x)
Diag(TemplateNameLoc, diag::err_template_spec_redecl_global_scope)
<< Kind << ClassTemplate << ScopeSpecifierRange;
else
SuppressedDiag = true;
} else if (isa<NamespaceDecl>(TemplateContext)) {
if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x)
Diag(TemplateNameLoc, diag::err_template_spec_redecl_out_of_scope)
<< Kind << ClassTemplate
<< cast<NamedDecl>(TemplateContext) << ScopeSpecifierRange;
else
SuppressedDiag = true;
}
if (!SuppressedDiag)
Diag(ClassTemplate->getLocation(), diag::note_template_decl_here);
// Make sure that this redeclaration (or definition) occurs in an enclosing
// namespace. We perform this check for explicit specializations and, in
// C++0x, for explicit instantiations as well (per DR275).
// FIXME: -Wc++0x should make these warnings.
// Note that HandleDeclarator() performs this check for explicit
// specializations of function templates, static data members, and member
// functions, so we skip the check here for those kinds of entities.
// FIXME: HandleDeclarator's diagnostics aren't quite as good, though.
// Should we refactor the check, so that it occurs later?
if (!ComplainedAboutScope && !S.CurContext->Encloses(SpecializedContext) &&
((TSK == TSK_ExplicitSpecialization &&
!(isa<FunctionTemplateDecl>(Specialized) || isa<VarDecl>(Specialized) ||
isa<FunctionDecl>(Specialized))) ||
S.getLangOptions().CPlusPlus0x)) {
if (isa<TranslationUnitDecl>(SpecializedContext))
S.Diag(Loc, diag::err_template_spec_redecl_global_scope)
<< EntityKind << Specialized;
else if (isa<NamespaceDecl>(SpecializedContext))
S.Diag(Loc, diag::err_template_spec_redecl_out_of_scope)
<< EntityKind << Specialized
<< cast<NamedDecl>(SpecializedContext);
S.Diag(Specialized->getLocation(), diag::note_template_decl_here);
}
// FIXME: check for specialization-after-instantiation errors and such.
return false;
}
/// \brief Check the non-type template arguments of a class template
/// partial specialization according to C++ [temp.class.spec]p9.
///
@ -2714,11 +2771,9 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec,
// Check whether we can declare a class template specialization in
// the current scope.
if (TUK != TUK_Friend &&
CheckClassTemplateSpecializationScope(ClassTemplate, PrevDecl,
TemplateNameLoc,
SS.getRange(),
isPartialSpecialization,
/*ExplicitInstantiation=*/false))
CheckTemplateSpecializationScope(*this, ClassTemplate, PrevDecl,
TemplateNameLoc, isPartialSpecialization,
TSK_ExplicitSpecialization))
return true;
// The canonical type
@ -3008,6 +3063,13 @@ Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD,
// FIXME: Check if the prior specialization has a point of instantiation.
// If so, we have run afoul of C++ [temp.expl.spec]p6.
// Check the scope of this explicit specialization.
if (CheckTemplateSpecializationScope(*this,
Specialization->getPrimaryTemplate(),
Specialization, FD->getLocation(),
false, TSK_ExplicitSpecialization))
return true;
// Mark the prior declaration as an explicit specialization, so that later
// clients know that this is an explicit specialization.
// FIXME: Check for prior explicit instantiations?
@ -3071,18 +3133,10 @@ Sema::ActOnExplicitInstantiation(Scope *S,
Kind = ClassTemplate->getTemplatedDecl()->getTagKind();
}
// C++0x [temp.explicit]p2:
// [...] An explicit instantiation shall appear in an enclosing
// namespace of its template. [...]
//
// This is C++ DR 275.
if (CheckClassTemplateSpecializationScope(ClassTemplate, 0,
TemplateNameLoc,
SS.getRange(),
/*PartialSpecialization=*/false,
/*ExplicitInstantiation=*/true))
return true;
TemplateSpecializationKind TSK
= ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition
: TSK_ExplicitInstantiationDeclaration;
// Translate the parser's template argument list in our AST format.
llvm::SmallVector<TemplateArgument, 16> TemplateArgs;
translateTemplateArguments(TemplateArgsIn, TemplateArgLocs, TemplateArgs);
@ -3111,6 +3165,16 @@ Sema::ActOnExplicitInstantiation(Scope *S,
ClassTemplateSpecializationDecl *PrevDecl
= ClassTemplate->getSpecializations().FindNodeOrInsertPos(ID, InsertPos);
// C++0x [temp.explicit]p2:
// [...] An explicit instantiation shall appear in an enclosing
// namespace of its template. [...]
//
// This is C++ DR 275.
if (CheckTemplateSpecializationScope(*this, ClassTemplate, PrevDecl,
TemplateNameLoc, false,
TSK))
return true;
ClassTemplateSpecializationDecl *Specialization = 0;
bool SpecializationRequiresInstantiation = true;
@ -3224,9 +3288,6 @@ Sema::ActOnExplicitInstantiation(Scope *S,
//
// This check comes when we actually try to perform the
// instantiation.
TemplateSpecializationKind TSK
= ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition
: TSK_ExplicitInstantiationDeclaration;
if (SpecializationRequiresInstantiation)
InstantiateClassTemplateSpecialization(Specialization, TSK);
else // Instantiate the members of this class template specialization.

View File

@ -1,8 +1,10 @@
// RUN: clang-cc -fsyntax-only %s
// RUN: clang-cc -fsyntax-only -verify %s
// This test creates cases where implicit instantiations of various entities
// would cause a diagnostic, but provides expliict specializations for those
// entities that avoid the diagnostic. The intent is to verify that
// implicit instantiations do not occur (because the explicit specialization
// is used instead).
struct NonDefaultConstructible {
NonDefaultConstructible(int);
};

View File

@ -0,0 +1,114 @@
// RUN: clang-cc -fsyntax-only -verify %s
// This test creates cases where implicit instantiations of various entities
// would cause a diagnostic, but provides expliict specializations for those
// entities that avoid the diagnostic. The specializations are alternately
// declarations and definitions, and the intent of this test is to verify
// that we allow specializations only in the appropriate namespaces (and
// nowhere else).
struct NonDefaultConstructible {
NonDefaultConstructible(int);
};
// C++ [temp.expl.spec]p1:
// An explicit specialization of any of the following:
// -- function template
namespace N0 {
template<typename T> void f0(T) { // expected-note{{here}}
T t;
}
template<> void f0(NonDefaultConstructible) { }
void test_f0(NonDefaultConstructible NDC) {
f0(NDC);
}
template<> void f0(int);
template<> void f0(long);
}
template<> void N0::f0(int) { } // okay
namespace N1 {
template<> void N0::f0(long) { } // expected-error{{not in a namespace enclosing}}
}
template<> void N0::f0(double) { } // expected-error{{originally be declared}}
// FIXME: update the remainder of this test to check for scopes properly.
// -- class template
template<typename T>
struct X0 {
static T member;
void f1(T t) {
t = 17;
}
struct Inner : public T { };
template<typename U>
struct InnerTemplate : public T { };
template<typename U>
void ft1(T t, U u);
};
template<typename T>
template<typename U>
void X0<T>::ft1(T t, U u) {
t = u;
}
template<typename T> T X0<T>::member;
template<> struct X0<void> { };
X0<void> test_X0;
// -- member function of a class template
template<> void X0<void*>::f1(void *) { }
void test_spec(X0<void*> xvp, void *vp) {
xvp.f1(vp);
}
// -- static data member of a class template
template<>
NonDefaultConstructible X0<NonDefaultConstructible>::member = 17;
NonDefaultConstructible &get_static_member() {
return X0<NonDefaultConstructible>::member;
}
// -- member class of a class template
template<>
struct X0<void*>::Inner { };
X0<void*>::Inner inner0;
// -- member class template of a class template
template<>
template<>
struct X0<void*>::InnerTemplate<int> { };
X0<void*>::InnerTemplate<int> inner_template0;
// -- member function template of a class template
template<>
template<>
void X0<void*>::ft1(void*, const void*) { }
void test_func_template(X0<void *> xvp, void *vp, const void *cvp) {
xvp.ft1(vp, cvp);
}
// example from the standard:
template<class T> class stream;
template<> class stream<char> { /* ... */ };
template<class T> class Array { /* ... */ };
template<class T> void sort(Array<T>& v) { /* ... */ }
template<> void sort<char*>(Array<char*>&) ;

View File

@ -85,12 +85,12 @@ namespace N {
template<> struct N::B<int> { }; // okay
template<> struct N::B<float> { }; // expected-error{{class template specialization of 'B' not in namespace 'N'}}
template<> struct N::B<float> { }; // expected-error{{originally}}
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}}
template<> struct ::A<long double>; // expected-error{{originally}}
}
template<> struct N::B<char> {

View File

@ -9,7 +9,7 @@ namespace N {
}
template<typename T>
struct N::M::A<T*> { }; // expected-error{{not in namespace}}
struct N::M::A<T*> { }; // expected-error{{originally}}
// C++ [temp.class.spec]p9
// bullet 1
@ -25,7 +25,7 @@ template <class T, T* t> struct C<T*, t>; // okay
template< int X, int (*array_ptr)[X] > class A2 {}; // expected-note{{here}}
int array[5];
template< int X > class A2<X,&array> { }; // expected-error{{specializes}}
template< int X > class A2<X, &array> { }; // expected-error{{specializes}}
template<typename T, int N, template<typename X> class TT>
struct Test0;