From: Douglas Gregor Date: Fri, 25 Sep 2009 18:43:00 +0000 (+0000) Subject: WIP implementation of explicit instantiation of function templates, X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d5a423b279e787e9fdd8309fe52cb515388c54ea;p=clang WIP implementation of explicit instantiation of function templates, member functions of class template specializations, and static data members. The mechanics are (mostly) present, but the semantic analysis is very weak. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82789 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclarationName.h b/include/clang/AST/DeclarationName.h index 9850e8fff0..9f9bfa6e3d 100644 --- a/include/clang/AST/DeclarationName.h +++ b/include/clang/AST/DeclarationName.h @@ -16,6 +16,7 @@ #include "clang/Basic/IdentifierTable.h" #include "clang/AST/Type.h" #include "clang/AST/CanonicalType.h" +#include "clang/Basic/PartialDiagnostic.h" namespace llvm { template struct DenseMapInfo; @@ -327,7 +328,15 @@ inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, return DB; } - +/// Insertion operator for partial diagnostics. This allows binding +/// DeclarationName's into a partial diagnostic with <<. +inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, + DeclarationName N) { + PD.AddTaggedVal(N.getAsOpaqueInteger(), + Diagnostic::ak_declarationname); + return PD; +} + } // end namespace clang namespace llvm { diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 106b6a3e02..095b7ab387 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1036,7 +1036,26 @@ def note_nontemplate_decl_here : Note< "non-templated declaration is here">; def err_explicit_instantiation_out_of_scope : Error< "explicit instantiation of %0 not in a namespace enclosing %1">; - +def err_explicit_instantiation_requires_name : Error< + "explicit instantiation declaration requires a name">; +def err_explicit_instantiation_of_typedef : Error< + "explicit instantiation of typedef %0">; +def err_explicit_instantiation_not_known : Error< + "explicit instantiation of %0 does not refer to a function template, member " + "function, member class, or static data member">; +def note_explicit_instantiation_here : Note< + "explicit instantiation refers here">; +def err_explicit_instantiation_data_member_not_instantiated : Error< + "explicit instantiation refers to static data member %q0 that is not an " + "instantiation">; +def err_explicit_instantiation_member_function_not_instantiated : Error< + "explicit instantiation refers to member function %q0 that is not an " + "instantiation">; +def err_explicit_instantiation_ambiguous : Error< + "partial ordering for explicit instantiation of %0 is ambiguous">; +def note_explicit_instantiation_candidate : Note< + "explicit instantiation candidate function template here %0">; + // C++ typename-specifiers def err_typename_nested_not_found : Error<"no type named %0 in %1">; def err_typename_nested_not_found_global : Error< diff --git a/include/clang/Basic/PartialDiagnostic.h b/include/clang/Basic/PartialDiagnostic.h index b4ba793e7e..d893dd3aef 100644 --- a/include/clang/Basic/PartialDiagnostic.h +++ b/include/clang/Basic/PartialDiagnostic.h @@ -18,9 +18,12 @@ #include "clang/AST/Type.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/STLExtras.h" namespace clang { +class DeclarationName; + class PartialDiagnostic { struct Storage { Storage() : NumDiagArgs(0), NumDiagRanges(0) { } @@ -132,6 +135,9 @@ public: PD.AddSourceRange(R); return PD; } + + friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, + DeclarationName N); }; inline PartialDiagnostic PDiag(unsigned DiagID) { diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 8fdf779acd..4be5a763a9 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -1898,6 +1898,38 @@ public: return DeclResult(); } + /// \brief Process the explicit instantiation of a function template or a + /// member of a class template. + /// + /// This routine is invoked when an explicit instantiation of a + /// function template or member function of a class template specialization + /// is encountered. In the following example, + /// ActOnExplicitInstantiation will be invoked to force the + /// instantiation of X: + /// + /// \code + /// template void f(T); + /// template void f(int); // explicit instantiation + /// \endcode + /// + /// \param S the current scope + /// + /// \param ExternLoc the location of the 'extern' keyword that specifies that + /// this is an extern template (if any). + /// + /// \param TemplateLoc the location of the 'template' keyword that + /// specifies that this is an explicit instantiation. + /// + /// \param D the declarator describing the declaration to be implicitly + /// instantiated. + virtual DeclResult ActOnExplicitInstantiation(Scope *S, + SourceLocation ExternLoc, + SourceLocation TemplateLoc, + Declarator &D) { + return DeclResult(); + } + + /// \brief Called when the parser has parsed a C++ typename /// specifier that ends in an identifier, e.g., "typename T::type". /// diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 75ff82772a..9525eb364c 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -419,13 +419,36 @@ Parser::DeclPtrTy Parser::ParseDeclarationAfterDeclarator(Declarator &D, } // Inform the current actions module that we just parsed this declarator. - DeclPtrTy ThisDecl = TemplateInfo.TemplateParams? - Actions.ActOnTemplateDeclarator(CurScope, + DeclPtrTy ThisDecl; + switch (TemplateInfo.Kind) { + case ParsedTemplateInfo::NonTemplate: + ThisDecl = Actions.ActOnDeclarator(CurScope, D); + break; + + case ParsedTemplateInfo::Template: + case ParsedTemplateInfo::ExplicitSpecialization: + ThisDecl = Actions.ActOnTemplateDeclarator(CurScope, Action::MultiTemplateParamsArg(Actions, TemplateInfo.TemplateParams->data(), TemplateInfo.TemplateParams->size()), - D) - : Actions.ActOnDeclarator(CurScope, D); + D); + break; + + case ParsedTemplateInfo::ExplicitInstantiation: { + Action::DeclResult ThisRes + = Actions.ActOnExplicitInstantiation(CurScope, + TemplateInfo.ExternLoc, + TemplateInfo.TemplateLoc, + D); + if (ThisRes.isInvalid()) { + SkipUntil(tok::semi, true, true); + return DeclPtrTy(); + } + + ThisDecl = ThisRes.get(); + break; + } + } // Parse declarator '=' initializer. if (Tok.is(tok::equal)) { diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 8377d50939..56f4693cd2 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2550,6 +2550,11 @@ public: SourceLocation NameLoc, AttributeList *Attr); + virtual DeclResult ActOnExplicitInstantiation(Scope *S, + SourceLocation ExternLoc, + SourceLocation TemplateLoc, + Declarator &D); + bool CheckTemplateArgumentList(TemplateDecl *Template, SourceLocation TemplateLoc, SourceLocation LAngleLoc, @@ -2779,6 +2784,15 @@ public: FunctionTemplateDecl *getMoreSpecializedTemplate(FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, TemplatePartialOrderingContext TPOC); + FunctionDecl *getMostSpecialized(FunctionDecl **Specializations, + unsigned NumSpecializations, + TemplatePartialOrderingContext TPOC, + SourceLocation Loc, + const PartialDiagnostic &NoneDiag, + const PartialDiagnostic &AmbigDiag, + const PartialDiagnostic &CandidateDiag, + unsigned *Index = 0); + ClassTemplatePartialSpecializationDecl * getMoreSpecializedPartialSpecialization( ClassTemplatePartialSpecializationDecl *PS1, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 509237a855..37fbf45374 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/Parse/DeclSpec.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/PartialDiagnostic.h" #include "llvm/Support/Compiler.h" #include "llvm/ADT/StringExtras.h" using namespace clang; @@ -3326,6 +3327,180 @@ Sema::ActOnExplicitInstantiation(Scope *S, return TagD; } +Sema::DeclResult Sema::ActOnExplicitInstantiation(Scope *S, + SourceLocation ExternLoc, + SourceLocation TemplateLoc, + Declarator &D) { + // Explicit instantiations always require a name. + DeclarationName Name = GetNameForDeclarator(D); + if (!Name) { + if (!D.isInvalidType()) + Diag(D.getDeclSpec().getSourceRange().getBegin(), + diag::err_explicit_instantiation_requires_name) + << D.getDeclSpec().getSourceRange() + << D.getSourceRange(); + + return true; + } + + // The scope passed in may not be a decl scope. Zip up the scope tree until + // we find one that is. + while ((S->getFlags() & Scope::DeclScope) == 0 || + (S->getFlags() & Scope::TemplateParamScope) != 0) + S = S->getParent(); + + // Determine the type of the declaration. + QualType R = GetTypeForDeclarator(D, S, 0); + if (R.isNull()) + return true; + + if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_typedef) { + // Cannot explicitly instantiate a typedef. + Diag(D.getIdentifierLoc(), diag::err_explicit_instantiation_of_typedef) + << Name; + return true; + } + + // Determine what kind of explicit instantiation we have. + TemplateSpecializationKind TSK + = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition + : TSK_ExplicitInstantiationDeclaration; + + LookupResult Previous = LookupParsedName(S, &D.getCXXScopeSpec(), + Name, LookupOrdinaryName); + + if (!R->isFunctionType()) { + // C++ [temp.explicit]p1: + // A [...] static data member of a class template can be explicitly + // instantiated from the member definition associated with its class + // template. + if (Previous.isAmbiguous()) { + return DiagnoseAmbiguousLookup(Previous, Name, D.getIdentifierLoc(), + D.getSourceRange()); + } + + VarDecl *Prev = dyn_cast_or_null(Previous.getAsDecl()); + if (!Prev || !Prev->isStaticDataMember()) { + // We expect to see a data data member here. + Diag(D.getIdentifierLoc(), diag::err_explicit_instantiation_not_known) + << Name; + for (LookupResult::iterator P = Previous.begin(), PEnd = Previous.end(); + P != PEnd; ++P) + Diag(P->getLocation(), diag::note_explicit_instantiation_here); + return true; + } + + if (!Prev->getInstantiatedFromStaticDataMember()) { + // FIXME: Check for explicit specialization? + Diag(D.getIdentifierLoc(), + diag::err_explicit_instantiation_data_member_not_instantiated) + << Prev; + Diag(Prev->getLocation(), diag::note_explicit_instantiation_here); + // FIXME: Can we provide a note showing where this was declared? + return true; + } + + // Instantiate static data member. + // FIXME: Note that this is an explicit instantiation. + if (TSK == TSK_ExplicitInstantiationDefinition) + InstantiateStaticDataMemberDefinition(D.getIdentifierLoc(), Prev, false); + + // FIXME: Create an ExplicitInstantiation node? + return DeclPtrTy(); + } + + // C++ [temp.explicit]p1: + // A [...] function [...] can be explicitly instantiated from its template. + // A member function [...] of a class template can be explicitly + // instantiated from the member definition associated with its class + // template. + // FIXME: Implement this! + llvm::SmallVector Matches; + for (LookupResult::iterator P = Previous.begin(), PEnd = Previous.end(); + P != PEnd; ++P) { + NamedDecl *Prev = *P; + if (CXXMethodDecl *Method = dyn_cast(Prev)) { + // FIXME: If there were any explicitly-specified template arguments, + // don't look for Method declarations. + if (Context.hasSameUnqualifiedType(Method->getType(), R)) { + Matches.clear(); + Matches.push_back(Method); + break; + } + } + + FunctionTemplateDecl *FunTmpl = dyn_cast(Prev); + if (!FunTmpl) + continue; + + TemplateDeductionInfo Info(Context); + FunctionDecl *Specialization = 0; + if (TemplateDeductionResult TDK + = DeduceTemplateArguments(FunTmpl, /*FIXME:*/false, 0, 0, + R, Specialization, Info)) { + // FIXME: Keep track of almost-matches? + (void)TDK; + continue; + } + + Matches.push_back(Specialization); + } + + // Find the most specialized function template specialization. + FunctionDecl *Specialization + = getMostSpecialized(Matches.data(), Matches.size(), TPOC_Other, + D.getIdentifierLoc(), + PartialDiagnostic(diag::err_explicit_instantiation_not_known) << Name, + PartialDiagnostic(diag::err_explicit_instantiation_ambiguous) << Name, + PartialDiagnostic(diag::note_explicit_instantiation_candidate)); + + if (!Specialization) + return true; + + switch (Specialization->getTemplateSpecializationKind()) { + case TSK_Undeclared: + Diag(D.getIdentifierLoc(), + diag::err_explicit_instantiation_member_function_not_instantiated) + << Specialization + << (Specialization->getTemplateSpecializationKind() == + TSK_ExplicitSpecialization); + Diag(Specialization->getLocation(), diag::note_explicit_instantiation_here); + return true; + + case TSK_ExplicitSpecialization: + // C++ [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. + break; + + case TSK_ExplicitInstantiationDefinition: + // FIXME: Check that we aren't trying to perform an explicit instantiation + // declaration now. + // Fall through + + case TSK_ImplicitInstantiation: + case TSK_ExplicitInstantiationDeclaration: + // Instantiate the function, if this is an explicit instantiation + // definition. + if (TSK == TSK_ExplicitInstantiationDefinition) + InstantiateFunctionDefinition(D.getIdentifierLoc(), Specialization, + false); + + // FIXME: setTemplateSpecializationKind doesn't (yet) work for + // non-templated member functions. + if (!Specialization->getPrimaryTemplate()) + break; + + Specialization->setTemplateSpecializationKind(TSK); + break; + } + + // FIXME: Create some kind of ExplicitInstantiationDecl here. + return DeclPtrTy(); +} + Sema::TypeResult Sema::ActOnDependentTag(Scope *S, unsigned TagSpec, TagUseKind TUK, const CXXScopeSpec &SS, IdentifierInfo *Name, diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index b5aa075bd6..64b7f8b140 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -1885,6 +1885,124 @@ Sema::getMoreSpecializedTemplate(FunctionTemplateDecl *FT1, return 0; } +/// \brief Determine if the two templates are equivalent. +static bool isSameTemplate(TemplateDecl *T1, TemplateDecl *T2) { + if (T1 == T2) + return true; + + if (!T1 || !T2) + return false; + + return T1->getCanonicalDecl() == T2->getCanonicalDecl(); +} + +/// \brief Retrieve the most specialized of the given function template +/// specializations. +/// +/// \param Specializations the set of function template specializations that +/// we will be comparing. +/// +/// \param NumSpecializations the number of function template specializations in +/// \p Specializations +/// +/// \param TPOC the partial ordering context to use to compare the function +/// template specializations. +/// +/// \param Loc the location where the ambiguity or no-specializations +/// diagnostic should occur. +/// +/// \param NoneDiag partial diagnostic used to diagnose cases where there are +/// no matching candidates. +/// +/// \param AmbigDiag partial diagnostic used to diagnose an ambiguity, if one +/// occurs. +/// +/// \param CandidateDiag partial diagnostic used for each function template +/// specialization that is a candidate in the ambiguous ordering. One parameter +/// in this diagnostic should be unbound, which will correspond to the string +/// describing the template arguments for the function template specialization. +/// +/// \param Index if non-NULL and the result of this function is non-nULL, +/// receives the index corresponding to the resulting function template +/// specialization. +/// +/// \returns the most specialized function template specialization, if +/// found. Otherwise, returns NULL. +/// +/// \todo FIXME: Consider passing in the "also-ran" candidates that failed +/// template argument deduction. +FunctionDecl *Sema::getMostSpecialized(FunctionDecl **Specializations, + unsigned NumSpecializations, + TemplatePartialOrderingContext TPOC, + SourceLocation Loc, + const PartialDiagnostic &NoneDiag, + const PartialDiagnostic &AmbigDiag, + const PartialDiagnostic &CandidateDiag, + unsigned *Index) { + if (NumSpecializations == 0) { + Diag(Loc, NoneDiag); + return 0; + } + + if (NumSpecializations == 1) { + if (Index) + *Index = 0; + + return Specializations[0]; + } + + + // Find the function template that is better than all of the templates it + // has been compared to. + unsigned Best = 0; + FunctionTemplateDecl *BestTemplate + = Specializations[Best]->getPrimaryTemplate(); + assert(BestTemplate && "Not a function template specialization?"); + for (unsigned I = 1; I != NumSpecializations; ++I) { + FunctionTemplateDecl *Challenger = Specializations[I]->getPrimaryTemplate(); + assert(Challenger && "Not a function template specialization?"); + if (isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger, + TPOC), + Challenger)) { + Best = I; + BestTemplate = Challenger; + } + } + + // Make sure that the "best" function template is more specialized than all + // of the others. + bool Ambiguous = false; + for (unsigned I = 0; I != NumSpecializations; ++I) { + FunctionTemplateDecl *Challenger = Specializations[I]->getPrimaryTemplate(); + if (I != Best && + !isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger, + TPOC), + BestTemplate)) { + Ambiguous = true; + break; + } + } + + if (!Ambiguous) { + // We found an answer. Return it. + if (Index) + *Index = Best; + return Specializations[Best]; + } + + // Diagnose the ambiguity. + Diag(Loc, AmbigDiag); + + // FIXME: Can we order the candidates in some sane way? + for (unsigned I = 0; I != NumSpecializations; ++I) + Diag(Specializations[I]->getLocation(), CandidateDiag) + << getTemplateArgumentBindingsText( + Specializations[I]->getPrimaryTemplate()->getTemplateParameters(), + *Specializations[I]->getTemplateSpecializationArgs()); + + return 0; +} + /// \brief Returns the more specialized class template partial specialization /// according to the rules of partial ordering of class template partial /// specializations (C++ [temp.class.order]). diff --git a/test/Parser/cxx-template-decl.cpp b/test/Parser/cxx-template-decl.cpp index 94b7069afb..7f1ff3dc31 100644 --- a/test/Parser/cxx-template-decl.cpp +++ b/test/Parser/cxx-template-decl.cpp @@ -2,7 +2,8 @@ // Errors export class foo { }; // expected-error {{expected template}} -template x; // expected-error {{C++ requires a type specifier for all declarations}} +template x; // expected-error {{C++ requires a type specifier for all declarations}} \ + // expected-error {{does not refer}} export template x; // expected-error {{expected '<' after 'template'}} export template class x0; // expected-note {{exported templates are unsupported}} template < ; // expected-error {{parse error}} expected-error {{declaration does not declare anything}} diff --git a/test/SemaTemplate/explicit-instantiation.cpp b/test/SemaTemplate/explicit-instantiation.cpp new file mode 100644 index 0000000000..f92c4635cc --- /dev/null +++ b/test/SemaTemplate/explicit-instantiation.cpp @@ -0,0 +1,57 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +template void *; // expected-error{{expected unqualified-id}} + +template typedef void f0; // expected-error{{explicit instantiation of typedef}} + +int v0; // expected-note{{refers here}} +template int v0; // expected-error{{does not refer}} + +template +struct X0 { + static T value; + + T f0(T x) { + return x + 1; // expected-error{{invalid operands}} + } + T* f0(T*, T*); + + template + T f0(T, U); +}; + +template int X0::value; + +struct NotDefaultConstructible { + NotDefaultConstructible(int); +}; + +template NotDefaultConstructible X0::value; + +template int X0::f0(int); +template int* X0::f0(int*, int*); +template int X0::f0(int, float); + +template int X0::f0(int) const; // expected-error{{does not refer}} +template int* X0::f0(int*, float*); // expected-error{{does not refer}} + +struct X1 { }; +typedef int X1::*MemPtr; + +template MemPtr X0::f0(MemPtr); // expected-note{{requested here}} + +struct X2 { + int f0(int); // expected-note{{refers here}} + + template T f1(T); + template T* f1(T*); + + template void f2(T, U*); // expected-note{{candidate}} + template void f2(T*, U); // expected-note{{candidate}} +}; + +template int X2::f0(int); // expected-error{{not an instantiation}} + +template int *X2::f1(int *); // okay + +template void X2::f2(int *, int *); // expected-error{{ambiguous}}