Improve the semantic checking for explicit instantiations of

templates. In particular:
  - An explicit instantiation can follow an implicit instantiation (we
  were improperly diagnosing this as an error, previously).
  - In C++0x, an explicit instantiation that follows an explicit
  specialization of the same template specialization is ignored. In
  C++98, we just emit an extension warning.
  - In C++0x, an explicit instantiation must be in a namespace
  enclosing the original template. C++98 has no such requirement.

Also, fixed a longstanding FIXME regarding the integral type that is
used for the size of a constant array type when it is being instantiated.

llvm-svn: 71689
This commit is contained in:
Douglas Gregor 2009-05-13 18:28:20 +00:00
parent 69921959b4
commit f61eca93c0
6 changed files with 158 additions and 47 deletions

View File

@ -694,11 +694,14 @@ def err_template_spec_decl_out_of_scope_global : Error<
def err_template_spec_decl_out_of_scope : Error<
"class template specialization of %0 not in namespace %1">;
def err_template_spec_decl_function_scope : Error<
"class template specialization of %0 in function scope">;
"%select{class template specialization|explicit instantiation}0 of %1 "
"in function scope">;
def err_template_spec_redecl_out_of_scope : Error<
"class template specialization of %0 not in a namespace enclosing %1">;
"%select{class template specialization|explicit instantiation}0 of %1 "
"not in a namespace enclosing %2">;
def err_template_spec_redecl_global_scope : Error<
"class template specialization of %0 must occur in at global scope">;
"%select{class template specialization|explicit instantiation}0 of %1 must "
"occur at global scope">;
// C++ Template Instantiation
def err_template_recursion_depth_exceeded : Error<
@ -722,13 +725,15 @@ def err_nested_name_spec_non_tag : Error<
"type %0 cannot be used prior to '::' because it has no members">;
// C++ Explicit Instantiation
def err_explicit_instantiation_redef : Error<
"explicit instantiation of %0 occurs after "
"%select{|explicit specialization|implicit instantiation|explicit "
"instantiation}1">;
def note_previous_instantiation : Note<
"previous %select{|explicit specialization|implicit instantiation|explicit "
"instantiation}0 is here">;
def err_explicit_instantiation_duplicate : Error<
"duplicate explicit instantiation of %0">;
def note_previous_explicit_instantiation : Note<
"previous explicit instantiation is here">;
def ext_explicit_instantiation_after_specialization : Extension<
"explicit instantiation of %0 that occurs after an explicit "
"specialization will be ignored (C++0x extension)">;
def note_previous_template_specialization : Note<
"previous template specialization is here">;
// C++ typename-specifiers
def err_typename_nested_not_found : Error<"no type named %0 in %1">;

View File

@ -1882,7 +1882,8 @@ public:
bool CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate,
ClassTemplateSpecializationDecl *PrevDecl,
SourceLocation TemplateNameLoc,
SourceRange ScopeSpecifierRange);
SourceRange ScopeSpecifierRange,
bool ExplicitInstantiation);
virtual DeclResult
ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK,

View File

