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.

llvm-svn: 288301
This commit is contained in:
Richard Smith 2016-12-01 02:11:49 +00:00
parent b66cb88c2e
commit baa4783d31
6 changed files with 143 additions and 48 deletions

View File

@ -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);

View File

@ -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<FunctionTemplateDecl>((*D)->getUnderlyingDecl())) {
if (FunctionTemplateDecl *FnTmpl =
dyn_cast<FunctionTemplateDecl>((*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<FunctionDecl>((*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));
}

View File

@ -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()

View File

@ -8075,7 +8075,8 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
NamedDecl *Prev = *P;
if (!HasExplicitTemplateArgs) {
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Prev)) {
QualType Adjusted = adjustCCAndNoReturn(R, Method->getType());
QualType Adjusted = adjustCCAndNoReturn(R, Method->getType(),
/*AdjustExceptionSpec*/true);
if (Context.hasSameUnqualifiedType(Method->getType(), Adjusted)) {
Matches.clear();

View File

@ -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<FunctionProtoType>();
CallingConv CC = FunctionTypeP->getCallConv();
bool NoReturn = FunctionTypeP->getNoReturnAttr();
const FunctionProtoType *ArgFunctionTypeP =
ArgFunctionType->getAs<FunctionProtoType>();
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<FunctionProtoType>(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 {

View File

@ -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<typename T> void f() noexcept {}
template<typename T> struct X { void f() noexcept {} };
template void f<int>();
template void X<int>::f();
}
namespace ConversionFunction {
struct A { template<typename T> 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<bool N, bool D> 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<bool N, bool D> void *operator new(size_t, OperatorDelete::T<N, D>) noexcept(N);
template<bool N, bool D> void operator delete(void*, OperatorDelete::T<N, D>) 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<false, false>{}) A; // expected-error {{deleted function}}
A *t01 = new (T<false, true>{}) A; // expected-error {{deleted function}}
A *t10 = new (T<true, false>{}) A; // expected-error {{deleted function}}
A *t11 = new (T<true, true>{}) A; // expected-error {{deleted function}}
}