Implement support for calling member function templates, which involves:

- Allowing one to name a member function template within a class
  template and on the right-hand side of a member access expression.
  - Template argument deduction for calls to member function templates.
  - Registering specializations of member function templates (and
  finding them later).

llvm-svn: 79581
This commit is contained in:
Douglas Gregor 2009-08-21 00:16:32 +00:00
parent d5eaa05a7f
commit 97628d6a4c
5 changed files with 169 additions and 26 deletions

View File

@ -804,6 +804,14 @@ public:
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions = false,
bool ForceRValue = false);
void AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl,
bool HasExplicitTemplateArgs,
const TemplateArgument *ExplicitTemplateArgs,
unsigned NumExplicitTemplateArgs,
Expr *Object, Expr **Args, unsigned NumArgs,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions = false,
bool ForceRValue = false);
void AddTemplateOverloadCandidate(FunctionTemplateDecl *FunctionTemplate,
bool HasExplicitTemplateArgs,
const TemplateArgument *ExplicitTemplateArgs,

View File

@ -939,18 +939,35 @@ Sema::BuildDeclarationNameExpr(SourceLocation Loc, NamedDecl *D,
Ctx = Method->getParent();
MemberType = Method->getType();
}
} else if (FunctionTemplateDecl *FunTmpl
= dyn_cast<FunctionTemplateDecl>(D)) {
if (CXXMethodDecl *Method
= dyn_cast<CXXMethodDecl>(FunTmpl->getTemplatedDecl())) {
if (!Method->isStatic()) {
Ctx = Method->getParent();
MemberType = Context.OverloadTy;
}
}
} else if (OverloadedFunctionDecl *Ovl
= dyn_cast<OverloadedFunctionDecl>(D)) {
// FIXME: We need an abstraction for iterating over one or more function
// templates or functions. This code is far too repetitive!
for (OverloadedFunctionDecl::function_iterator
Func = Ovl->function_begin(),
FuncEnd = Ovl->function_end();
Func != FuncEnd; ++Func) {
if (CXXMethodDecl *DMethod = dyn_cast<CXXMethodDecl>(*Func))
if (!DMethod->isStatic()) {
Ctx = Ovl->getDeclContext();
MemberType = Context.OverloadTy;
break;
}
CXXMethodDecl *DMethod = 0;
if (FunctionTemplateDecl *FunTmpl
= dyn_cast<FunctionTemplateDecl>(*Func))
DMethod = dyn_cast<CXXMethodDecl>(FunTmpl->getTemplatedDecl());
else
DMethod = dyn_cast<CXXMethodDecl>(*Func);
if (DMethod && !DMethod->isStatic()) {
Ctx = DMethod->getDeclContext();
MemberType = Context.OverloadTy;
break;
}
}
}
@ -2112,6 +2129,13 @@ Sema::ActOnMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
MemberFn, MemberLoc,
MemberFn->getType()));
}
if (FunctionTemplateDecl *FunTmpl
= dyn_cast<FunctionTemplateDecl>(MemberDecl)) {
MarkDeclarationReferenced(MemberLoc, MemberDecl);
return Owned(new (Context) MemberExpr(BaseExpr, OpKind == tok::arrow,
FunTmpl, MemberLoc,
Context.OverloadTy));
}
if (OverloadedFunctionDecl *Ovl
= dyn_cast<OverloadedFunctionDecl>(MemberDecl))
return Owned(new (Context) MemberExpr(BaseExpr, OpKind == tok::arrow, Ovl,

View File

@ -2237,6 +2237,48 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, Expr *Object,
}
}
/// \brief Add a C++ member function template as a candidate to the candidate
/// set, using template argument deduction to produce an appropriate member
/// function template specialization.
void
Sema::AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl,
bool HasExplicitTemplateArgs,
const TemplateArgument *ExplicitTemplateArgs,
unsigned NumExplicitTemplateArgs,
Expr *Object, Expr **Args, unsigned NumArgs,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions,
bool ForceRValue) {
// C++ [over.match.funcs]p7:
// In each case where a candidate is a function template, candidate
// function template specializations are generated using template argument
// deduction (14.8.3, 14.8.2). Those candidates are then handled as
// candidate functions in the usual way.113) A given name can refer to one
// or more function templates and also to a set of overloaded non-template
// functions. In such a case, the candidate functions generated from each
// function template are combined with the set of non-template candidate
// functions.
TemplateDeductionInfo Info(Context);
FunctionDecl *Specialization = 0;
if (TemplateDeductionResult Result
= DeduceTemplateArguments(MethodTmpl, HasExplicitTemplateArgs,
ExplicitTemplateArgs, NumExplicitTemplateArgs,
Args, NumArgs, Specialization, Info)) {
// FIXME: Record what happened with template argument deduction, so
// that we can give the user a beautiful diagnostic.
(void)Result;
return;
}
// Add the function template specialization produced by template argument
// deduction as a candidate.
assert(Specialization && "Missing member function template specialization?");
assert(isa<CXXMethodDecl>(Specialization) &&
"Specialization is not a member function?");
AddMethodCandidate(cast<CXXMethodDecl>(Specialization), Object, Args, NumArgs,
CandidateSet, SuppressUserConversions, ForceRValue);
}
/// \brief Add a C++ function template as a candidate in the candidate set,
/// using template argument deduction to produce an appropriate function
/// template specialization.
@ -4276,19 +4318,36 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
Expr *ObjectArg = MemExpr->getBase();
CXXMethodDecl *Method = 0;
if (OverloadedFunctionDecl *Ovl
= dyn_cast<OverloadedFunctionDecl>(MemExpr->getMemberDecl())) {
if (isa<OverloadedFunctionDecl>(MemExpr->getMemberDecl()) ||
isa<FunctionTemplateDecl>(MemExpr->getMemberDecl())) {
// Add overload candidates
OverloadCandidateSet CandidateSet;
for (OverloadedFunctionDecl::function_iterator Func = Ovl->function_begin(),
FuncEnd = Ovl->function_end();
Func != FuncEnd; ++Func) {
assert(isa<CXXMethodDecl>(*Func) && "Function is not a method");
Method = cast<CXXMethodDecl>(*Func);
AddMethodCandidate(Method, ObjectArg, Args, NumArgs, CandidateSet,
/*SuppressUserConversions=*/false);
}
DeclarationName DeclName = MemExpr->getMemberDecl()->getDeclName();
if (OverloadedFunctionDecl *Ovl
= dyn_cast<OverloadedFunctionDecl>(MemExpr->getMemberDecl())) {
for (OverloadedFunctionDecl::function_iterator
Func = Ovl->function_begin(),
FuncEnd = Ovl->function_end();
Func != FuncEnd; ++Func) {
if (Method = dyn_cast<CXXMethodDecl>(*Func))
AddMethodCandidate(Method, ObjectArg, Args, NumArgs, CandidateSet,
/*SuppressUserConversions=*/false);
else
AddMethodTemplateCandidate(cast<FunctionTemplateDecl>(*Func),
/*FIXME:*/false, /*FIXME:*/0,
/*FIXME:*/0, ObjectArg, Args, NumArgs,
CandidateSet,
/*SuppressUsedConversions=*/false);
}
} else
AddMethodTemplateCandidate(
cast<FunctionTemplateDecl>(MemExpr->getMemberDecl()),
/*FIXME:*/false, /*FIXME:*/0,
/*FIXME:*/0, ObjectArg, Args, NumArgs,
CandidateSet,
/*SuppressUsedConversions=*/false);
OverloadCandidateSet::iterator Best;
switch (BestViableFunction(CandidateSet, MemExpr->getLocStart(), Best)) {
case OR_Success:
@ -4298,7 +4357,7 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
case OR_No_Viable_Function:
Diag(MemExpr->getSourceRange().getBegin(),
diag::err_ovl_no_viable_member_function_in_call)
<< Ovl->getDeclName() << MemExprE->getSourceRange();
<< DeclName << MemExprE->getSourceRange();
PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false);
// FIXME: Leaking incoming expressions!
return true;
@ -4306,7 +4365,7 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
case OR_Ambiguous:
Diag(MemExpr->getSourceRange().getBegin(),
diag::err_ovl_ambiguous_member_call)
<< Ovl->getDeclName() << MemExprE->getSourceRange();
<< DeclName << MemExprE->getSourceRange();
PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false);
// FIXME: Leaking incoming expressions!
return true;
@ -4315,7 +4374,7 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
Diag(MemExpr->getSourceRange().getBegin(),
diag::err_ovl_deleted_member_call)
<< Best->Function->isDeleted()
<< Ovl->getDeclName() << MemExprE->getSourceRange();
<< DeclName << MemExprE->getSourceRange();
PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false);
// FIXME: Leaking incoming expressions!
return true;

