diff options
author | Douglas Gregor <dgregor@apple.com> | 2009-09-24 23:14:47 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2009-09-24 23:14:47 +0000 |
commit | b9aa6b214c8fbc3e081dde575eef1f0913d48bdc (patch) | |
tree | 77bf753a91b1d025823f3adc0a285c0d973beab9 | |
parent | 119fa68eb1394a2653bb651dc5c1cf294cbbdbda (diff) |
WIP implementation of explicit function template specialization. This
first implementation recognizes when a function declaration is an
explicit function template specialization (based on the presence of a
template<> header), performs template argument deduction + ambiguity
resolution to determine which template is being specialized, and hooks
There are many caveats here:
- We completely and totally drop any explicitly-specified template
arguments on the floor
- We don't diagnose any of the extra semantic things that we should
diagnose.
- I haven't looked to see that we're getting the right linkage for
explicit specializations
On a happy note, this silences a bunch of errors that show up in
libstdc++'s <iostream>, although Clang still can't get through the
entire header.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@82728 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/AST/Decl.h | 15 | ||||
-rw-r--r-- | include/clang/AST/DeclTemplate.h | 3 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 10 | ||||
-rw-r--r-- | lib/AST/Decl.cpp | 22 | ||||
-rw-r--r-- | lib/AST/DeclTemplate.cpp | 6 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 8 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 26 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.cpp | 3 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 160 | ||||
-rw-r--r-- | lib/Sema/SemaTemplateDeduction.cpp | 2 | ||||
-rw-r--r-- | test/SemaTemplate/function-template-specialization.cpp | 25 |
11 files changed, 269 insertions, 11 deletions
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 4ea5e1a734..0b02ee26a8 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -1081,6 +1081,12 @@ public: TemplateOrSpecialization = Template; } + /// \brief Determine whether this function is a function template + /// specialization. + bool isFunctionTemplateSpecialization() const { + return getPrimaryTemplate() != 0; + } + /// \brief Retrieve the primary template that this function template /// specialization either specializes or was instantiated from. /// @@ -1105,10 +1111,17 @@ public: /// /// \param TemplateArgs the template arguments that produced this /// function template specialization from the template. + /// + /// \param InsertPos If non-NULL, the position in the function template + /// specialization set where the function template specialization data will + /// be inserted. + /// + /// \param TSK the kind of template specialization this is. void setFunctionTemplateSpecialization(ASTContext &Context, FunctionTemplateDecl *Template, const TemplateArgumentList *TemplateArgs, - void *InsertPos); + void *InsertPos, + TemplateSpecializationKind TSK = TSK_ImplicitInstantiation); /// \brief Determine what kind of template instantiation this function /// represents. diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index fb33758a2e..5d3e8eab43 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -419,6 +419,9 @@ public: TemplateArgumentListBuilder &Builder, bool TakeArgs); + /// \brief Produces a shallow copy of the given template argument list + TemplateArgumentList(const TemplateArgumentList &Other); + ~TemplateArgumentList(); /// \brief Retrieve the template argument at a given index. diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index cc7bb31638..106b6a3e02 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -967,6 +967,16 @@ def err_partial_spec_ordering_ambiguous : Error< "ambiguous partial specializations of %0">; def note_partial_spec_match : Note<"partial specialization matches %0">; +// C++ Function template specializations +def err_function_template_spec_no_match : Error< + "no function template matches function template specialization %0">; +def err_function_template_spec_ambiguous : Error< + "function template specialization %0 ambiguously refers to more than one " + "function template; explicitly specify%select{|additional }1 template " + "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">, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 1a7aaac6f7..25d3d44cc8 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -689,7 +689,10 @@ void FunctionDecl::setFunctionTemplateSpecialization(ASTContext &Context, FunctionTemplateDecl *Template, const TemplateArgumentList *TemplateArgs, - void *InsertPos) { + void *InsertPos, + TemplateSpecializationKind TSK) { + assert(TSK != TSK_Undeclared && + "Must specify the type of function template specialization"); FunctionTemplateSpecializationInfo *Info = TemplateOrSpecialization.dyn_cast<FunctionTemplateSpecializationInfo*>(); if (!Info) @@ -697,13 +700,24 @@ FunctionDecl::setFunctionTemplateSpecialization(ASTContext &Context, Info->Function = this; Info->Template.setPointer(Template); - Info->Template.setInt(TSK_ImplicitInstantiation - 1); + Info->Template.setInt(TSK - 1); Info->TemplateArguments = TemplateArgs; TemplateOrSpecialization = Info; // Insert this function template specialization into the set of known - // function template specialiations. - Template->getSpecializations().InsertNode(Info, InsertPos); + // function template specializations. + if (InsertPos) + Template->getSpecializations().InsertNode(Info, InsertPos); + else { + // Try to insert the new node. If there is an existing node, remove it + // first. + FunctionTemplateSpecializationInfo *Existing + = Template->getSpecializations().GetOrInsertNode(Info); + if (Existing) { + Template->getSpecializations().RemoveNode(Existing); + Template->getSpecializations().GetOrInsertNode(Info); + } + } } TemplateSpecializationKind FunctionDecl::getTemplateSpecializationKind() const { diff --git a/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp index bd1aa7ca9e..7836b3f827 100644 --- a/lib/AST/DeclTemplate.cpp +++ b/lib/AST/DeclTemplate.cpp @@ -373,6 +373,12 @@ TemplateArgumentList::TemplateArgumentList(ASTContext &Context, Builder.ReleaseArgs(); } +TemplateArgumentList::TemplateArgumentList(const TemplateArgumentList &Other) + : FlatArguments(Other.FlatArguments.getPointer(), 1), + NumFlatArguments(Other.flat_size()), + StructuredArguments(Other.StructuredArguments.getPointer(), 1), + NumStructuredArguments(Other.NumStructuredArguments) { } + TemplateArgumentList::~TemplateArgumentList() { // FIXME: Deallocate template arguments } diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 542acb6058..18b56c5bfb 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2515,6 +2515,14 @@ public: MultiTemplateParamsArg TemplateParameterLists, Declarator &D); + bool CheckFunctionTemplateSpecialization(FunctionDecl *FD, + bool HasExplicitTemplateArgs, + SourceLocation LAngleLoc, + const TemplateArgument *ExplicitTemplateArgs, + unsigned NumExplicitTemplateArgs, + SourceLocation RAngleLoc, + NamedDecl *&PrevDecl); + virtual DeclResult ActOnExplicitInstantiation(Scope *S, SourceLocation ExternLoc, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index ec27814870..c9983a8f73 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1794,9 +1794,11 @@ Sema::HandleDeclarator(Scope *S, Declarator &D, if (New == 0) return DeclPtrTy(); - // If this has an identifier and is not an invalid redeclaration, - // add it to the scope stack. - if (Name && !(Redeclaration && New->isInvalidDecl())) + // If this has an identifier and is not an invalid redeclaration or + // function template specialization, add it to the scope stack. + if (Name && !(Redeclaration && New->isInvalidDecl()) && + !(isa<FunctionDecl>(New) && + cast<FunctionDecl>(New)->isFunctionTemplateSpecialization())) PushOnScopeChains(New, S); return DeclPtrTy::make(New); @@ -2516,6 +2518,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, // Match up the template parameter lists with the scope specifier, then // determine whether we have a template or a template specialization. FunctionTemplateDecl *FunctionTemplate = 0; + bool isFunctionTemplateSpecialization = false; if (TemplateParameterList *TemplateParams = MatchTemplateParametersToScopeSpecifier( D.getDeclSpec().getSourceRange().getBegin(), @@ -2536,13 +2539,14 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, FunctionTemplate->setLexicalDeclContext(CurContext); NewFD->setDescribedFunctionTemplate(FunctionTemplate); } else { - // FIXME: Handle function template specializations + // This is a function template specialization. + isFunctionTemplateSpecialization = true; } // FIXME: Free this memory properly. TemplateParamLists.release(); } - + // C++ [dcl.fct.spec]p5: // The virtual specifier shall only be used in declarations of // nonstatic class member functions that appear within a @@ -2678,6 +2682,18 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, isOutOfScopePreviousDeclaration(PrevDecl, DC, Context))) PrevDecl = 0; + // FIXME: If the declarator has a template argument list but + // isFunctionTemplateSpecialization is false, this is a function template + // specialization but the user forgot the "template<>" header. Complain about + // the missing template<> header and set isFunctionTemplateSpecialization. + + if (isFunctionTemplateSpecialization && + CheckFunctionTemplateSpecialization(NewFD, + /*FIXME:*/false, SourceLocation(), + 0, 0, SourceLocation(), + PrevDecl)) + NewFD->setInvalidDecl(); + // Perform semantic checking on the function declaration. bool OverloadableAttrRequired = false; // FIXME: HACK! CheckFunctionDeclaration(NewFD, PrevDecl, Redeclaration, diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index edb854ccdd..6200beb863 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -4055,6 +4055,7 @@ Sema::ResolveAddressOfOverloadedFunction(Expr *From, QualType ToType, // resulting template argument list is used to generate a single // function template specialization, which is added to the set of // overloaded functions considered. + // FIXME: We don't really want to build the specialization here, do we? FunctionDecl *Specialization = 0; TemplateDeductionInfo Info(Context); if (TemplateDeductionResult Result @@ -4064,6 +4065,8 @@ Sema::ResolveAddressOfOverloadedFunction(Expr *From, QualType ToType, // FIXME: make a note of the failed deduction for diagnostics. (void)Result; } else { + // FIXME: If the match isn't exact, shouldn't we just drop this as + // a candidate? Find a testcase before changing the code. assert(FunctionType == Context.getCanonicalType(Specialization->getType())); Matches.insert( diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 97b5b52ce5..509237a855 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2877,6 +2877,166 @@ Sema::ActOnStartOfFunctionTemplateDef(Scope *FnBodyScope, return DeclPtrTy(); } +/// \brief Perform semantic analysis for the given function template +/// specialization. +/// +/// This routine performs all of the semantic analysis required for an +/// explicit function template specialization. On successful completion, +/// the function declaration \p FD will become a function template +/// specialization. +/// +/// \param FD the function declaration, which will be updated to become a +/// function template specialization. +/// +/// \param HasExplicitTemplateArgs whether any template arguments were +/// explicitly provided. +/// +/// \param LAngleLoc the location of the left angle bracket ('<'), if +/// template arguments were explicitly provided. +/// +/// \param ExplicitTemplateArgs the explicitly-provided template arguments, +/// if any. +/// +/// \param NumExplicitTemplateArgs the number of explicitly-provided template +/// arguments. This number may be zero even when HasExplicitTemplateArgs is +/// true as in, e.g., \c void sort<>(char*, char*); +/// +/// \param RAngleLoc the location of the right angle bracket ('>'), if +/// template arguments were explicitly provided. +/// +/// \param PrevDecl the set of declarations that +bool +Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD, + bool HasExplicitTemplateArgs, + SourceLocation LAngleLoc, + const TemplateArgument *ExplicitTemplateArgs, + unsigned NumExplicitTemplateArgs, + SourceLocation RAngleLoc, + NamedDecl *&PrevDecl) { + // The set of function template specializations that could match this + // explicit function template specialization. + typedef llvm::SmallVector<FunctionDecl *, 8> CandidateSet; + CandidateSet Candidates; + + DeclContext *FDLookupContext = FD->getDeclContext()->getLookupContext(); + for (OverloadIterator Ovl(PrevDecl), OvlEnd; Ovl != OvlEnd; ++Ovl) { + if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(*Ovl)) { + // Only consider templates found within the same semantic lookup scope as + // FD. + if (!FDLookupContext->Equals(Ovl->getDeclContext()->getLookupContext())) + continue; + + // C++ [temp.expl.spec]p11: + // A trailing template-argument can be left unspecified in the + // template-id naming an explicit function template specialization + // provided it can be deduced from the function argument type. + // Perform template argument deduction to determine whether we may be + // specializing this template. + // FIXME: It is somewhat wasteful to build + TemplateDeductionInfo Info(Context); + FunctionDecl *Specialization = 0; + if (TemplateDeductionResult TDK + = DeduceTemplateArguments(FunTmpl, HasExplicitTemplateArgs, + ExplicitTemplateArgs, + NumExplicitTemplateArgs, + FD->getType(), + Specialization, + Info)) { + // FIXME: Template argument deduction failed; record why it failed, so + // that we can provide nifty diagnostics. + (void)TDK; + continue; + } + + // Record this candidate. + Candidates.push_back(Specialization); + } + } + + if (Candidates.empty()) { + Diag(FD->getLocation(), diag::err_function_template_spec_no_match) + << FD->getDeclName(); + // FIXME: Print the almost-ran candidates. + return true; + } + + if (Candidates.size() > 1) { + // C++ [temp.func.order]p1: + // Partial ordering of overloaded function template declarations is used + // [...] when [...] an explicit specialization (14.7.3) refers to a + // function template specialization. + CandidateSet::iterator Best = Candidates.begin(); + for (CandidateSet::iterator C = Best + 1, CEnd = Candidates.end(); + C != CEnd; ++C) { + if (getMoreSpecializedTemplate((*Best)->getPrimaryTemplate(), + (*C)->getPrimaryTemplate(), + TPOC_Other) + == (*C)->getPrimaryTemplate()) + Best = C; + } + + bool Ambiguous = false; + for (CandidateSet::iterator C = Candidates.begin(), CEnd = Candidates.end(); + C != CEnd; ++C) { + if (C != Best && + getMoreSpecializedTemplate((*Best)->getPrimaryTemplate(), + (*C)->getPrimaryTemplate(), + TPOC_Other) + != (*Best)->getPrimaryTemplate()) { + Ambiguous = true; + break; + } + } + + if (Ambiguous) { + // Partial ordering was ambiguous. + Diag(FD->getLocation(), diag::err_function_template_spec_ambiguous) + << FD->getDeclName() + << HasExplicitTemplateArgs; + + for (CandidateSet::iterator C = Candidates.begin(), + CEnd = Candidates.end(); + C != CEnd; ++C) + Diag((*C)->getLocation(), diag::note_function_template_spec_matched) + << getTemplateArgumentBindingsText( + (*C)->getPrimaryTemplate()->getTemplateParameters(), + *(*C)->getTemplateSpecializationArgs()); + + return true; + } + + // Move the best candidate to the front of the candidates list. + std::swap(*Best, Candidates.front()); + } + + // The first candidate is a prior declaration of the function template + // specialization we're declared here, which we may have created above. + FunctionDecl *Specialization = Candidates.front(); + + // FIXME: Check if the prior specialization has a point of instantiation. + // If so, we have run afoul of C++ [temp.expl.spec]p6. + + // 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? + Specialization->setTemplateSpecializationKind(TSK_ExplicitSpecialization); + + // Turn the given function declaration into a function template + // specialization, with the template arguments from the previous + // specialization. + FD->setFunctionTemplateSpecialization(Context, + Specialization->getPrimaryTemplate(), + new (Context) TemplateArgumentList( + *Specialization->getTemplateSpecializationArgs()), + /*InsertPos=*/0, + TSK_ExplicitSpecialization); + + // The "previous declaration" for this function template specialization is + // the prior function template specialization. + PrevDecl = Specialization; + return false; +} + // Explicit instantiation of a class template specialization // FIXME: Implement extern template semantics Sema::DeclResult diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 2ad1a61b50..b5aa075bd6 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -1436,7 +1436,7 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, } /// \brief Deduce template arguments when taking the address of a function -/// template (C++ [temp.deduct.funcaddr]). +/// template (C++ [temp.deduct.funcaddr]) or matching a /// /// \param FunctionTemplate the function template for which we are performing /// template argument deduction. diff --git a/test/SemaTemplate/function-template-specialization.cpp b/test/SemaTemplate/function-template-specialization.cpp new file mode 100644 index 0000000000..5946d9b680 --- /dev/null +++ b/test/SemaTemplate/function-template-specialization.cpp @@ -0,0 +1,25 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +template<int N> void f0(int (&array)[N]); + +// Simple function template specialization (using overloading) +template<> void f0(int (&array)[1]); + +void test_f0() { + int iarr1[1]; + f0(iarr1); +} + +// Function template specialization where there are no matches +template<> void f0(char (&array)[1]); // expected-error{{no function template matches}} + +// Function template specialization that requires partial ordering +template<typename T, int N> void f1(T (&array)[N]); // expected-note{{matches}} +template<int N> void f1(int (&array)[N]); // expected-note{{matches}} + +template<> void f1(float (&array)[1]); +template<> void f1(int (&array)[1]); + +// Function template specialization that results in an ambiguity +template<typename T> void f1(T (&array)[17]); // expected-note{{matches}} +template<> void f1(int (&array)[17]); // expected-error{{ambiguous}} |