aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2009-09-24 23:14:47 +0000
committerDouglas Gregor <dgregor@apple.com>2009-09-24 23:14:47 +0000
commitb9aa6b214c8fbc3e081dde575eef1f0913d48bdc (patch)
tree77bf753a91b1d025823f3adc0a285c0d973beab9
parent119fa68eb1394a2653bb651dc5c1cf294cbbdbda (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.h15
-rw-r--r--include/clang/AST/DeclTemplate.h3
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td10
-rw-r--r--lib/AST/Decl.cpp22
-rw-r--r--lib/AST/DeclTemplate.cpp6
-rw-r--r--lib/Sema/Sema.h8
-rw-r--r--lib/Sema/SemaDecl.cpp26
-rw-r--r--lib/Sema/SemaOverload.cpp3
-rw-r--r--lib/Sema/SemaTemplate.cpp160
-rw-r--r--lib/Sema/SemaTemplateDeduction.cpp2
-rw-r--r--test/SemaTemplate/function-template-specialization.cpp25
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}}