diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index c872d09d22187e325627b8f6182f638ee36cadf1..275039fc0a27b7730352f691afb41d5c4091c550 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -7555,6 +7555,10 @@ public: unsigned ThisTypeQuals); void SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto, const MultiLevelTemplateArgumentList &Args); + bool SubstExceptionSpec(SourceLocation Loc, + FunctionProtoType::ExceptionSpecInfo &ESI, + SmallVectorImpl<QualType> &ExceptionStorage, + const MultiLevelTemplateArgumentList &Args); ParmVarDecl *SubstParmVarDecl(ParmVarDecl *D, const MultiLevelTemplateArgumentList &TemplateArgs, int indexAdjustment, diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 75b69ae04f56379eb1ea8fba96cdc8417824e4b7..983b1ea795dda528f71bdb4c2da9545e5cf6272e 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -56,8 +56,12 @@ namespace clang { TDF_TopLevelParameterTypeList = 0x10, /// \brief Within template argument deduction from overload resolution per /// C++ [over.over] allow matching function types that are compatible in - /// terms of noreturn and default calling convention adjustments. - TDF_InOverloadResolution = 0x20 + /// terms of noreturn and default calling convention adjustments, or + /// similarly matching a declared template specialization against a + /// possible template, per C++ [temp.deduct.decl]. In either case, permit + /// deduction where the parameter is a function type that can be converted + /// to the argument type. + TDF_AllowCompatibleFunctionType = 0x20, }; } @@ -1306,9 +1310,10 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, // If the parameter type is not dependent, there is nothing to deduce. if (!Param->isDependentType()) { if (!(TDF & TDF_SkipNonDependent)) { - bool NonDeduced = (TDF & TDF_InOverloadResolution)? - !S.isSameOrCompatibleFunctionType(CanParam, CanArg) : - Param != Arg; + bool NonDeduced = + (TDF & TDF_AllowCompatibleFunctionType) + ? !S.isSameOrCompatibleFunctionType(CanParam, CanArg) + : Param != Arg; if (NonDeduced) { return Sema::TDK_NonDeducedMismatch; } @@ -1318,10 +1323,10 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, } else if (!Param->isDependentType()) { CanQualType ParamUnqualType = CanParam.getUnqualifiedType(), ArgUnqualType = CanArg.getUnqualifiedType(); - bool Success = (TDF & TDF_InOverloadResolution)? - S.isSameOrCompatibleFunctionType(ParamUnqualType, - ArgUnqualType) : - ParamUnqualType == ArgUnqualType; + bool Success = + (TDF & TDF_AllowCompatibleFunctionType) + ? S.isSameOrCompatibleFunctionType(ParamUnqualType, ArgUnqualType) + : ParamUnqualType == ArgUnqualType; if (Success) return Sema::TDK_Success; } @@ -1524,17 +1529,56 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, return Sema::TDK_NonDeducedMismatch; // Check return types. - if (Sema::TemplateDeductionResult Result = - DeduceTemplateArgumentsByTypeMatch( - S, TemplateParams, FunctionProtoParam->getReturnType(), - FunctionProtoArg->getReturnType(), Info, Deduced, 0)) + if (auto Result = DeduceTemplateArgumentsByTypeMatch( + S, TemplateParams, FunctionProtoParam->getReturnType(), + FunctionProtoArg->getReturnType(), Info, Deduced, 0)) return Result; - return DeduceTemplateArguments( - S, TemplateParams, FunctionProtoParam->param_type_begin(), - FunctionProtoParam->getNumParams(), - FunctionProtoArg->param_type_begin(), - FunctionProtoArg->getNumParams(), Info, Deduced, SubTDF); + // Check parameter types. + if (auto Result = DeduceTemplateArguments( + S, TemplateParams, FunctionProtoParam->param_type_begin(), + FunctionProtoParam->getNumParams(), + FunctionProtoArg->param_type_begin(), + FunctionProtoArg->getNumParams(), Info, Deduced, SubTDF)) + return Result; + + if (TDF & TDF_AllowCompatibleFunctionType) + return Sema::TDK_Success; + + // FIXME: Per core-2016/10/1019 (no corresponding core issue yet), permit + // deducing through the noexcept-specifier if it's part of the canonical + // type. libstdc++ relies on this. + Expr *NoexceptExpr = FunctionProtoParam->getNoexceptExpr(); + if (NonTypeTemplateParmDecl *NTTP = + NoexceptExpr ? getDeducedParameterFromExpr(Info, NoexceptExpr) + : nullptr) { + assert(NTTP->getDepth() == Info.getDeducedDepth() && + "saw non-type template parameter with wrong depth"); + + llvm::APSInt Noexcept(1); + switch (FunctionProtoArg->canThrow(S.Context)) { + case CT_Cannot: + Noexcept = 1; + LLVM_FALLTHROUGH; + + case CT_Can: + // We give E in noexcept(E) the "deduced from array bound" treatment. + // FIXME: Should we? + return DeduceNonTypeTemplateArgument( + S, TemplateParams, NTTP, Noexcept, S.Context.BoolTy, + /*ArrayBound*/true, Info, Deduced); + + case CT_Dependent: + if (Expr *ArgNoexceptExpr = FunctionProtoArg->getNoexceptExpr()) + return DeduceNonTypeTemplateArgument( + S, TemplateParams, NTTP, ArgNoexceptExpr, Info, Deduced); + // Can't deduce anything from throw(T...). + break; + } + } + // FIXME: Detect non-deduced exception specification mismatches? + + return Sema::TDK_Success; } case Type::InjectedClassName: { @@ -1544,7 +1588,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, ->getInjectedSpecializationType(); assert(isa<TemplateSpecializationType>(Param) && "injected class name is not a template specialization type"); - // fall through + LLVM_FALLTHROUGH; } // template-name<T> (where template-name refers to a class template) @@ -2820,6 +2864,17 @@ Sema::SubstituteExplicitTemplateArguments( if (FunctionType) { auto EPI = Proto->getExtProtoInfo(); EPI.ExtParameterInfos = ExtParamInfos.getPointerOrNull(ParamTypes.size()); + + // In C++1z onwards, exception specifications are part of the function type, + // so substitution into the type must also substitute into the exception + // specification. + SmallVector<QualType, 4> ExceptionStorage; + if (getLangOpts().CPlusPlus1z && + SubstExceptionSpec( + Function->getLocation(), EPI.ExceptionSpec, ExceptionStorage, + MultiLevelTemplateArgumentList(*ExplicitArgumentList))) + return TDK_SubstitutionFailure; + *FunctionType = BuildFunctionType(ResultType, ParamTypes, Function->getLocation(), Function->getDeclName(), @@ -3714,13 +3769,6 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments( = FunctionTemplate->getTemplateParameters(); QualType FunctionType = Function->getType(); - // When taking the address of a function, we require convertibility of - // the resulting function type. Otherwise, we allow arbitrary mismatches - // of calling convention, noreturn, and noexcept. - if (!IsAddressOfFunction) - ArgFunctionType = adjustCCAndNoReturn(ArgFunctionType, FunctionType, - /*AdjustExceptionSpec*/true); - // Substitute any explicit template arguments. LocalInstantiationScope InstScope(*this); SmallVector<DeducedTemplateArgument, 4> Deduced; @@ -3737,6 +3785,13 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments( NumExplicitlySpecified = Deduced.size(); } + // When taking the address of a function, we require convertibility of + // the resulting function type. Otherwise, we allow arbitrary mismatches + // of calling convention and noreturn. + if (!IsAddressOfFunction) + ArgFunctionType = adjustCCAndNoReturn(ArgFunctionType, FunctionType, + /*AdjustExceptionSpec*/false); + // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( *this, Sema::ExpressionEvaluationContext::Unevaluated); @@ -3756,9 +3811,8 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments( } if (!ArgFunctionType.isNull()) { - unsigned TDF = TDF_TopLevelParameterTypeList; - if (IsAddressOfFunction) - TDF |= TDF_InOverloadResolution; + unsigned TDF = + TDF_TopLevelParameterTypeList | TDF_AllowCompatibleFunctionType; // Deduce template arguments from the function type. if (TemplateDeductionResult Result = DeduceTemplateArgumentsByTypeMatch(*this, TemplateParams, @@ -3789,7 +3843,7 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments( !ResolveExceptionSpec(Info.getLocation(), SpecializationFPT)) return TDK_MiscellaneousDeductionFailure; - // Adjust the exception specification of the argument again to match the + // Adjust the exception specification of the argument to match the // substituted and resolved type we just formed. (Calling convention and // noreturn can't be dependent, so we don't actually need this for them // right now.) @@ -5127,6 +5181,8 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T, for (unsigned I = 0, N = Proto->getNumParams(); I != N; ++I) MarkUsedTemplateParameters(Ctx, Proto->getParamType(I), OnlyDeduced, Depth, Used); + if (auto *E = Proto->getNoexceptExpr()) + MarkUsedTemplateParameters(Ctx, E, OnlyDeduced, Depth, Used); break; } diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 2279c9ca55026f659e265417ea21e47a8da6612a..fe92dd8ac6530c1e9c848b2af149e7a894d0bd9b 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -1692,20 +1692,26 @@ TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T, return TLB.getTypeSourceInfo(Context, Result); } +bool Sema::SubstExceptionSpec(SourceLocation Loc, + FunctionProtoType::ExceptionSpecInfo &ESI, + SmallVectorImpl<QualType> &ExceptionStorage, + const MultiLevelTemplateArgumentList &Args) { + assert(ESI.Type != EST_Uninstantiated); + + bool Changed = false; + TemplateInstantiator Instantiator(*this, Args, Loc, DeclarationName()); + return Instantiator.TransformExceptionSpec(Loc, ESI, ExceptionStorage, + Changed); +} + void Sema::SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto, const MultiLevelTemplateArgumentList &Args) { FunctionProtoType::ExceptionSpecInfo ESI = Proto->getExtProtoInfo().ExceptionSpec; - assert(ESI.Type != EST_Uninstantiated); - - TemplateInstantiator Instantiator(*this, Args, New->getLocation(), - New->getDeclName()); SmallVector<QualType, 4> ExceptionStorage; - bool Changed = false; - if (Instantiator.TransformExceptionSpec( - New->getTypeSourceInfo()->getTypeLoc().getLocEnd(), ESI, - ExceptionStorage, Changed)) + if (SubstExceptionSpec(New->getTypeSourceInfo()->getTypeLoc().getLocEnd(), + ESI, ExceptionStorage, Args)) // On error, recover by dropping the exception specification. ESI.Type = EST_None; diff --git a/test/CXX/drs/dr13xx.cpp b/test/CXX/drs/dr13xx.cpp index 56ff1237480ff62537c762bd5ed560af9c272511..64ba3be2a618d19ee2ce1382cc868c05f4d7f979 100644 --- a/test/CXX/drs/dr13xx.cpp +++ b/test/CXX/drs/dr13xx.cpp @@ -192,7 +192,7 @@ namespace dr1330 { // dr1330: 4 c++11 static_assert(!noexcept(B<Q>().g()), ""); #endif - template<typename T> int f() throw(typename T::error) { return 0; } // expected-error 1-4{{prior to '::'}} expected-note 0-1{{instantiation of}} + template<typename T> int f() throw(typename T::error) { return 0; } // expected-error 1-4{{prior to '::'}} expected-note 0-1{{prior to '::'}} expected-note 0-1{{requested here}} #if __cplusplus > 201402L // expected-error@-2 0-1{{C++1z}} expected-note@-2 0-1{{noexcept}} #endif @@ -203,7 +203,16 @@ namespace dr1330 { // dr1330: 4 c++11 decltype(f<char>()) f2; // expected-note {{instantiation of}} bool f3 = noexcept(f<float>()); // expected-note {{instantiation of}} #endif - template int f<short>(); // expected-note {{instantiation of}} + // In C++1z onwards, substituting explicit template arguments into the + // function type substitutes into the exception specification (because it's + // part of the type). In earlier languages, we don't notice there's a problem + // until we've already started to instantiate. + template int f<short>(); +#if __cplusplus >= 201703L + // expected-error@-2 {{does not refer to a function template}} +#else + // expected-note@-4 {{instantiation of}} +#endif template<typename T> struct C { C() throw(typename T::type); // expected-error 1-2{{prior to '::'}} diff --git a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p4.cpp b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p4.cpp index e7b665a341371163eed00ce0c7d6029072f5d6b5..357ea664037df55467847f7c73877b76b6edbe65 100644 --- a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p4.cpp +++ b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p4.cpp @@ -46,10 +46,11 @@ namespace noexcept_conversion { template <class T> int h(T *, T *); // expected-note {{deduced conflicting types for parameter 'T' ('void () noexcept' vs. 'void ()')}} int x = h(g1, g2); // expected-error {{no matching function}} - // FIXME: It seems like a defect that B is not deducible here. - template<bool B> int i(void () noexcept(B)); // expected-note 2{{couldn't infer template argument 'B'}} - int i1 = i(g1); // expected-error {{no matching function}} - int i2 = i(g2); // expected-error {{no matching function}} + // We consider it a defect that deduction does not support the following. + // FIXME: Check that the defect is resolved as we expect. + template<bool B> int i(void () noexcept(B)); + int i1 = i(g1); + int i2 = i(g2); } #else // expected-no-diagnostics diff --git a/test/SemaCXX/cxx1z-noexcept-function-type.cpp b/test/SemaCXX/cxx1z-noexcept-function-type.cpp index a3f710970456bf7d5dff464aa2799e2288c118c1..40dc3a22e530b40a9f892fe3f46795c07bcca4a5 100644 --- a/test/SemaCXX/cxx1z-noexcept-function-type.cpp +++ b/test/SemaCXX/cxx1z-noexcept-function-type.cpp @@ -16,7 +16,7 @@ template<typename A, typename B> void redecl3() throw(B); // expected-error {{do typedef int I; template<bool B> void redecl4(I) noexcept(B); -template<bool B> void redecl4(I) noexcept(B); // expected-note {{failed template argument deduction}} +template<bool B> void redecl4(I) noexcept(B); // expected-note {{could not match 'void (I) noexcept(false)' (aka 'void (int) noexcept(false)') against 'void (int) noexcept'}} void (*init_with_exact_type_a)(int) noexcept = redecl4<true>; void (*init_with_mismatched_type_a)(int) = redecl4<true>; diff --git a/test/SemaTemplate/temp_arg_type.cpp b/test/SemaTemplate/temp_arg_type.cpp index daad61c14292c0b00ab32d8ba451dde4d55482c5..9069f63e0224fee4eb0589d280bb96dbee4a445d 100644 --- a/test/SemaTemplate/temp_arg_type.cpp +++ b/test/SemaTemplate/temp_arg_type.cpp @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s // RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s template<typename T> class A; // expected-note 2 {{template parameter is declared here}} expected-note{{template is declared here}} @@ -53,3 +54,56 @@ A1<Array<int, 17>::type> ax; // FIXME: [temp.arg.type]p3. The check doesn't really belong here (it // belongs somewhere in the template instantiation section). + +#if __cplusplus >= 201703 +// As a defect resolution, we support deducing B in noexcept(B). +namespace deduce_noexcept { + template<typename> struct function; + template<typename R, typename ...A, bool N> + struct function<R(A...) noexcept(N)> { + static constexpr bool Noexcept = N; + }; + static_assert(function<int(float, double) noexcept>::Noexcept); + static_assert(!function<int(float, double)>::Noexcept); + + void noexcept_function() noexcept; + void throwing_function(); + + template<typename T, bool B> float &deduce_function(T(*)() noexcept(B)); // expected-note {{candidate}} + template<typename T> int &deduce_function(T(*)() noexcept); // expected-note {{candidate}} + void test_function_deduction() { + // FIXME: This should probably unambiguously select the second overload. + int &r = deduce_function(noexcept_function); // expected-error {{ambiguous}} + float &s = deduce_function(throwing_function); + } + + namespace low_priority_deduction { + template<int> struct A {}; + template<auto B> void f(A<B>, void(*)() noexcept(B)) { + using T = decltype(B); + using T = int; + } + void g() { f(A<0>(), g); } // ok, deduce B as an int + } + + // FIXME: It's not clear whether this should work. We're told to deduce with + // P being the function template type and A being the declared type, which + // would accept this, but considering the exception specification in such + // cases breaks new/delete matching. + template<bool Noexcept> void dep() noexcept(Noexcept) {} // expected-note 3{{couldn't infer template argument 'Noexcept'}} + template void dep(); // expected-error {{does not refer to a function template}} + template void dep() noexcept(true); // expected-error {{does not refer to a function template}} + template void dep() noexcept(false); // expected-error {{does not refer to a function template}} + + // FIXME: It's also not clear whether this should be valid: do we substitute + // into the function type (including the exception specification) or not? + template<typename T> typename T::type1 f() noexcept(T::a); + template<typename T> typename T::type2 f() noexcept(T::b) {} + struct X { + static constexpr bool b = true; + using type1 = void; + using type2 = void; + }; + template void f<X>(); +} +#endif