From 97628d6a4ce229f2936d2b34231bb7dfa432f810 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 21 Aug 2009 00:16:32 +0000 Subject: [PATCH] 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 --- clang/lib/Sema/Sema.h | 8 ++ clang/lib/Sema/SemaExpr.cpp | 36 ++++++-- clang/lib/Sema/SemaOverload.cpp | 87 ++++++++++++++++--- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 35 +++++++- .../SemaTemplate/member-function-template.cpp | 29 ++++++- 5 files changed, 169 insertions(+), 26 deletions(-) diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index f13a87c5802b..9b9d6ebf0300 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -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, diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 9f2be1c70bb7..5f16ef3d1346 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -939,18 +939,35 @@ Sema::BuildDeclarationNameExpr(SourceLocation Loc, NamedDecl *D, Ctx = Method->getParent(); MemberType = Method->getType(); } + } else if (FunctionTemplateDecl *FunTmpl + = dyn_cast(D)) { + if (CXXMethodDecl *Method + = dyn_cast(FunTmpl->getTemplatedDecl())) { + if (!Method->isStatic()) { + Ctx = Method->getParent(); + MemberType = Context.OverloadTy; + } + } } else if (OverloadedFunctionDecl *Ovl = dyn_cast(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(*Func)) - if (!DMethod->isStatic()) { - Ctx = Ovl->getDeclContext(); - MemberType = Context.OverloadTy; - break; - } + CXXMethodDecl *DMethod = 0; + if (FunctionTemplateDecl *FunTmpl + = dyn_cast(*Func)) + DMethod = dyn_cast(FunTmpl->getTemplatedDecl()); + else + DMethod = dyn_cast(*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(MemberDecl)) { + MarkDeclarationReferenced(MemberLoc, MemberDecl); + return Owned(new (Context) MemberExpr(BaseExpr, OpKind == tok::arrow, + FunTmpl, MemberLoc, + Context.OverloadTy)); + } if (OverloadedFunctionDecl *Ovl = dyn_cast(MemberDecl)) return Owned(new (Context) MemberExpr(BaseExpr, OpKind == tok::arrow, Ovl, diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index d338e427c8d7..df92634745bc 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -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(Specialization) && + "Specialization is not a member function?"); + AddMethodCandidate(cast(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(MemExpr->getMemberDecl())) { + if (isa(MemExpr->getMemberDecl()) || + isa(MemExpr->getMemberDecl())) { // Add overload candidates OverloadCandidateSet CandidateSet; - for (OverloadedFunctionDecl::function_iterator Func = Ovl->function_begin(), - FuncEnd = Ovl->function_end(); - Func != FuncEnd; ++Func) { - assert(isa(*Func) && "Function is not a method"); - Method = cast(*Func); - AddMethodCandidate(Method, ObjectArg, Args, NumArgs, CandidateSet, - /*SuppressUserConversions=*/false); - } - + DeclarationName DeclName = MemExpr->getMemberDecl()->getDeclName(); + + if (OverloadedFunctionDecl *Ovl + = dyn_cast(MemExpr->getMemberDecl())) { + for (OverloadedFunctionDecl::function_iterator + Func = Ovl->function_begin(), + FuncEnd = Ovl->function_end(); + Func != FuncEnd; ++Func) { + if (Method = dyn_cast(*Func)) + AddMethodCandidate(Method, ObjectArg, Args, NumArgs, CandidateSet, + /*SuppressUserConversions=*/false); + else + AddMethodTemplateCandidate(cast(*Func), + /*FIXME:*/false, /*FIXME:*/0, + /*FIXME:*/0, ObjectArg, Args, NumArgs, + CandidateSet, + /*SuppressUsedConversions=*/false); + } + } else + AddMethodTemplateCandidate( + cast(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; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 23256a853cd1..8d75ac463ab7 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -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 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; } diff --git a/clang/test/SemaTemplate/member-function-template.cpp b/clang/test/SemaTemplate/member-function-template.cpp index 69ebec136587..0f3a37a702f8 100644 --- a/clang/test/SemaTemplate/member-function-template.cpp +++ b/clang/test/SemaTemplate/member-function-template.cpp @@ -1,5 +1,30 @@ -// RUN: clang-cc -fsyntax-only %s +// RUN: clang-cc -fsyntax-only -verify %s struct X { - template T& f(T); + template T& f0(T); + + void g0(int i, double d) { + int &ir = f0(i); + double &dr = f0(d); + } + + template T& f1(T); + template 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); +} \ No newline at end of file