mirror of https://github.com/microsoft/clang.git
[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:
parent
a2380ee70a
commit
410ea620fd
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
if (auto Result = DeduceTemplateArgumentsByTypeMatch(
|
||||
S, TemplateParams, FunctionProtoParam->getReturnType(),
|
||||
FunctionProtoArg->getReturnType(), Info, Deduced, 0))
|
||||
return Result;
|
||||
|
||||
return DeduceTemplateArguments(
|
||||
// Check parameter types.
|
||||
if (auto Result = DeduceTemplateArguments(
|
||||
S, TemplateParams, FunctionProtoParam->param_type_begin(),
|
||||
FunctionProtoParam->getNumParams(),
|
||||
FunctionProtoArg->param_type_begin(),
|
||||
FunctionProtoArg->getNumParams(), Info, Deduced, SubTDF);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 '::'}}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue