[c++1z] Support deducing B in noexcept(B).

This is not required by the standard (yet), but there seems to be reasonable
support for this being a defect according to CWG discussion, and libstdc++ 7.1
relies on it working.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@304946 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Richard Smith 2017-06-07 21:46:22 +00:00
parent a2380ee70a
commit 410ea620fd
7 changed files with 175 additions and 45 deletions

View File

@ -7555,6 +7555,10 @@ public:
unsigned ThisTypeQuals); unsigned ThisTypeQuals);
void SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto, void SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto,
const MultiLevelTemplateArgumentList &Args); const MultiLevelTemplateArgumentList &Args);
bool SubstExceptionSpec(SourceLocation Loc,
FunctionProtoType::ExceptionSpecInfo &ESI,
SmallVectorImpl<QualType> &ExceptionStorage,
const MultiLevelTemplateArgumentList &Args);
ParmVarDecl *SubstParmVarDecl(ParmVarDecl *D, ParmVarDecl *SubstParmVarDecl(ParmVarDecl *D,
const MultiLevelTemplateArgumentList &TemplateArgs, const MultiLevelTemplateArgumentList &TemplateArgs,
int indexAdjustment, int indexAdjustment,

View File

@ -56,8 +56,12 @@ namespace clang {
TDF_TopLevelParameterTypeList = 0x10, TDF_TopLevelParameterTypeList = 0x10,
/// \brief Within template argument deduction from overload resolution per /// \brief Within template argument deduction from overload resolution per
/// C++ [over.over] allow matching function types that are compatible in /// C++ [over.over] allow matching function types that are compatible in
/// terms of noreturn and default calling convention adjustments. /// terms of noreturn and default calling convention adjustments, or
TDF_InOverloadResolution = 0x20 /// 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 the parameter type is not dependent, there is nothing to deduce.
if (!Param->isDependentType()) { if (!Param->isDependentType()) {
if (!(TDF & TDF_SkipNonDependent)) { if (!(TDF & TDF_SkipNonDependent)) {
bool NonDeduced = (TDF & TDF_InOverloadResolution)? bool NonDeduced =
!S.isSameOrCompatibleFunctionType(CanParam, CanArg) : (TDF & TDF_AllowCompatibleFunctionType)
Param != Arg; ? !S.isSameOrCompatibleFunctionType(CanParam, CanArg)
: Param != Arg;
if (NonDeduced) { if (NonDeduced) {
return Sema::TDK_NonDeducedMismatch; return Sema::TDK_NonDeducedMismatch;
} }
@ -1318,10 +1323,10 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
} else if (!Param->isDependentType()) { } else if (!Param->isDependentType()) {
CanQualType ParamUnqualType = CanParam.getUnqualifiedType(), CanQualType ParamUnqualType = CanParam.getUnqualifiedType(),
ArgUnqualType = CanArg.getUnqualifiedType(); ArgUnqualType = CanArg.getUnqualifiedType();
bool Success = (TDF & TDF_InOverloadResolution)? bool Success =
S.isSameOrCompatibleFunctionType(ParamUnqualType, (TDF & TDF_AllowCompatibleFunctionType)
ArgUnqualType) : ? S.isSameOrCompatibleFunctionType(ParamUnqualType, ArgUnqualType)
ParamUnqualType == ArgUnqualType; : ParamUnqualType == ArgUnqualType;
if (Success) if (Success)
return Sema::TDK_Success; return Sema::TDK_Success;
} }
@ -1524,17 +1529,56 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
return Sema::TDK_NonDeducedMismatch; return Sema::TDK_NonDeducedMismatch;
// Check return types. // Check return types.
if (Sema::TemplateDeductionResult Result = if (auto Result = DeduceTemplateArgumentsByTypeMatch(
DeduceTemplateArgumentsByTypeMatch( S, TemplateParams, FunctionProtoParam->getReturnType(),
S, TemplateParams, FunctionProtoParam->getReturnType(), FunctionProtoArg->getReturnType(), Info, Deduced, 0))
FunctionProtoArg->getReturnType(), Info, Deduced, 0))
return Result; return Result;
return DeduceTemplateArguments( // Check parameter types.
S, TemplateParams, FunctionProtoParam->param_type_begin(), if (auto Result = DeduceTemplateArguments(
FunctionProtoParam->getNumParams(), S, TemplateParams, FunctionProtoParam->param_type_begin(),
FunctionProtoArg->param_type_begin(), FunctionProtoParam->getNumParams(),
FunctionProtoArg->getNumParams(), Info, Deduced, SubTDF); 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: { case Type::InjectedClassName: {
@ -1544,7 +1588,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
->getInjectedSpecializationType(); ->getInjectedSpecializationType();
assert(isa<TemplateSpecializationType>(Param) && assert(isa<TemplateSpecializationType>(Param) &&
"injected class name is not a template specialization type"); "injected class name is not a template specialization type");
// fall through LLVM_FALLTHROUGH;
} }
// template-name<T> (where template-name refers to a class template) // template-name<T> (where template-name refers to a class template)
@ -2820,6 +2864,17 @@ Sema::SubstituteExplicitTemplateArguments(
if (FunctionType) { if (FunctionType) {
auto EPI = Proto->getExtProtoInfo(); auto EPI = Proto->getExtProtoInfo();
EPI.ExtParameterInfos = ExtParamInfos.getPointerOrNull(ParamTypes.size()); 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, *FunctionType = BuildFunctionType(ResultType, ParamTypes,
Function->getLocation(), Function->getLocation(),
Function->getDeclName(), Function->getDeclName(),
@ -3714,13 +3769,6 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
= FunctionTemplate->getTemplateParameters(); = FunctionTemplate->getTemplateParameters();
QualType FunctionType = Function->getType(); 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. // Substitute any explicit template arguments.
LocalInstantiationScope InstScope(*this); LocalInstantiationScope InstScope(*this);
SmallVector<DeducedTemplateArgument, 4> Deduced; SmallVector<DeducedTemplateArgument, 4> Deduced;
@ -3737,6 +3785,13 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
NumExplicitlySpecified = Deduced.size(); 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. // Unevaluated SFINAE context.
EnterExpressionEvaluationContext Unevaluated( EnterExpressionEvaluationContext Unevaluated(
*this, Sema::ExpressionEvaluationContext::Unevaluated); *this, Sema::ExpressionEvaluationContext::Unevaluated);
@ -3756,9 +3811,8 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
} }
if (!ArgFunctionType.isNull()) { if (!ArgFunctionType.isNull()) {
unsigned TDF = TDF_TopLevelParameterTypeList; unsigned TDF =
if (IsAddressOfFunction) TDF_TopLevelParameterTypeList | TDF_AllowCompatibleFunctionType;
TDF |= TDF_InOverloadResolution;
// Deduce template arguments from the function type. // Deduce template arguments from the function type.
if (TemplateDeductionResult Result if (TemplateDeductionResult Result
= DeduceTemplateArgumentsByTypeMatch(*this, TemplateParams, = DeduceTemplateArgumentsByTypeMatch(*this, TemplateParams,
@ -3789,7 +3843,7 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
!ResolveExceptionSpec(Info.getLocation(), SpecializationFPT)) !ResolveExceptionSpec(Info.getLocation(), SpecializationFPT))
return TDK_MiscellaneousDeductionFailure; 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 // substituted and resolved type we just formed. (Calling convention and
// noreturn can't be dependent, so we don't actually need this for them // noreturn can't be dependent, so we don't actually need this for them
// right now.) // right now.)
@ -5127,6 +5181,8 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
for (unsigned I = 0, N = Proto->getNumParams(); I != N; ++I) for (unsigned I = 0, N = Proto->getNumParams(); I != N; ++I)
MarkUsedTemplateParameters(Ctx, Proto->getParamType(I), OnlyDeduced, MarkUsedTemplateParameters(Ctx, Proto->getParamType(I), OnlyDeduced,
Depth, Used); Depth, Used);
if (auto *E = Proto->getNoexceptExpr())
MarkUsedTemplateParameters(Ctx, E, OnlyDeduced, Depth, Used);
break; break;
} }

View File

@ -1692,20 +1692,26 @@ TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T,
return TLB.getTypeSourceInfo(Context, Result); 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, void Sema::SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto,
const MultiLevelTemplateArgumentList &Args) { const MultiLevelTemplateArgumentList &Args) {
FunctionProtoType::ExceptionSpecInfo ESI = FunctionProtoType::ExceptionSpecInfo ESI =
Proto->getExtProtoInfo().ExceptionSpec; Proto->getExtProtoInfo().ExceptionSpec;
assert(ESI.Type != EST_Uninstantiated);
TemplateInstantiator Instantiator(*this, Args, New->getLocation(),
New->getDeclName());
SmallVector<QualType, 4> ExceptionStorage; SmallVector<QualType, 4> ExceptionStorage;
bool Changed = false; if (SubstExceptionSpec(New->getTypeSourceInfo()->getTypeLoc().getLocEnd(),
if (Instantiator.TransformExceptionSpec( ESI, ExceptionStorage, Args))
New->getTypeSourceInfo()->getTypeLoc().getLocEnd(), ESI,
ExceptionStorage, Changed))
// On error, recover by dropping the exception specification. // On error, recover by dropping the exception specification.
ESI.Type = EST_None; ESI.Type = EST_None;

View File

@ -192,7 +192,7 @@ namespace dr1330 { // dr1330: 4 c++11
static_assert(!noexcept(B<Q>().g()), ""); static_assert(!noexcept(B<Q>().g()), "");
#endif #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 #if __cplusplus > 201402L
// expected-error@-2 0-1{{C++1z}} expected-note@-2 0-1{{noexcept}} // expected-error@-2 0-1{{C++1z}} expected-note@-2 0-1{{noexcept}}
#endif #endif
@ -203,7 +203,16 @@ namespace dr1330 { // dr1330: 4 c++11
decltype(f<char>()) f2; // expected-note {{instantiation of}} decltype(f<char>()) f2; // expected-note {{instantiation of}}
bool f3 = noexcept(f<float>()); // expected-note {{instantiation of}} bool f3 = noexcept(f<float>()); // expected-note {{instantiation of}}
#endif #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 { template<typename T> struct C {
C() throw(typename T::type); // expected-error 1-2{{prior to '::'}} C() throw(typename T::type); // expected-error 1-2{{prior to '::'}}

View File

@ -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 ()')}} 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}} int x = h(g1, g2); // expected-error {{no matching function}}
// FIXME: It seems like a defect that B is not deducible here. // We consider it a defect that deduction does not support the following.
template<bool B> int i(void () noexcept(B)); // expected-note 2{{couldn't infer template argument 'B'}} // FIXME: Check that the defect is resolved as we expect.
int i1 = i(g1); // expected-error {{no matching function}} template<bool B> int i(void () noexcept(B));
int i2 = i(g2); // expected-error {{no matching function}} int i1 = i(g1);
int i2 = i(g2);
} }
#else #else
// expected-no-diagnostics // expected-no-diagnostics

View File

@ -16,7 +16,7 @@ template<typename A, typename B> void redecl3() throw(B); // expected-error {{do
typedef int I; typedef int I;
template<bool B> void redecl4(I) noexcept(B); 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_exact_type_a)(int) noexcept = redecl4<true>;
void (*init_with_mismatched_type_a)(int) = redecl4<true>; void (*init_with_mismatched_type_a)(int) = redecl4<true>;

View File

@ -1,6 +1,7 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s // 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++98 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %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}} 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 // FIXME: [temp.arg.type]p3. The check doesn't really belong here (it
// belongs somewhere in the template instantiation section). // 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