View File

@ -457,7 +457,26 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D) {
}
Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D) {
// FIXME: Look for existing, explicit specializations.
// Check whether there is already a function template specialization for
// this declaration.
FunctionTemplateDecl *FunctionTemplate = D->getDescribedFunctionTemplate();
void *InsertPos = 0;
if (FunctionTemplate) {
llvm::FoldingSetNodeID ID;
FunctionTemplateSpecializationInfo::Profile(ID,
TemplateArgs.getFlatArgumentList(),
TemplateArgs.flat_size(),
SemaRef.Context);
FunctionTemplateSpecializationInfo *Info
= FunctionTemplate->getSpecializations().FindNodeOrInsertPos(ID,
InsertPos);
// If we already have a function template specialization, return it.
if (Info)
return Info->Function;
}
Sema::LocalInstantiationScope Scope(SemaRef);
llvm::SmallVector<ParmVarDecl *, 4> Params;
@ -471,7 +490,9 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D) {
= CXXMethodDecl::Create(SemaRef.Context, Record, D->getLocation(),
D->getDeclName(), T, D->getDeclaratorInfo(),
D->isStatic(), D->isInline());
Method->setInstantiationOfMemberFunction(D);
if (!FunctionTemplate)
Method->setInstantiationOfMemberFunction(D);
// If we are instantiating a member function defined
// out-of-line, the instantiation will have the same lexical
@ -501,8 +522,14 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D) {
SemaRef.CheckFunctionDeclaration(Method, PrevDecl, Redeclaration,
/*FIXME:*/OverloadableAttrRequired);
if (!Method->isInvalidDecl() || !PrevDecl)
Owner->addDecl(Method);
if (FunctionTemplate)
// Record this function template specialization.
Method->setFunctionTemplateSpecialization(SemaRef.Context,
FunctionTemplate,
&TemplateArgs,
InsertPos);
else if (!Method->isInvalidDecl() || !PrevDecl)
Owner->addDecl(Method);
return Method;
}

View File

@ -1,5 +1,30 @@
// RUN: clang-cc -fsyntax-only %s
// RUN: clang-cc -fsyntax-only -verify %s
struct X {
template<typename T> T& f(T);
template<typename T> T& f0(T);
void g0(int i, double d) {
int &ir = f0(i);
double &dr = f0(d);
}
template<typename T> T& f1(T);
template<typename T, typename U> U& f1(T, U);
void g1(int i, double d) {
int &ir1 = f1(i);
int &ir2 = f1(d, i);
int &ir3 = f1(i, i);
}
};
void test_X_f0(X x, int i, float f) {
int &ir = x.f0(i);
float &fr = x.f0(f);
}
void test_X_f1(X x, int i, float f) {
int &ir1 = x.f1(i);
int &ir2 = x.f1(f, i);
int &ir3 = x.f1(i, i);
}