From 6db8ed4498b83fe9336e3855a4ba1a298b04ee00 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 30 Jun 2009 23:57:56 +0000 Subject: [PATCH] When explicit template arguments are provided for a function call, substitute those template arguments into the function parameter types prior to template argument deduction. There's still a bit of work to do to make this work properly when only some of the template arguments are specified. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@74576 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/Sema.h | 14 ++- lib/Sema/SemaExpr.cpp | 35 +++++- lib/Sema/SemaOverload.cpp | 36 ++++-- lib/Sema/SemaTemplateDeduction.cpp | 109 +++++++++++++++--- .../temp.fct.spec/temp.arg.explicit/p1.cpp | 9 +- 5 files changed, 172 insertions(+), 31 deletions(-) diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 2379a17964..9b906aadbd 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -701,6 +701,9 @@ public: bool SuppressUserConversions = false, bool ForceRValue = false); void AddTemplateOverloadCandidate(FunctionTemplateDecl *FunctionTemplate, + bool HasExplicitTemplateArgs, + const TemplateArgument *ExplicitTemplateArgs, + unsigned NumExplicitTemplateArgs, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions = false, @@ -747,6 +750,9 @@ public: FunctionDecl *ResolveOverloadedCallFn(Expr *Fn, NamedDecl *Callee, DeclarationName UnqualifiedName, + bool HasExplicitTemplateArgs, + const TemplateArgument *ExplicitTemplateArgs, + unsigned NumExplicitTemplateArgs, SourceLocation LParenLoc, Expr **Args, unsigned NumArgs, SourceLocation *CommaLocs, @@ -2247,7 +2253,10 @@ public: TDK_TooManyArguments, /// \brief When performing template argument deduction for a class /// template, there were too few call arguments. - TDK_TooFewArguments + TDK_TooFewArguments, + /// \brief The explicitly-specified template arguments were not valid + /// template arguments for the given template. + TDK_InvalidExplicitArguments }; /// \brief Provides information about an attempted template argument @@ -2329,6 +2338,9 @@ public: TemplateDeductionResult DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, + bool HasExplicitTemplateArgs, + const TemplateArgument *ExplicitTemplateArgs, + unsigned NumExplicitTemplateArgs, Expr **Args, unsigned NumArgs, FunctionDecl *&Specialization, TemplateDeductionInfo &Info); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 2ee3c992b6..e9c88d7f6e 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -2635,8 +2635,13 @@ Sema::ActOnCallExpr(Scope *S, ExprArg fn, SourceLocation LParenLoc, } // If we're directly calling a function, get the appropriate declaration. + // Also, in C++, keep track of whether we should perform argument-dependent + // lookup and whether there were any explicitly-specified template arguments. Expr *FnExpr = Fn; bool ADL = true; + bool HasExplicitTemplateArgs = 0; + const TemplateArgument *ExplicitTemplateArgs = 0; + unsigned NumExplicitTemplateArgs = 0; while (true) { if (ImplicitCastExpr *IcExpr = dyn_cast(FnExpr)) FnExpr = IcExpr->getSubExpr(); @@ -2661,6 +2666,28 @@ Sema::ActOnCallExpr(Scope *S, ExprArg fn, SourceLocation LParenLoc, } else if (TemplateIdRefExpr *TemplateIdRef = dyn_cast(FnExpr)) { NDecl = TemplateIdRef->getTemplateName().getAsTemplateDecl(); + HasExplicitTemplateArgs = true; + ExplicitTemplateArgs = TemplateIdRef->getTemplateArgs(); + NumExplicitTemplateArgs = TemplateIdRef->getNumTemplateArgs(); + + // C++ [temp.arg.explicit]p6: + // [Note: For simple function names, argument dependent lookup (3.4.2) + // applies even when the function name is not visible within the + // scope of the call. This is because the call still has the syntactic + // form of a function call (3.4.1). But when a function template with + // explicit template arguments is used, the call does not have the + // correct syntactic form unless there is a function template with + // that name visible at the point of the call. If no such name is + // visible, the call is not syntactically well-formed and + // argument-dependent lookup does not apply. If some such name is + // visible, argument dependent lookup applies and additional function + // templates may be found in other namespaces. + // + // The summary of this paragraph is that, if we get to this point and the + // template-id was not a qualified name, then argument-dependent lookup + // is still possible. + if (TemplateIdRef->getQualifier()) + ADL = false; break; } else { // Any kind of name that does not refer to a declaration (or @@ -2692,8 +2719,12 @@ Sema::ActOnCallExpr(Scope *S, ExprArg fn, SourceLocation LParenLoc, ADL = false; if (Ovl || FunctionTemplate || ADL) { - FDecl = ResolveOverloadedCallFn(Fn, NDecl, UnqualifiedName, LParenLoc, - Args, NumArgs, CommaLocs, RParenLoc, ADL); + FDecl = ResolveOverloadedCallFn(Fn, NDecl, UnqualifiedName, + HasExplicitTemplateArgs, + ExplicitTemplateArgs, + NumExplicitTemplateArgs, + LParenLoc, Args, NumArgs, CommaLocs, + RParenLoc, ADL); if (!FDecl) return ExprError(); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index f7deb1f26f..03ac2d9bb7 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -2166,8 +2166,9 @@ void Sema::AddFunctionCandidates(const FunctionSet &Functions, AddOverloadCandidate(FD, Args, NumArgs, CandidateSet, SuppressUserConversions); else - AddTemplateOverloadCandidate(cast(*F), Args, - NumArgs, CandidateSet, + AddTemplateOverloadCandidate(cast(*F), + /*FIXME: explicit args */false, 0, 0, + Args, NumArgs, CandidateSet, SuppressUserConversions); } } @@ -2273,6 +2274,9 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, Expr *Object, /// template specialization. void Sema::AddTemplateOverloadCandidate(FunctionTemplateDecl *FunctionTemplate, + bool HasExplicitTemplateArgs, + const TemplateArgument *ExplicitTemplateArgs, + unsigned NumExplicitTemplateArgs, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions, @@ -2289,8 +2293,9 @@ Sema::AddTemplateOverloadCandidate(FunctionTemplateDecl *FunctionTemplate, TemplateDeductionInfo Info(Context); FunctionDecl *Specialization = 0; if (TemplateDeductionResult Result - = DeduceTemplateArguments(FunctionTemplate, Args, NumArgs, - Specialization, Info)) { + = DeduceTemplateArguments(FunctionTemplate, 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; @@ -3438,8 +3443,9 @@ Sema::AddArgumentDependentLookupCandidates(DeclarationName Name, if (FunctionDecl *FD = dyn_cast(*Func)) AddOverloadCandidate(FD, Args, NumArgs, CandidateSet); else - AddTemplateOverloadCandidate(cast(*Func), Args, - NumArgs, CandidateSet); + AddTemplateOverloadCandidate(cast(*Func), + /*FIXME: explicit args */false, 0, 0, + Args, NumArgs, CandidateSet); } } @@ -3758,6 +3764,9 @@ Sema::ResolveAddressOfOverloadedFunction(Expr *From, QualType ToType, /// arguments and Fn, and returns NULL. FunctionDecl *Sema::ResolveOverloadedCallFn(Expr *Fn, NamedDecl *Callee, DeclarationName UnqualifiedName, + bool HasExplicitTemplateArgs, + const TemplateArgument *ExplicitTemplateArgs, + unsigned NumExplicitTemplateArgs, SourceLocation LParenLoc, Expr **Args, unsigned NumArgs, SourceLocation *CommaLocs, @@ -3790,11 +3799,17 @@ FunctionDecl *Sema::ResolveOverloadedCallFn(Expr *Fn, NamedDecl *Callee, Func != FuncEnd; ++Func) { DeclContext *Ctx = 0; if (FunctionDecl *FunDecl = dyn_cast(*Func)) { + if (HasExplicitTemplateArgs) + continue; + AddOverloadCandidate(FunDecl, Args, NumArgs, CandidateSet); Ctx = FunDecl->getDeclContext(); } else { FunctionTemplateDecl *FunTmpl = cast(*Func); - AddTemplateOverloadCandidate(FunTmpl, Args, NumArgs, CandidateSet); + AddTemplateOverloadCandidate(FunTmpl, HasExplicitTemplateArgs, + ExplicitTemplateArgs, + NumExplicitTemplateArgs, + Args, NumArgs, CandidateSet); Ctx = FunTmpl->getDeclContext(); } @@ -3803,6 +3818,7 @@ FunctionDecl *Sema::ResolveOverloadedCallFn(Expr *Fn, NamedDecl *Callee, ArgumentDependentLookup = false; } } else if (FunctionDecl *Func = dyn_cast_or_null(Callee)) { + assert(!HasExplicitTemplateArgs && "Explicit template arguments?"); AddOverloadCandidate(Func, Args, NumArgs, CandidateSet); if (Func->getDeclContext()->isRecord() || @@ -3810,7 +3826,10 @@ FunctionDecl *Sema::ResolveOverloadedCallFn(Expr *Fn, NamedDecl *Callee, ArgumentDependentLookup = false; } else if (FunctionTemplateDecl *FuncTemplate = dyn_cast_or_null(Callee)) { - AddTemplateOverloadCandidate(FuncTemplate, Args, NumArgs, CandidateSet); + AddTemplateOverloadCandidate(FuncTemplate, HasExplicitTemplateArgs, + ExplicitTemplateArgs, + NumExplicitTemplateArgs, + Args, NumArgs, CandidateSet); if (FuncTemplate->getDeclContext()->isRecord()) ArgumentDependentLookup = false; @@ -3819,6 +3838,7 @@ FunctionDecl *Sema::ResolveOverloadedCallFn(Expr *Fn, NamedDecl *Callee, if (Callee) UnqualifiedName = Callee->getDeclName(); + // FIXME: Pass explicit template arguments through for ADL if (ArgumentDependentLookup) AddArgumentDependentLookupCandidates(UnqualifiedName, Args, NumArgs, CandidateSet); diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 5300385540..810de048c2 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -868,6 +868,16 @@ static bool isSimpleTemplateIdType(QualType T) { /// \param FunctionTemplate the function template for which we are performing /// template argument deduction. /// +/// \param HasExplicitTemplateArgs whether any template arguments were +/// explicitly specified. +/// +/// \param ExplicitTemplateArguments when @p HasExplicitTemplateArgs is true, +/// the explicitly-specified template arguments. +/// +/// \param NumExplicitTemplateArguments when @p HasExplicitTemplateArgs is true, +/// the number of explicitly-specified template arguments in +/// @p ExplicitTemplateArguments. This value may be zero. +/// /// \param Args the function call arguments /// /// \param NumArgs the number of arguments in Args @@ -880,22 +890,22 @@ static bool isSimpleTemplateIdType(QualType T) { /// about template argument deduction. /// /// \returns the result of template argument deduction. -/// -/// FIXME: We will also need to pass in any explicitly-specified template -/// arguments. Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, + bool HasExplicitTemplateArgs, + const TemplateArgument *ExplicitTemplateArgs, + unsigned NumExplicitTemplateArgs, Expr **Args, unsigned NumArgs, FunctionDecl *&Specialization, TemplateDeductionInfo &Info) { FunctionDecl *Function = FunctionTemplate->getTemplatedDecl(); - + // C++ [temp.deduct.call]p1: // Template argument deduction is done by comparing each function template // parameter type (call it P) with the type of the corresponding argument // of the call (call it A) as described below. unsigned CheckArgs = NumArgs; - if (NumArgs < Function->getNumParams()) + if (NumArgs < Function->getMinRequiredArguments()) return TDK_TooFewArguments; else if (NumArgs > Function->getNumParams()) { const FunctionProtoType *Proto @@ -905,18 +915,83 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, CheckArgs = Function->getNumParams(); } - + // Template argument deduction for function templates in a SFINAE context. // Trap any errors that might occur. SFINAETrap Trap(*this); - - // Deduce template arguments from the function parameters. - llvm::SmallVector Deduced; - Deduced.resize(FunctionTemplate->getTemplateParameters()->size()); + + // The types of the parameters from which we will perform template argument + // deduction. TemplateParameterList *TemplateParams = FunctionTemplate->getTemplateParameters(); + llvm::SmallVector Deduced; + llvm::SmallVector ParamTypes; + if (NumExplicitTemplateArgs) { + // C++ [temp.arg.explicit]p3: + // Template arguments that are present shall be specified in the + // declaration order of their corresponding template-parameters. The + // template argument list shall not specify more template-arguments than + // there are corresponding template-parameters. + TemplateArgumentListBuilder Builder(TemplateParams, + NumExplicitTemplateArgs); + if (CheckTemplateArgumentList(FunctionTemplate, + SourceLocation(), SourceLocation(), + ExplicitTemplateArgs, + NumExplicitTemplateArgs, + SourceLocation(), + Builder) || Trap.hasErrorOccurred()) + return TDK_InvalidExplicitArguments; + + // Enter a new template instantiation context for the substitution of the + // explicitly-specified template arguments into the + InstantiatingTemplate Inst(*this, FunctionTemplate->getLocation(), + FunctionTemplate, Deduced.data(), Deduced.size()); + if (Inst) + return TDK_InstantiationDepth; + + // Form the template argument list from the explicitly-specified + // template arguments. + TemplateArgumentList *ExplicitArgumentList + = new (Context) TemplateArgumentList(Context, Builder, /*TakeArgs=*/true); + Info.reset(ExplicitArgumentList); + + // Instantiate the types of each of the function parameters given the + // explicitly-specified template arguments. + for (FunctionDecl::param_iterator P = Function->param_begin(), + PEnd = Function->param_end(); + P != PEnd; + ++P) { + QualType ParamType = InstantiateType((*P)->getType(), + *ExplicitArgumentList, + (*P)->getLocation(), + (*P)->getDeclName()); + if (ParamType.isNull() || Trap.hasErrorOccurred()) + return TDK_SubstitutionFailure; + + ParamTypes.push_back(ParamType); + } + + // C++ [temp.arg.explicit]p2: + // Trailing template arguments that can be deduced (14.8.2) may be + // omitted from the list of explicit template- arguments. If all of the + // template arguments can be deduced, they may all be omitted; in this + // case, the empty template argument list <> itself may also be omitted. + // + // Take all of the explicitly-specified arguments and put them into the + // set of deduced template arguments. + Deduced.reserve(TemplateParams->size()); + for (unsigned I = 0, N = ExplicitArgumentList->size(); I != N; ++I) + Deduced.push_back(ExplicitArgumentList->get(I)); + } else { + // Just fill in the parameter types from the function declaration. + for (unsigned I = 0; I != CheckArgs; ++I) + ParamTypes.push_back(Function->getParamDecl(I)->getType()); + } + + // Deduce template arguments from the function parameters. + Deduced.resize(TemplateParams->size()); for (unsigned I = 0; I != CheckArgs; ++I) { - QualType ParamType = Function->getParamDecl(I)->getType(); + QualType ParamType = ParamTypes[I]; QualType ArgType = Args[I]->getType(); // C++ [temp.deduct.call]p2: @@ -998,11 +1073,6 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, // FIXME: C++ [temp.deduct.call] paragraphs 6-9 deal with function // pointer parameters. } - - InstantiatingTemplate Inst(*this, FunctionTemplate->getLocation(), - FunctionTemplate, Deduced.data(), Deduced.size()); - if (Inst) - return TDK_InstantiationDepth; // C++ [temp.deduct.type]p2: // [...] or if any template argument remains neither deduced nor @@ -1030,6 +1100,13 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, = new (Context) TemplateArgumentList(Context, Builder, /*TakeArgs=*/true); Info.reset(DeducedArgumentList); + // Enter a new template instantiation context while we instantiate the + // actual function declaration. + InstantiatingTemplate Inst(*this, FunctionTemplate->getLocation(), + FunctionTemplate, Deduced.data(), Deduced.size()); + if (Inst) + return TDK_InstantiationDepth; + // Substitute the deduced template arguments into the function template // declaration to produce the function template specialization. Specialization = cast_or_null( diff --git a/test/CXX/temp/temp.fct.spec/temp.arg.explicit/p1.cpp b/test/CXX/temp/temp.fct.spec/temp.arg.explicit/p1.cpp index 97c5ce96f6..088a9e55eb 100644 --- a/test/CXX/temp/temp.fct.spec/temp.arg.explicit/p1.cpp +++ b/test/CXX/temp/temp.fct.spec/temp.arg.explicit/p1.cpp @@ -2,10 +2,11 @@ template struct A { }; -template T make(A); +template T make(); +template T make2(const T&); void test_make() { - int& ir0 = make(A()); - A a0 = make< A >(A >()); + int& ir0 = make(); + A a0 = make< A >(); + A a1 = make2< A >(A()); } - -- 2.40.0