From: Richard Smith Date: Thu, 1 Dec 2016 02:11:49 +0000 (+0000) Subject: PR31081: ignore exception specifications when deducing function template X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=61df53b690c63a68e51625043c0a930c440c72dc;p=clang PR31081: ignore exception specifications when deducing function template arguments from a declaration; despite what the standard says, this form of deduction should not be considering exception specifications. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@288301 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 0d8abb56a1..2fa91a3afb 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -6515,7 +6515,12 @@ public: // C++ Template Argument Deduction (C++ [temp.deduct]) //===--------------------------------------------------------------------===// - QualType adjustCCAndNoReturn(QualType ArgFunctionType, QualType FunctionType); + /// Adjust the type \p ArgFunctionType to match the calling convention, + /// noreturn, and optionally the exception specification of \p FunctionType. + /// Deduction often wants to ignore these properties when matching function + /// types. + QualType adjustCCAndNoReturn(QualType ArgFunctionType, QualType FunctionType, + bool AdjustExceptionSpec = false); /// \brief Describes the result of template argument deduction. /// @@ -6624,7 +6629,7 @@ public: QualType ArgFunctionType, FunctionDecl *&Specialization, sema::TemplateDeductionInfo &Info, - bool InOverloadResolution = false); + bool IsAddressOfFunction = false); TemplateDeductionResult DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, @@ -6637,7 +6642,7 @@ public: TemplateArgumentListInfo *ExplicitTemplateArgs, FunctionDecl *&Specialization, sema::TemplateDeductionInfo &Info, - bool InOverloadResolution = false); + bool IsAddressOfFunction = false); /// \brief Substitute Replacement for \p auto in \p TypeWithAuto QualType SubstAutoType(QualType TypeWithAuto, QualType Replacement); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index dfbbf01a51..79b5356d79 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -2319,8 +2319,6 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, // To perform this comparison, we compute the function type that // the deallocation function should have, and use that type both // for template argument deduction and for comparison purposes. - // - // FIXME: this comparison should ignore CC and the like. QualType ExpectedFunctionType; { const FunctionProtoType *Proto @@ -2334,7 +2332,6 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, FunctionProtoType::ExtProtoInfo EPI; // FIXME: This is not part of the standard's rule. EPI.Variadic = Proto->isVariadic(); - EPI.ExceptionSpec.Type = EST_BasicNoexcept; ExpectedFunctionType = Context.getFunctionType(Context.VoidTy, ArgTypes, EPI); @@ -2344,8 +2341,8 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, DEnd = FoundDelete.end(); D != DEnd; ++D) { FunctionDecl *Fn = nullptr; - if (FunctionTemplateDecl *FnTmpl - = dyn_cast((*D)->getUnderlyingDecl())) { + if (FunctionTemplateDecl *FnTmpl = + dyn_cast((*D)->getUnderlyingDecl())) { // Perform template argument deduction to try to match the // expected function type. TemplateDeductionInfo Info(StartLoc); @@ -2355,7 +2352,10 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, } else Fn = cast((*D)->getUnderlyingDecl()); - if (Context.hasSameType(Fn->getType(), ExpectedFunctionType)) + if (Context.hasSameType(adjustCCAndNoReturn(Fn->getType(), + ExpectedFunctionType, + /*AdjustExcpetionSpec*/true), + ExpectedFunctionType)) Matches.push_back(std::make_pair(D.getPair(), Fn)); } diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index cf90a34383..5d28d16f11 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -10572,7 +10572,7 @@ private: = S.DeduceTemplateArguments(FunctionTemplate, &OvlExplicitTemplateArgs, TargetFunctionType, Specialization, - Info, /*InOverloadResolution=*/true)) { + Info, /*IsAddressOfFunction*/true)) { // Make a note of the failed deduction for diagnostics. FailedCandidates.addCandidate() .set(CurAccessFunPair, FunctionTemplate->getTemplatedDecl(), @@ -10975,7 +10975,7 @@ Sema::ResolveSingleFunctionTemplateSpecialization(OverloadExpr *ovl, if (TemplateDeductionResult Result = DeduceTemplateArguments(FunctionTemplate, &ExplicitTemplateArgs, Specialization, Info, - /*InOverloadResolution=*/true)) { + /*IsAddressOfFunction*/true)) { // Make a note of the failed deduction for diagnostics. // TODO: Actually use the failed-deduction info? FailedCandidates.addCandidate() diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 4e941a53b8..9a6434579f 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -8075,7 +8075,8 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, NamedDecl *Prev = *P; if (!HasExplicitTemplateArgs) { if (CXXMethodDecl *Method = dyn_cast(Prev)) { - QualType Adjusted = adjustCCAndNoReturn(R, Method->getType()); + QualType Adjusted = adjustCCAndNoReturn(R, Method->getType(), + /*AdjustExceptionSpec*/true); if (Context.hasSameUnqualifiedType(Method->getType(), Adjusted)) { Matches.clear(); diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 11d744b72b..dc96d3f544 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -3571,25 +3571,42 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments( } QualType Sema::adjustCCAndNoReturn(QualType ArgFunctionType, - QualType FunctionType) { + QualType FunctionType, + bool AdjustExceptionSpec) { if (ArgFunctionType.isNull()) return ArgFunctionType; const FunctionProtoType *FunctionTypeP = FunctionType->castAs(); - CallingConv CC = FunctionTypeP->getCallConv(); - bool NoReturn = FunctionTypeP->getNoReturnAttr(); const FunctionProtoType *ArgFunctionTypeP = ArgFunctionType->getAs(); - if (ArgFunctionTypeP->getCallConv() == CC && - ArgFunctionTypeP->getNoReturnAttr() == NoReturn) + + FunctionProtoType::ExtProtoInfo EPI = ArgFunctionTypeP->getExtProtoInfo(); + bool Rebuild = false; + + CallingConv CC = FunctionTypeP->getCallConv(); + if (EPI.ExtInfo.getCC() != CC) { + EPI.ExtInfo = EPI.ExtInfo.withCallingConv(CC); + Rebuild = true; + } + + bool NoReturn = FunctionTypeP->getNoReturnAttr(); + if (EPI.ExtInfo.getNoReturn() != NoReturn) { + EPI.ExtInfo = EPI.ExtInfo.withNoReturn(NoReturn); + Rebuild = true; + } + + if (AdjustExceptionSpec && (FunctionTypeP->hasExceptionSpec() || + ArgFunctionTypeP->hasExceptionSpec())) { + EPI.ExceptionSpec = FunctionTypeP->getExtProtoInfo().ExceptionSpec; + Rebuild = true; + } + + if (!Rebuild) return ArgFunctionType; - FunctionType::ExtInfo EI = ArgFunctionTypeP->getExtInfo().withCallingConv(CC); - EI = EI.withNoReturn(NoReturn); - ArgFunctionTypeP = - cast(Context.adjustFunctionType(ArgFunctionTypeP, EI)); - return QualType(ArgFunctionTypeP, 0); + return Context.getFunctionType(ArgFunctionTypeP->getReturnType(), + ArgFunctionTypeP->getParamTypes(), EPI); } /// \brief Deduce template arguments when taking the address of a function @@ -3614,14 +3631,17 @@ QualType Sema::adjustCCAndNoReturn(QualType ArgFunctionType, /// \param Info the argument will be updated to provide additional information /// about template argument deduction. /// +/// \param IsAddressOfFunction If \c true, we are deducing as part of taking +/// the address of a function template per [temp.deduct.funcaddr] and +/// [over.over]. If \c false, we are looking up a function template +/// specialization based on its signature, per [temp.deduct.decl]. +/// /// \returns the result of template argument deduction. -Sema::TemplateDeductionResult -Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, - TemplateArgumentListInfo *ExplicitTemplateArgs, - QualType ArgFunctionType, - FunctionDecl *&Specialization, - TemplateDeductionInfo &Info, - bool InOverloadResolution) { +Sema::TemplateDeductionResult Sema::DeduceTemplateArguments( + FunctionTemplateDecl *FunctionTemplate, + TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ArgFunctionType, + FunctionDecl *&Specialization, TemplateDeductionInfo &Info, + bool IsAddressOfFunction) { if (FunctionTemplate->isInvalidDecl()) return TDK_Invalid; @@ -3629,8 +3649,13 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, TemplateParameterList *TemplateParams = FunctionTemplate->getTemplateParameters(); QualType FunctionType = Function->getType(); - if (!InOverloadResolution) - ArgFunctionType = adjustCCAndNoReturn(ArgFunctionType, FunctionType); + + // 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); @@ -3655,9 +3680,11 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, Deduced.resize(TemplateParams->size()); // If the function has a deduced return type, substitute it for a dependent - // type so that we treat it as a non-deduced context in what follows. + // type so that we treat it as a non-deduced context in what follows. If we + // are looking up by signature, the signature type should also have a deduced + // return type, which we instead expect to exactly match. bool HasDeducedReturnType = false; - if (getLangOpts().CPlusPlus14 && InOverloadResolution && + if (getLangOpts().CPlusPlus14 && IsAddressOfFunction && Function->getReturnType()->getContainedAutoType()) { FunctionType = SubstAutoType(FunctionType, Context.DependentTy); HasDeducedReturnType = true; @@ -3665,7 +3692,8 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, if (!ArgFunctionType.isNull()) { unsigned TDF = TDF_TopLevelParameterTypeList; - if (InOverloadResolution) TDF |= TDF_InOverloadResolution; + if (IsAddressOfFunction) + TDF |= TDF_InOverloadResolution; // Deduce template arguments from the function type. if (TemplateDeductionResult Result = DeduceTemplateArgumentsByTypeMatch(*this, TemplateParams, @@ -3696,16 +3724,27 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, !ResolveExceptionSpec(Info.getLocation(), SpecializationFPT)) return TDK_MiscellaneousDeductionFailure; + // Adjust the exception specification of the argument again 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.) + QualType SpecializationType = Specialization->getType(); + if (!IsAddressOfFunction) + ArgFunctionType = adjustCCAndNoReturn(ArgFunctionType, SpecializationType, + /*AdjustExceptionSpec*/true); + // If the requested function type does not match the actual type of the // specialization with respect to arguments of compatible pointer to function // types, template argument deduction fails. if (!ArgFunctionType.isNull()) { - if (InOverloadResolution && !isSameOrCompatibleFunctionType( - Context.getCanonicalType(Specialization->getType()), - Context.getCanonicalType(ArgFunctionType))) + if (IsAddressOfFunction && + !isSameOrCompatibleFunctionType( + Context.getCanonicalType(SpecializationType), + Context.getCanonicalType(ArgFunctionType))) return TDK_MiscellaneousDeductionFailure; - else if(!InOverloadResolution && - !Context.hasSameType(Specialization->getType(), ArgFunctionType)) + + if (!IsAddressOfFunction && + !Context.hasSameType(SpecializationType, ArgFunctionType)) return TDK_MiscellaneousDeductionFailure; } @@ -3977,16 +4016,22 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *ConversionTemplate, /// \param Info the argument will be updated to provide additional information /// about template argument deduction. /// +/// \param IsAddressOfFunction If \c true, we are deducing as part of taking +/// the address of a function template in a context where we do not have a +/// target type, per [over.over]. If \c false, we are looking up a function +/// template specialization based on its signature, which only happens when +/// deducing a function parameter type from an argument that is a template-id +/// naming a function template specialization. +/// /// \returns the result of template argument deduction. -Sema::TemplateDeductionResult -Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, - TemplateArgumentListInfo *ExplicitTemplateArgs, - FunctionDecl *&Specialization, - TemplateDeductionInfo &Info, - bool InOverloadResolution) { +Sema::TemplateDeductionResult Sema::DeduceTemplateArguments( + FunctionTemplateDecl *FunctionTemplate, + TemplateArgumentListInfo *ExplicitTemplateArgs, + FunctionDecl *&Specialization, TemplateDeductionInfo &Info, + bool IsAddressOfFunction) { return DeduceTemplateArguments(FunctionTemplate, ExplicitTemplateArgs, QualType(), Specialization, Info, - InOverloadResolution); + IsAddressOfFunction); } namespace { diff --git a/test/SemaCXX/cxx1z-noexcept-function-type.cpp b/test/SemaCXX/cxx1z-noexcept-function-type.cpp index 105aaa92f9..0b22e3c86d 100644 --- a/test/SemaCXX/cxx1z-noexcept-function-type.cpp +++ b/test/SemaCXX/cxx1z-noexcept-function-type.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -std=c++14 -verify %s -// RUN: %clang_cc1 -std=c++1z -verify %s +// RUN: %clang_cc1 -std=c++14 -verify -fexceptions -fcxx-exceptions %s +// RUN: %clang_cc1 -std=c++1z -verify -fexceptions -fcxx-exceptions %s #if __cplusplus > 201402L @@ -106,3 +106,47 @@ namespace Builtins { typedef int arr[strcmp("bar", "foo") + 4 * strncmp("foo", "bar", 4)]; typedef int arr[3]; } + +namespace ExplicitInstantiation { + template void f() noexcept {} + template struct X { void f() noexcept {} }; + template void f(); + template void X::f(); +} + +namespace ConversionFunction { + struct A { template operator T() noexcept; }; + int a = A().operator int(); +} + +using size_t = decltype(sizeof(0)); + +namespace OperatorDelete { + struct W {}; + struct X {}; + struct Y {}; + struct Z {}; + template struct T {}; +} +void *operator new(size_t, OperatorDelete::W) noexcept(false); +void operator delete(void*, OperatorDelete::W) noexcept(false) = delete; // expected-note {{here}} +void *operator new(size_t, OperatorDelete::X) noexcept(false); +void operator delete(void*, OperatorDelete::X) noexcept(true) = delete; // expected-note {{here}} +void *operator new(size_t, OperatorDelete::Y) noexcept(true); +void operator delete(void*, OperatorDelete::Y) noexcept(false) = delete; // expected-note {{here}} +void *operator new(size_t, OperatorDelete::Z) noexcept(true); +void operator delete(void*, OperatorDelete::Z) noexcept(true) = delete; // expected-note {{here}} +template void *operator new(size_t, OperatorDelete::T) noexcept(N); +template void operator delete(void*, OperatorDelete::T) noexcept(D) = delete; // expected-note 4{{here}} +namespace OperatorDelete { + struct A { A(); }; + A *w = new (W{}) A; // expected-error {{deleted function}} + A *x = new (X{}) A; // expected-error {{deleted function}} + A *y = new (Y{}) A; // expected-error {{deleted function}} + A *z = new (Z{}) A; // expected-error {{deleted function}} + + A *t00 = new (T{}) A; // expected-error {{deleted function}} + A *t01 = new (T{}) A; // expected-error {{deleted function}} + A *t10 = new (T{}) A; // expected-error {{deleted function}} + A *t11 = new (T{}) A; // expected-error {{deleted function}} +}