From 4b52e25f3b05ab0f9d2492276a52323a50a84fb7 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 21 Dec 2009 23:17:24 +0000 Subject: [PATCH] When a template-id refers to a single function template, and the explicitly-specified template arguments are enough to determine the instantiation, and either template argument deduction fails or is not performed in that context, we can resolve the template-id down to a function template specialization (so sayeth C++0x [temp.arg.explicit]p3). Fixes PR5811. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@91852 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 5 +- lib/Sema/Sema.h | 13 ++- lib/Sema/SemaOverload.cpp | 87 ++++++++++++++++++ lib/Sema/SemaTemplate.cpp | 3 + lib/Sema/SemaTemplateDeduction.cpp | 91 ++++++++++++++----- lib/Sema/SemaType.cpp | 39 +++++++- .../temp.arg.explicit/p3-nodeduct.cpp | 37 ++++++++ .../SemaCXX/decltype-overloaded-functions.cpp | 4 +- 8 files changed, 249 insertions(+), 30 deletions(-) create mode 100644 test/CXX/temp/temp.fct.spec/temp.arg.explicit/p3-nodeduct.cpp diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 25f9693ec0..3662bd4793 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -589,7 +589,8 @@ def err_temp_copy_deleted : Error< // C++0x decltype def err_cannot_determine_declared_type_of_overloaded_function : Error< - "can't determine the declared type of an overloaded function">; + "cannot determine the %select{type|declared type}0 of an overloaded " + "function">; // C++0x auto def err_auto_variable_cannot_appear_in_own_initializer : Error< @@ -1007,6 +1008,8 @@ def err_template_arg_unnamed_type : Error< "template argument uses unnamed type">; def note_template_unnamed_type_here : Note< "unnamed type used in template argument was declared here">; +def err_template_arg_overload_type : Error< + "template argument is the type of an unresolved overloaded function">; def err_template_arg_not_class_template : Error< "template argument does not refer to a class template or template " "template parameter">; diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 8e1ed41375..35fee3d866 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1036,6 +1036,8 @@ public: FunctionDecl *ResolveAddressOfOverloadedFunction(Expr *From, QualType ToType, bool Complain); + FunctionDecl *ResolveSingleFunctionTemplateSpecialization(Expr *From); + Expr *FixOverloadedFunctionReference(Expr *E, FunctionDecl *Fn); OwningExprResult FixOverloadedFunctionReference(OwningExprResult, FunctionDecl *Fn); @@ -2664,7 +2666,10 @@ public: TDK_TooFewArguments, /// \brief The explicitly-specified template arguments were not valid /// template arguments for the given template. - TDK_InvalidExplicitArguments + TDK_InvalidExplicitArguments, + /// \brief The arguments included an overloaded function name that could + /// not be resolved to a suitable function. + TDK_FailedOverloadResolution }; /// \brief Provides information about an attempted template argument @@ -2778,6 +2783,12 @@ public: CXXConversionDecl *&Specialization, TemplateDeductionInfo &Info); + TemplateDeductionResult + DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, + const TemplateArgumentListInfo *ExplicitTemplateArgs, + FunctionDecl *&Specialization, + TemplateDeductionInfo &Info); + FunctionTemplateDecl *getMoreSpecializedTemplate(FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, TemplatePartialOrderingContext TPOC); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 4565e94ebe..fbcb807428 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -4591,6 +4591,93 @@ Sema::ResolveAddressOfOverloadedFunction(Expr *From, QualType ToType, return 0; } +/// \brief Given an expression that refers to an overloaded function, try to +/// resolve that overloaded function expression down to a single function. +/// +/// This routine can only resolve template-ids that refer to a single function +/// template, where that template-id refers to a single template whose template +/// arguments are either provided by the template-id or have defaults, +/// as described in C++0x [temp.arg.explicit]p3. +FunctionDecl *Sema::ResolveSingleFunctionTemplateSpecialization(Expr *From) { + // C++ [over.over]p1: + // [...] [Note: any redundant set of parentheses surrounding the + // overloaded function name is ignored (5.1). ] + Expr *OvlExpr = From->IgnoreParens(); + + // C++ [over.over]p1: + // [...] The overloaded function name can be preceded by the & + // operator. + if (UnaryOperator *UnOp = dyn_cast(OvlExpr)) { + if (UnOp->getOpcode() == UnaryOperator::AddrOf) + OvlExpr = UnOp->getSubExpr()->IgnoreParens(); + } + + bool HasExplicitTemplateArgs = false; + TemplateArgumentListInfo ExplicitTemplateArgs; + + llvm::SmallVector Fns; + + // Look into the overloaded expression. + if (UnresolvedLookupExpr *UL + = dyn_cast(OvlExpr)) { + Fns.append(UL->decls_begin(), UL->decls_end()); + if (UL->hasExplicitTemplateArgs()) { + HasExplicitTemplateArgs = true; + UL->copyTemplateArgumentsInto(ExplicitTemplateArgs); + } + } else if (UnresolvedMemberExpr *ME + = dyn_cast(OvlExpr)) { + Fns.append(ME->decls_begin(), ME->decls_end()); + if (ME->hasExplicitTemplateArgs()) { + HasExplicitTemplateArgs = true; + ME->copyTemplateArgumentsInto(ExplicitTemplateArgs); + } + } + + // If we didn't actually find any template-ids, we're done. + if (Fns.empty() || !HasExplicitTemplateArgs) + return 0; + + // Look through all of the overloaded functions, searching for one + // whose type matches exactly. + FunctionDecl *Matched = 0; + for (llvm::SmallVectorImpl::iterator I = Fns.begin(), + E = Fns.end(); I != E; ++I) { + // C++0x [temp.arg.explicit]p3: + // [...] In contexts where deduction is done and fails, or in contexts + // where deduction is not done, if a template argument list is + // specified and it, along with any default template arguments, + // identifies a single function template specialization, then the + // template-id is an lvalue for the function template specialization. + FunctionTemplateDecl *FunctionTemplate = cast(*I); + + // C++ [over.over]p2: + // If the name is a function template, template argument deduction is + // done (14.8.2.2), and if the argument deduction succeeds, the + // 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 + = DeduceTemplateArguments(FunctionTemplate, &ExplicitTemplateArgs, + Specialization, Info)) { + // FIXME: make a note of the failed deduction for diagnostics. + (void)Result; + continue; + } + + // Multiple matches; we can't resolve to a single declaration. + if (Matched) + return 0; + + Matched = Specialization; + } + + return Matched; +} + /// \brief Add a single candidate to the overload set. static void AddOverloadedCallCandidate(Sema &S, NamedDecl *Callee, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 9b046e3696..e6bd77d208 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2189,6 +2189,9 @@ bool Sema::CheckTemplateArgument(TemplateTypeParmDecl *Param, Diag(SR.getBegin(), diag::err_template_arg_unnamed_type) << SR; Diag(Tag->getDecl()->getLocation(), diag::note_template_unnamed_type_here); return true; + } else if (Context.hasSameUnqualifiedType(Arg, Context.OverloadTy)) { + SourceRange SR = ArgInfo->getTypeLoc().getFullSourceRange(); + return Diag(SR.getBegin(), diag::err_template_arg_overload_type) << SR; } return false; diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 5434beaef1..11798c468a 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -1506,15 +1506,39 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, ParamType->getAs()->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. + ArgType = ResolvedArg->getType(); + if (ArgType->isPointerType() || ArgType->isMemberPointerType()) + TDF |= TDF_IgnoreQualifiers; + + ResolvedArg->Destroy(Context); + } + if (TemplateDeductionResult Result = ::DeduceTemplateArguments(Context, TemplateParams, ParamType, ArgType, Info, Deduced, TDF)) return Result; - // FIXME: C++0x [temp.deduct.call] paragraphs 6-9 deal with function - // pointer parameters. - // FIXME: we need to check that the deduced A is the same as A, // modulo the various allowed differences. } @@ -1524,24 +1548,19 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, } /// \brief Deduce template arguments when taking the address of a function -/// template (C++ [temp.deduct.funcaddr]) or matching a +/// template (C++ [temp.deduct.funcaddr]) or matching a specialization to +/// a template. /// /// \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 ExplicitTemplateArguments the explicitly-specified template +/// arguments. /// /// \param ArgFunctionType the function type that will be used as the /// "argument" type (A) when performing template argument deduction from the -/// function template's function type. +/// function template's function type. This type may be NULL, if there is no +/// argument type to compare against, in C++0x [temp.arg.explicit]p3. /// /// \param Specialization if template argument deduction was successful, /// this will be set to the function template specialization produced by @@ -1578,14 +1597,16 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, // Trap any errors that might occur. SFINAETrap Trap(*this); - // Deduce template arguments from the function type. - Deduced.resize(TemplateParams->size()); - if (TemplateDeductionResult Result - = ::DeduceTemplateArguments(Context, TemplateParams, - FunctionType, ArgFunctionType, Info, - Deduced, 0)) - return Result; - + if (!ArgFunctionType.isNull()) { + // Deduce template arguments from the function type. + Deduced.resize(TemplateParams->size()); + if (TemplateDeductionResult Result + = ::DeduceTemplateArguments(Context, TemplateParams, + FunctionType, ArgFunctionType, Info, + Deduced, 0)) + return Result; + } + return FinishTemplateArgumentDeduction(FunctionTemplate, Deduced, Specialization, Info); } @@ -1694,6 +1715,32 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, return Result; } +/// \brief Deduce template arguments for a function template when there is +/// nothing to deduce against (C++0x [temp.arg.explicit]p3). +/// +/// \param FunctionTemplate the function template for which we are performing +/// template argument deduction. +/// +/// \param ExplicitTemplateArguments the explicitly-specified template +/// arguments. +/// +/// \param Specialization if template argument deduction was successful, +/// this will be set to the function template specialization produced by +/// template argument deduction. +/// +/// \param Info the argument will be updated to provide additional information +/// about template argument deduction. +/// +/// \returns the result of template argument deduction. +Sema::TemplateDeductionResult +Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, + const TemplateArgumentListInfo *ExplicitTemplateArgs, + FunctionDecl *&Specialization, + TemplateDeductionInfo &Info) { + return DeduceTemplateArguments(FunctionTemplate, ExplicitTemplateArgs, + QualType(), Specialization, Info); +} + /// \brief Stores the result of comparing the qualifiers of two types. enum DeductionQualifierComparison { NeitherMoreQualified = 0, diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 37f19f2be4..ed30229e95 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -308,7 +308,11 @@ static QualType ConvertDeclSpecToType(Declarator &TheDeclarator, Sema &TheSema){ Expr *E = static_cast(DS.getTypeRep()); assert(E && "Didn't get an expression for typeof?"); // TypeQuals handled by caller. - Result = Context.getTypeOfExprType(E); + Result = TheSema.BuildTypeofExprType(E); + if (Result.isNull()) { + Result = Context.IntTy; + TheDeclarator.setInvalidType(true); + } break; } case DeclSpec::TST_decltype: { @@ -1826,14 +1830,41 @@ QualType Sema::getQualifiedNameType(const CXXScopeSpec &SS, QualType T) { } QualType Sema::BuildTypeofExprType(Expr *E) { + if (E->getType() == Context.OverloadTy) { + // C++ [temp.arg.explicit]p3 allows us to resolve a template-id to a + // function template specialization wherever deduction cannot occur. + if (FunctionDecl *Specialization + = ResolveSingleFunctionTemplateSpecialization(E)) { + E = FixOverloadedFunctionReference(E, Specialization); + if (!E) + return QualType(); + } else { + Diag(E->getLocStart(), + diag::err_cannot_determine_declared_type_of_overloaded_function) + << false << E->getSourceRange(); + return QualType(); + } + } + return Context.getTypeOfExprType(E); } QualType Sema::BuildDecltypeType(Expr *E) { if (E->getType() == Context.OverloadTy) { - Diag(E->getLocStart(), - diag::err_cannot_determine_declared_type_of_overloaded_function); - return QualType(); + // C++ [temp.arg.explicit]p3 allows us to resolve a template-id to a + // function template specialization wherever deduction cannot occur. + if (FunctionDecl *Specialization + = ResolveSingleFunctionTemplateSpecialization(E)) { + E = FixOverloadedFunctionReference(E, Specialization); + if (!E) + return QualType(); + } else { + Diag(E->getLocStart(), + diag::err_cannot_determine_declared_type_of_overloaded_function) + << true << E->getSourceRange(); + return QualType(); + } } + return Context.getDecltypeType(E); } diff --git a/test/CXX/temp/temp.fct.spec/temp.arg.explicit/p3-nodeduct.cpp b/test/CXX/temp/temp.fct.spec/temp.arg.explicit/p3-nodeduct.cpp new file mode 100644 index 0000000000..a8b83d4854 --- /dev/null +++ b/test/CXX/temp/temp.fct.spec/temp.arg.explicit/p3-nodeduct.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// PR5811 +template void Call(F f) { f(1); } +template void f(T); +void a() { Call(f); } + +// Check the conversion of a template-id to a pointer +template struct Constant { }; +Constant > constant0; + +template void constant_func(); +void test_constant_func() { + constant_func >(); +} + + +// Check typeof() on a template-id referring to a single function +template +struct is_same { + static const bool value = false; +}; + +template +struct is_same { + static const bool value = true; +}; + +int typeof0[is_same<__typeof__(f), void (int)>::value? 1 : -1]; +int typeof1[is_same<__typeof__(&f), void (*)(int)>::value? 1 : -1]; + +template void g(T); +template void g(T, T); + +int typeof2[is_same<__typeof__(g), void (int)>::value? 1 : -1]; // \ + // expected-error{{cannot determine the type of an overloaded function}} \ + // FIXME: expected-error{{use of undeclared identifier}} diff --git a/test/SemaCXX/decltype-overloaded-functions.cpp b/test/SemaCXX/decltype-overloaded-functions.cpp index 6013d0ae8b..906e868a42 100644 --- a/test/SemaCXX/decltype-overloaded-functions.cpp +++ b/test/SemaCXX/decltype-overloaded-functions.cpp @@ -2,10 +2,10 @@ void f(); void f(int); -decltype(f) a; // expected-error{{can't determine the declared type of an overloaded function}} +decltype(f) a; // expected-error{{cannot determine the declared type of an overloaded function}} template struct S { - decltype(T::f) * f; // expected-error{{can't determine the declared type of an overloaded function}} + decltype(T::f) * f; // expected-error{{cannot determine the declared type of an overloaded function}} }; struct K { void f(); void f(int); }; -- 2.40.0