@ -1892,19 +1892,20 @@ Sema::CheckTemplateDeclScope(Scope *S,
<< TemplateRange;
}
/// \brief Check whether a class template specialization in the
/// current context is well-formed.
/// \brief Check whether a class template specialization or explicit
/// instantiation 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.
/// This routine determines whether a class 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) {
SourceRange ScopeSpecifierRange,
bool ExplicitInstantiation) {
// 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
@ -1920,14 +1921,15 @@ Sema::CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate,
// declared.
if (CurContext->getLookupContext()->isFunctionOrMethod()) {
Diag(TemplateNameLoc, diag::err_template_spec_decl_function_scope)
<< ClassTemplate;
<< ExplicitInstantiation << ClassTemplate;
return true;
}
DeclContext *DC = CurContext->getEnclosingNamespaceContext();
DeclContext *TemplateContext
= ClassTemplate->getDeclContext()->getEnclosingNamespaceContext();
if (!PrevDecl || PrevDecl->getSpecializationKind() == TSK_Undeclared) {
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.
@ -1949,15 +1951,26 @@ Sema::CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate,
// 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;
// FIXME: In C++98, we would like to turn these errors into
// warnings, dependent on a -Wc++0x flag.
bool SuppressedDiag = false;
if (isa<TranslationUnitDecl>(TemplateContext)) {
if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x)
Diag(TemplateNameLoc, diag::err_template_spec_redecl_global_scope)
<< ExplicitInstantiation << ClassTemplate << ScopeSpecifierRange;
else
SuppressedDiag = true;
} else if (isa<NamespaceDecl>(TemplateContext)) {
if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x)
Diag(TemplateNameLoc, diag::err_template_spec_redecl_out_of_scope)
<< ExplicitInstantiation << ClassTemplate
<< cast<NamedDecl>(TemplateContext) << ScopeSpecifierRange;
else
SuppressedDiag = true;
}
Diag(ClassTemplate->getLocation(), diag::note_template_decl_here);
if (!SuppressedDiag)
Diag(ClassTemplate->getLocation(), diag::note_template_decl_here);
}
return false;
@ -2056,7 +2069,8 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK,
// the current scope.
if (CheckClassTemplateSpecializationScope(ClassTemplate, PrevDecl,
TemplateNameLoc,
SS.getRange()))
SS.getRange(),
/*ExplicitInstantiation=*/false))
return true;
if (PrevDecl && PrevDecl->getSpecializationKind() == TSK_Undeclared) {
@ -2179,6 +2193,17 @@ Sema::ActOnExplicitInstantiation(Scope *S, SourceLocation TemplateLoc,
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(),
/*ExplicitInstantiation=*/true))
return true;
// Translate the parser's template argument list in our AST format.
llvm::SmallVector<TemplateArgument, 16> TemplateArgs;
translateTemplateArguments(TemplateArgsIn, TemplateArgLocs, TemplateArgs);
@ -2206,18 +2231,55 @@ Sema::ActOnExplicitInstantiation(Scope *S, SourceLocation TemplateLoc,
ClassTemplateSpecializationDecl *Specialization = 0;
bool SpecializationRequiresInstantiation = true;
if (PrevDecl) {
if (PrevDecl->getSpecializationKind() != TSK_Undeclared) {
if (PrevDecl->getSpecializationKind() == TSK_ExplicitInstantiation) {
// This particular specialization has already been declared or
// instantiated. We cannot explicitly instantiate it.
Diag(TemplateNameLoc, diag::err_explicit_instantiation_redef)
<< Context.getTypeDeclType(PrevDecl)
<< (int)PrevDecl->getSpecializationKind();
Diag(PrevDecl->getLocation(), diag::note_previous_instantiation)
<< (int)PrevDecl->getSpecializationKind();
Diag(TemplateNameLoc, diag::err_explicit_instantiation_duplicate)
<< Context.getTypeDeclType(PrevDecl);
Diag(PrevDecl->getLocation(),
diag::note_previous_explicit_instantiation);
return DeclPtrTy::make(PrevDecl);
}
if (PrevDecl->getSpecializationKind() == TSK_ExplicitSpecialization) {
// C++0x [temp.explicit]p4:
// For a given set of template parameters, if an explicit
// instantiation of a template appears after a declaration of
// an explicit specialization for that template, the explicit
// instantiation has no effect.
if (!getLangOptions().CPlusPlus0x) {
Diag(TemplateNameLoc,
diag::ext_explicit_instantiation_after_specialization)
<< Context.getTypeDeclType(PrevDecl);
Diag(PrevDecl->getLocation(),
diag::note_previous_template_specialization);
}
// Create a new class template specialization declaration node
// for this explicit specialization. This node is only used to
// record the existence of this explicit instantiation for
// accurate reproduction of the source code; we don't actually
// use it for anything, since it is semantically irrelevant.
Specialization
= ClassTemplateSpecializationDecl::Create(Context,
ClassTemplate->getDeclContext(),
TemplateNameLoc,
ClassTemplate,
&ConvertedTemplateArgs[0],
ConvertedTemplateArgs.size(),
0);
Specialization->setLexicalDeclContext(CurContext);
CurContext->addDecl(Context, Specialization);
return DeclPtrTy::make(Specialization);
}
// If we have already (implicitly) instantiated this
// specialization, there is less work to do.
if (PrevDecl->getSpecializationKind() == TSK_ImplicitInstantiation)
SpecializationRequiresInstantiation = false;
// Since the only prior class template specialization with these
// arguments was referenced but not declared, reuse that
// declaration node as our own, updating its source location to
@ -2263,16 +2325,19 @@ Sema::ActOnExplicitInstantiation(Scope *S, SourceLocation TemplateLoc,
CurContext->addDecl(Context, Specialization);
// C++ [temp.explicit]p3:
//
// A definition of a class template or class member template
// shall be in scope at the point of the explicit instantiation of
// the class template or class member template.
//
// This check comes when we actually try to perform the
// instantiation.
if (InstantiateClassTemplateSpecialization(Specialization, true))
if (SpecializationRequiresInstantiation &&
InstantiateClassTemplateSpecialization(Specialization, true))
return true;
// FIXME: Instantiate all of the members of the template (that
// haven't already been instantiated!).
return DeclPtrTy::make(Specialization);
}

View File

@ -247,10 +247,26 @@ InstantiateConstantArrayType(const ConstantArrayType *T,
// Build a temporary integer literal to specify the size for
// BuildArrayType. Since we have already checked the size as part of
// creating the dependent array type in the first place, we know
// there aren't any errors.
// FIXME: Is IntTy big enough? Maybe not, but LongLongTy causes
// problems that I have yet to investigate.
IntegerLiteral ArraySize(T->getSize(), SemaRef.Context.IntTy, Loc);
// there aren't any errors. However, we do need to determine what
// C++ type to give the size expression.
llvm::APInt Size = T->getSize();
QualType Types[] = {
SemaRef.Context.UnsignedCharTy, SemaRef.Context.UnsignedShortTy,
SemaRef.Context.UnsignedIntTy, SemaRef.Context.UnsignedLongTy,
SemaRef.Context.UnsignedLongLongTy, SemaRef.Context.UnsignedInt128Ty
};
const unsigned NumTypes = sizeof(Types) / sizeof(QualType);
QualType SizeType;
for (unsigned I = 0; I != NumTypes; ++I)
if (Size.getBitWidth() == SemaRef.Context.getIntWidth(Types[I])) {
SizeType = Types[I];
break;
}
if (SizeType.isNull())
SizeType = SemaRef.Context.getFixedWidthIntType(Size.getBitWidth(), false);
IntegerLiteral ArraySize(Size, SizeType, Loc);
return SemaRef.BuildArrayType(ElementType, T->getSizeModifier(),
&ArraySize, T->getIndexTypeQualifier(),
Loc, Entity);

View File

@ -1,4 +1,4 @@
// RUN: clang-cc -fsyntax-only -verify %s
// RUN: clang-cc -fsyntax-only -verify -pedantic %s
//
// Tests explicit instantiation of templates.
template<typename T, typename U = T> class X0 { };
@ -24,13 +24,13 @@ template class X0<double> { }; // expected-error{{explicit specialization}}
// Check for explicit instantiations that come after other kinds of
// instantiations or declarations.
template class X0<int, int>; // expected-error{{after}}
template class X0<int, int>; // expected-error{{duplicate}}
template<> class X0<char> { }; // expected-note{{previous}}
template class X0<char>; // expected-error{{after}}
template class X0<char>; // expected-warning{{ignored}}
void foo(X0<short>) { } // expected-note{{previous}}
template class X0<short>; // expected-error{{after}}
void foo(X0<short>) { }
template class X0<short>;
// Check that explicit instantiations actually produce definitions. We
// determine whether this happens by placing semantic errors in the

View File

@ -0,0 +1,24 @@
// RUN: clang-cc -fsyntax-only -std=c++0x -verify %s
namespace N1 {
template<typename T> struct X0 { }; // expected-note{{here}}
namespace Inner {
template<typename T> struct X1 { };
}
template struct X0<int>;
template struct Inner::X1<int>;
}
template<typename T> struct X2 { }; // expected-note{{here}}
template struct ::N1::Inner::X1<float>;
namespace N2 {
using namespace N1;
template struct X0<double>; // expected-error{{not in a namespace enclosing}}
template struct X2<float>; // expected-error{{at global scope}}
}