Skip to content

Commit

Permalink
WIP implementation of explicit function template specialization. This
Browse files Browse the repository at this point in the history
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.

llvm-svn: 82728
  • Loading branch information
DougGregor committed Sep 24, 2009
1 parent d6f9a2f commit 3a923c2
Show file tree
Hide file tree
Showing 11 changed files with 269 additions and 11 deletions.
15 changes: 14 additions & 1 deletion clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,12 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
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.
///
Expand All @@ -1105,10 +1111,17 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
///
/// \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.
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,9 @@ class TemplateArgumentList {
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.
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">,
Expand Down
22 changes: 18 additions & 4 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,21 +689,35 @@ 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)
Info = new (Context) FunctionTemplateSpecializationInfo;

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 {
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/DeclTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -2515,6 +2515,14 @@ class Sema : public Action {
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,
Expand Down
26 changes: 21 additions & 5 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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(),
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand Down
160 changes: 160 additions & 0 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit 3a923c2

Please sign in to comment.