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:
parent
69921959b4
commit
f61eca93c0
|
@ -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">;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}}
|
||||
}
|
Loading…
Reference in New Issue