From ff66803b43f2ea9206637dceb793e9505f3b9c48 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 13 May 2009 18:28:20 +0000 Subject: [PATCH] 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@71689 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 25 +++-- lib/Sema/Sema.h | 3 +- lib/Sema/SemaTemplate.cpp | 119 ++++++++++++++++----- lib/Sema/SemaTemplateInstantiate.cpp | 24 ++++- test/SemaTemplate/temp_explicit.cpp | 10 +- test/SemaTemplate/temp_explicit_cxx0x.cpp | 24 +++++ 6 files changed, 158 insertions(+), 47 deletions(-) create mode 100644 test/SemaTemplate/temp_explicit_cxx0x.cpp diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index c459192b29..377a333b1e 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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">; diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 8c38c2bfd2..46120bcb38 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index c80bb882f6..adf9ea4941 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -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(TemplateContext)) - Diag(TemplateNameLoc, diag::err_template_spec_redecl_global_scope) - << ClassTemplate << ScopeSpecifierRange; - else if (isa(TemplateContext)) - Diag(TemplateNameLoc, diag::err_template_spec_redecl_out_of_scope) - << ClassTemplate << cast(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(TemplateContext)) { + if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x) + Diag(TemplateNameLoc, diag::err_template_spec_redecl_global_scope) + << ExplicitInstantiation << ClassTemplate << ScopeSpecifierRange; + else + SuppressedDiag = true; + } else if (isa(TemplateContext)) { + if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x) + Diag(TemplateNameLoc, diag::err_template_spec_redecl_out_of_scope) + << ExplicitInstantiation << ClassTemplate + << cast(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 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); } diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index ce8cbe0f43..3a2f677755 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -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); diff --git a/test/SemaTemplate/temp_explicit.cpp b/test/SemaTemplate/temp_explicit.cpp index 527532cfa9..b6009bd9c1 100644 --- a/test/SemaTemplate/temp_explicit.cpp +++ b/test/SemaTemplate/temp_explicit.cpp @@ -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 class X0 { }; @@ -24,13 +24,13 @@ template class X0 { }; // expected-error{{explicit specialization}} // Check for explicit instantiations that come after other kinds of // instantiations or declarations. -template class X0; // expected-error{{after}} +template class X0; // expected-error{{duplicate}} template<> class X0 { }; // expected-note{{previous}} -template class X0; // expected-error{{after}} +template class X0; // expected-warning{{ignored}} -void foo(X0) { } // expected-note{{previous}} -template class X0; // expected-error{{after}} +void foo(X0) { } +template class X0; // Check that explicit instantiations actually produce definitions. We // determine whether this happens by placing semantic errors in the diff --git a/test/SemaTemplate/temp_explicit_cxx0x.cpp b/test/SemaTemplate/temp_explicit_cxx0x.cpp new file mode 100644 index 0000000000..7045afc303 --- /dev/null +++ b/test/SemaTemplate/temp_explicit_cxx0x.cpp @@ -0,0 +1,24 @@ +// RUN: clang-cc -fsyntax-only -std=c++0x -verify %s +namespace N1 { + + template struct X0 { }; // expected-note{{here}} + + namespace Inner { + template struct X1 { }; + } + + template struct X0; + template struct Inner::X1; +} + +template struct X2 { }; // expected-note{{here}} + +template struct ::N1::Inner::X1; + +namespace N2 { + using namespace N1; + + template struct X0; // expected-error{{not in a namespace enclosing}} + + template struct X2; // expected-error{{at global scope}} +} -- 2.40.0