diff options
author | John McCall <rjmccall@apple.com> | 2010-02-02 02:21:27 +0000 |
---|---|---|
committer | John McCall <rjmccall@apple.com> | 2010-02-02 02:21:27 +0000 |
commit | eff92135d32039c9874dc356f3e93143af6069c1 (patch) | |
tree | aacbde63c02d5a5a95d4d333c3333247dff9d72a | |
parent | d0ed44812057f1adf0ebe38f3fab55973c4efaeb (diff) |
Implement C++ [temp.deduct.call]p6, template argument deduction for overloaded
arguments. Fix a bug where incomplete explicit specializations were being
passed through as legitimate. Fix a bug where the absence of an explicit
specialization in an overload set was causing overall deduction to fail.
Fixes PR6191.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95052 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/Sema/SemaTemplateDeduction.cpp | 142 | ||||
-rw-r--r-- | test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p6.cpp | 96 |
2 files changed, 207 insertions, 31 deletions
diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index b9160930cb..de13b664fe 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -1306,6 +1306,106 @@ Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate, return TDK_Success; } +static QualType GetTypeOfFunction(ASTContext &Context, + bool isAddressOfOperand, + FunctionDecl *Fn) { + if (!isAddressOfOperand) return Fn->getType(); + if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Fn)) + if (Method->isInstance()) + return Context.getMemberPointerType(Fn->getType(), + Context.getTypeDeclType(Method->getParent()).getTypePtr()); + return Context.getPointerType(Fn->getType()); +} + +/// Apply the deduction rules for overload sets. +/// +/// \return the null type if this argument should be treated as an +/// undeduced context +static QualType +ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams, + Expr *Arg, QualType ParamType) { + bool isAddressOfOperand = false; + + Arg = Arg->IgnoreParens(); + if (UnaryOperator *UnOp = dyn_cast<UnaryOperator>(Arg)) { + assert(UnOp->getOpcode() == UnaryOperator::AddrOf); + isAddressOfOperand = true; + Arg = UnOp->getSubExpr()->IgnoreParens(); + } + + const UnresolvedSetImpl *Decls; + bool HasExplicitArgs; + if (UnresolvedLookupExpr *ULE = dyn_cast<UnresolvedLookupExpr>(Arg)) { + Decls = &ULE->getDecls(); + HasExplicitArgs = ULE->hasExplicitTemplateArgs(); + } else { + UnresolvedMemberExpr *UME = cast<UnresolvedMemberExpr>(Arg); + Decls = &UME->getDecls(); + HasExplicitArgs = ULE->hasExplicitTemplateArgs(); + } + + // If there were explicit template arguments, we can only find + // something via C++ [temp.arg.explicit]p3, i.e. if the arguments + // unambiguously name a full specialization. + if (HasExplicitArgs) { + // But we can still look for an explicit specialization. + if (FunctionDecl *ExplicitSpec + = S.ResolveSingleFunctionTemplateSpecialization(Arg)) + return GetTypeOfFunction(S.Context, isAddressOfOperand, ExplicitSpec); + return QualType(); + } + + // C++0x [temp.deduct.call]p6: + // When P is a function type, pointer to function type, or pointer + // to member function type: + + if (!ParamType->isFunctionType() && + !ParamType->isFunctionPointerType() && + !ParamType->isMemberFunctionPointerType()) + return QualType(); + + QualType Match; + for (UnresolvedSetIterator I = Decls->begin(), + E = Decls->end(); I != E; ++I) { + NamedDecl *D = (*I)->getUnderlyingDecl(); + + // - If the argument is an overload set containing one or more + // function templates, the parameter is treated as a + // non-deduced context. + if (isa<FunctionTemplateDecl>(D)) + return QualType(); + + FunctionDecl *Fn = cast<FunctionDecl>(D); + QualType ArgType = GetTypeOfFunction(S.Context, isAddressOfOperand, Fn); + + // - If the argument is an overload set (not containing function + // templates), trial argument deduction is attempted using each + // of the members of the set. If deduction succeeds for only one + // of the overload set members, that member is used as the + // argument value for the deduction. If deduction succeeds for + // more than one member of the overload set the parameter is + // treated as a non-deduced context. + + // We do all of this in a fresh context per C++0x [temp.deduct.type]p2: + // Type deduction is done independently for each P/A pair, and + // the deduced template argument values are then combined. + // So we do not reject deductions which were made elsewhere. + llvm::SmallVector<TemplateArgument, 8> Deduced(TemplateParams->size()); + Sema::TemplateDeductionInfo Info(S.Context); + unsigned TDF = 0; + + Sema::TemplateDeductionResult Result + = DeduceTemplateArguments(S.Context, TemplateParams, + ParamType, ArgType, + Info, Deduced, TDF); + if (Result) continue; + if (!Match.isNull()) return QualType(); + Match = ArgType; + } + + return Match; +} + /// \brief Perform template argument deduction from a function call /// (C++ [temp.deduct.call]). /// @@ -1384,6 +1484,15 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, QualType ParamType = ParamTypes[I]; QualType ArgType = Args[I]->getType(); + // Overload sets usually make this parameter an undeduced + // context, but there are sometimes special circumstances. + if (ArgType == Context.OverloadTy) { + ArgType = ResolveOverloadForDeduction(*this, TemplateParams, + Args[I], ParamType); + if (ArgType.isNull()) + continue; + } + // C++ [temp.deduct.call]p2: // If P is not a reference type: QualType CanonParamType = Context.getCanonicalType(ParamType); @@ -1454,36 +1563,6 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, ParamType->getAs<PointerType>()->getPointeeType()))) TDF |= TDF_DerivedClass; - // FIXME: C++0x [temp.deduct.call] paragraphs 6-9 deal with function - // pointer parameters. - - if (Context.hasSameUnqualifiedType(ArgType, Context.OverloadTy)) { - // We know that template argument deduction will fail if the argument is - // still an overloaded function. Check whether we can resolve this - // argument as a single function template specialization per - // C++ [temp.arg.explicit]p3. - FunctionDecl *ExplicitSpec - = ResolveSingleFunctionTemplateSpecialization(Args[I]); - Expr *ResolvedArg = 0; - if (ExplicitSpec) - ResolvedArg = FixOverloadedFunctionReference(Args[I], ExplicitSpec); - if (!ExplicitSpec || !ResolvedArg) { - // Template argument deduction fails if we can't resolve the overloaded - // function. - return TDK_FailedOverloadResolution; - } - - // Get the type of the resolved argument, and adjust it per - // C++0x [temp.deduct.call]p3. - ArgType = ResolvedArg->getType(); - if (!ParamWasReference && ArgType->isFunctionType()) - ArgType = Context.getPointerType(ArgType); - if (ArgType->isPointerType() || ArgType->isMemberPointerType()) - TDF |= TDF_IgnoreQualifiers; - - ResolvedArg->Destroy(Context); - } - if (TemplateDeductionResult Result = ::DeduceTemplateArguments(Context, TemplateParams, ParamType, ArgType, Info, Deduced, @@ -1548,9 +1627,10 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, // Trap any errors that might occur. SFINAETrap Trap(*this); + Deduced.resize(TemplateParams->size()); + if (!ArgFunctionType.isNull()) { // Deduce template arguments from the function type. - Deduced.resize(TemplateParams->size()); if (TemplateDeductionResult Result = ::DeduceTemplateArguments(Context, TemplateParams, FunctionType, ArgFunctionType, Info, diff --git a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p6.cpp b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p6.cpp new file mode 100644 index 0000000000..c1fc696baf --- /dev/null +++ b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p6.cpp @@ -0,0 +1,96 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +namespace test0 { + // FIXME: this second note is horrible. + template<class T> void apply(T x, void (*f)(T)) { f(x); } // expected-note 2 {{failed template argument deduction}}\ + // expected-note {{no known conversion from '<overloaded function type>' to 'void (*)(int)'}} + + template<class A> void temp(A); + void test0() { + // okay: deduce T=int from first argument, A=int during overload + apply(0, &temp); + apply(0, &temp<>); + + // okay: deduce T=int from first and second arguments + apply(0, &temp<int>); + + // deduction failure: T=int from first, T=long from second + apply(0, &temp<long>); // expected-error {{no matching function for call to 'apply'}} + } + + void over(int); + int over(long); + + void test1() { + // okay: deductions match + apply(0, &over); + + // deduction failure: deduced T=long from first argument, T=int from second + apply(0L, &over); // expected-error {{no matching function for call to 'apply'}} + } + + void over(short); + + void test2() { + // deduce T=int from first arg, second arg is undeduced context, + // pick correct overload of 'over' during overload resolution for 'apply' + apply(0, &over); + } + + template<class A, class B> B temp2(A); + void test3() { + // deduce T=int from first arg, A=int B=void during overload resolution + apply(0, &temp2); + apply(0, &temp2<>); + apply(0, &temp2<int>); + + // overload failure + apply(0, &temp2<long>); // expected-error {{no matching function for call to 'apply'}} + } +} + +namespace test1 { + template<class T> void invoke(void (*f)(T)) { f(T()); } // expected-note 6 {{couldn't infer template argument}} \ + // expected-note {{failed template argument deduction}} + + template<class T> void temp(T); + void test0() { + // deduction failure: overload has template => undeduced context + invoke(&temp); // expected-error {{no matching function for call to 'invoke'}} + invoke(&temp<>); // expected-error {{no matching function for call to 'invoke'}} + + // okay: full template-id + invoke(&temp<int>); + } + + void over(int); + int over(long); + + void test1() { + // okay: only one overload matches + invoke(&over); + } + + void over(short); + + void test2() { + // deduction failure: overload has multiple matches => undeduced context + invoke(&over); // expected-error {{no matching function for call to 'invoke'}} + } + + template<class A, class B> B temp2(A); + void test3() { + // deduction failure: overload has template => undeduced context + // (even though partial application temp2<int> could in theory + // let us infer T=int) + invoke(&temp2); // expected-error {{no matching function for call to 'invoke'}} + invoke(&temp2<>); // expected-error {{no matching function for call to 'invoke'}} + invoke(&temp2<int>); // expected-error {{no matching function for call to 'invoke'}} + + // okay: full template-id + invoke(&temp2<int, void>); + + // overload failure + invoke(&temp2<int, int>); // expected-error {{no matching function for call to 'invoke'}} + } +} |