Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit 410ea62

Browse files
committed
[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
1 parent a2380ee commit 410ea62

File tree

7 files changed

+175
-45
lines changed

7 files changed

+175
-45
lines changed

include/clang/Sema/Sema.h

+4
Original file line numberDiff line numberDiff line change
@@ -7555,6 +7555,10 @@ class Sema {
75557555
unsigned ThisTypeQuals);
75567556
void SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto,
75577557
const MultiLevelTemplateArgumentList &Args);
7558+
bool SubstExceptionSpec(SourceLocation Loc,
7559+
FunctionProtoType::ExceptionSpecInfo &ESI,
7560+
SmallVectorImpl<QualType> &ExceptionStorage,
7561+
const MultiLevelTemplateArgumentList &Args);
75587562
ParmVarDecl *SubstParmVarDecl(ParmVarDecl *D,
75597563
const MultiLevelTemplateArgumentList &TemplateArgs,
75607564
int indexAdjustment,

lib/Sema/SemaTemplateDeduction.cpp

+86-30
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,12 @@ namespace clang {
5656
TDF_TopLevelParameterTypeList = 0x10,
5757
/// \brief Within template argument deduction from overload resolution per
5858
/// C++ [over.over] allow matching function types that are compatible in
59-
/// terms of noreturn and default calling convention adjustments.
60-
TDF_InOverloadResolution = 0x20
59+
/// terms of noreturn and default calling convention adjustments, or
60+
/// similarly matching a declared template specialization against a
61+
/// possible template, per C++ [temp.deduct.decl]. In either case, permit
62+
/// deduction where the parameter is a function type that can be converted
63+
/// to the argument type.
64+
TDF_AllowCompatibleFunctionType = 0x20,
6165
};
6266
}
6367

@@ -1306,9 +1310,10 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
13061310
// If the parameter type is not dependent, there is nothing to deduce.
13071311
if (!Param->isDependentType()) {
13081312
if (!(TDF & TDF_SkipNonDependent)) {
1309-
bool NonDeduced = (TDF & TDF_InOverloadResolution)?
1310-
!S.isSameOrCompatibleFunctionType(CanParam, CanArg) :
1311-
Param != Arg;
1313+
bool NonDeduced =
1314+
(TDF & TDF_AllowCompatibleFunctionType)
1315+
? !S.isSameOrCompatibleFunctionType(CanParam, CanArg)
1316+
: Param != Arg;
13121317
if (NonDeduced) {
13131318
return Sema::TDK_NonDeducedMismatch;
13141319
}
@@ -1318,10 +1323,10 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
13181323
} else if (!Param->isDependentType()) {
13191324
CanQualType ParamUnqualType = CanParam.getUnqualifiedType(),
13201325
ArgUnqualType = CanArg.getUnqualifiedType();
1321-
bool Success = (TDF & TDF_InOverloadResolution)?
1322-
S.isSameOrCompatibleFunctionType(ParamUnqualType,
1323-
ArgUnqualType) :
1324-
ParamUnqualType == ArgUnqualType;
1326+
bool Success =
1327+
(TDF & TDF_AllowCompatibleFunctionType)
1328+
? S.isSameOrCompatibleFunctionType(ParamUnqualType, ArgUnqualType)
1329+
: ParamUnqualType == ArgUnqualType;
13251330
if (Success)
13261331
return Sema::TDK_Success;
13271332
}
@@ -1524,17 +1529,56 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
15241529
return Sema::TDK_NonDeducedMismatch;
15251530

15261531
// Check return types.
1527-
if (Sema::TemplateDeductionResult Result =
1528-
DeduceTemplateArgumentsByTypeMatch(
1529-
S, TemplateParams, FunctionProtoParam->getReturnType(),
1530-
FunctionProtoArg->getReturnType(), Info, Deduced, 0))
1532+
if (auto Result = DeduceTemplateArgumentsByTypeMatch(
1533+
S, TemplateParams, FunctionProtoParam->getReturnType(),
1534+
FunctionProtoArg->getReturnType(), Info, Deduced, 0))
15311535
return Result;
15321536

1533-
return DeduceTemplateArguments(
1534-
S, TemplateParams, FunctionProtoParam->param_type_begin(),
1535-
FunctionProtoParam->getNumParams(),
1536-
FunctionProtoArg->param_type_begin(),
1537-
FunctionProtoArg->getNumParams(), Info, Deduced, SubTDF);
1537+
// Check parameter types.
1538+
if (auto Result = DeduceTemplateArguments(
1539+
S, TemplateParams, FunctionProtoParam->param_type_begin(),
1540+
FunctionProtoParam->getNumParams(),
1541+
FunctionProtoArg->param_type_begin(),
1542+
FunctionProtoArg->getNumParams(), Info, Deduced, SubTDF))
1543+
return Result;
1544+
1545+
if (TDF & TDF_AllowCompatibleFunctionType)
1546+
return Sema::TDK_Success;
1547+
1548+
// FIXME: Per core-2016/10/1019 (no corresponding core issue yet), permit
1549+
// deducing through the noexcept-specifier if it's part of the canonical
1550+
// type. libstdc++ relies on this.
1551+
Expr *NoexceptExpr = FunctionProtoParam->getNoexceptExpr();
1552+
if (NonTypeTemplateParmDecl *NTTP =
1553+
NoexceptExpr ? getDeducedParameterFromExpr(Info, NoexceptExpr)
1554+
: nullptr) {
1555+
assert(NTTP->getDepth() == Info.getDeducedDepth() &&
1556+
"saw non-type template parameter with wrong depth");
1557+
1558+
llvm::APSInt Noexcept(1);
1559+
switch (FunctionProtoArg->canThrow(S.Context)) {
1560+
case CT_Cannot:
1561+
Noexcept = 1;
1562+
LLVM_FALLTHROUGH;
1563+
1564+
case CT_Can:
1565+
// We give E in noexcept(E) the "deduced from array bound" treatment.
1566+
// FIXME: Should we?
1567+
return DeduceNonTypeTemplateArgument(
1568+
S, TemplateParams, NTTP, Noexcept, S.Context.BoolTy,
1569+
/*ArrayBound*/true, Info, Deduced);
1570+
1571+
case CT_Dependent:
1572+
if (Expr *ArgNoexceptExpr = FunctionProtoArg->getNoexceptExpr())
1573+
return DeduceNonTypeTemplateArgument(
1574+
S, TemplateParams, NTTP, ArgNoexceptExpr, Info, Deduced);
1575+
// Can't deduce anything from throw(T...).
1576+
break;
1577+
}
1578+
}
1579+
// FIXME: Detect non-deduced exception specification mismatches?
1580+
1581+
return Sema::TDK_Success;
15381582
}
15391583

15401584
case Type::InjectedClassName: {
@@ -1544,7 +1588,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S,
15441588
->getInjectedSpecializationType();
15451589
assert(isa<TemplateSpecializationType>(Param) &&
15461590
"injected class name is not a template specialization type");
1547-
// fall through
1591+
LLVM_FALLTHROUGH;
15481592
}
15491593

15501594
// template-name<T> (where template-name refers to a class template)
@@ -2820,6 +2864,17 @@ Sema::SubstituteExplicitTemplateArguments(
28202864
if (FunctionType) {
28212865
auto EPI = Proto->getExtProtoInfo();
28222866
EPI.ExtParameterInfos = ExtParamInfos.getPointerOrNull(ParamTypes.size());
2867+
2868+
// In C++1z onwards, exception specifications are part of the function type,
2869+
// so substitution into the type must also substitute into the exception
2870+
// specification.
2871+
SmallVector<QualType, 4> ExceptionStorage;
2872+
if (getLangOpts().CPlusPlus1z &&
2873+
SubstExceptionSpec(
2874+
Function->getLocation(), EPI.ExceptionSpec, ExceptionStorage,
2875+
MultiLevelTemplateArgumentList(*ExplicitArgumentList)))
2876+
return TDK_SubstitutionFailure;
2877+
28232878
*FunctionType = BuildFunctionType(ResultType, ParamTypes,
28242879
Function->getLocation(),
28252880
Function->getDeclName(),
@@ -3714,13 +3769,6 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
37143769
= FunctionTemplate->getTemplateParameters();
37153770
QualType FunctionType = Function->getType();
37163771

3717-
// When taking the address of a function, we require convertibility of
3718-
// the resulting function type. Otherwise, we allow arbitrary mismatches
3719-
// of calling convention, noreturn, and noexcept.
3720-
if (!IsAddressOfFunction)
3721-
ArgFunctionType = adjustCCAndNoReturn(ArgFunctionType, FunctionType,
3722-
/*AdjustExceptionSpec*/true);
3723-
37243772
// Substitute any explicit template arguments.
37253773
LocalInstantiationScope InstScope(*this);
37263774
SmallVector<DeducedTemplateArgument, 4> Deduced;
@@ -3737,6 +3785,13 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
37373785
NumExplicitlySpecified = Deduced.size();
37383786
}
37393787

3788+
// When taking the address of a function, we require convertibility of
3789+
// the resulting function type. Otherwise, we allow arbitrary mismatches
3790+
// of calling convention and noreturn.
3791+
if (!IsAddressOfFunction)
3792+
ArgFunctionType = adjustCCAndNoReturn(ArgFunctionType, FunctionType,
3793+
/*AdjustExceptionSpec*/false);
3794+
37403795
// Unevaluated SFINAE context.
37413796
EnterExpressionEvaluationContext Unevaluated(
37423797
*this, Sema::ExpressionEvaluationContext::Unevaluated);
@@ -3756,9 +3811,8 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
37563811
}
37573812

37583813
if (!ArgFunctionType.isNull()) {
3759-
unsigned TDF = TDF_TopLevelParameterTypeList;
3760-
if (IsAddressOfFunction)
3761-
TDF |= TDF_InOverloadResolution;
3814+
unsigned TDF =
3815+
TDF_TopLevelParameterTypeList | TDF_AllowCompatibleFunctionType;
37623816
// Deduce template arguments from the function type.
37633817
if (TemplateDeductionResult Result
37643818
= DeduceTemplateArgumentsByTypeMatch(*this, TemplateParams,
@@ -3789,7 +3843,7 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
37893843
!ResolveExceptionSpec(Info.getLocation(), SpecializationFPT))
37903844
return TDK_MiscellaneousDeductionFailure;
37913845

3792-
// Adjust the exception specification of the argument again to match the
3846+
// Adjust the exception specification of the argument to match the
37933847
// substituted and resolved type we just formed. (Calling convention and
37943848
// noreturn can't be dependent, so we don't actually need this for them
37953849
// right now.)
@@ -5127,6 +5181,8 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
51275181
for (unsigned I = 0, N = Proto->getNumParams(); I != N; ++I)
51285182
MarkUsedTemplateParameters(Ctx, Proto->getParamType(I), OnlyDeduced,
51295183
Depth, Used);
5184+
if (auto *E = Proto->getNoexceptExpr())
5185+
MarkUsedTemplateParameters(Ctx, E, OnlyDeduced, Depth, Used);
51305186
break;
51315187
}
51325188

lib/Sema/SemaTemplateInstantiate.cpp

+14-8
Original file line numberDiff line numberDiff line change
@@ -1692,20 +1692,26 @@ TypeSourceInfo *Sema::SubstFunctionDeclType(TypeSourceInfo *T,
16921692
return TLB.getTypeSourceInfo(Context, Result);
16931693
}
16941694

1695+
bool Sema::SubstExceptionSpec(SourceLocation Loc,
1696+
FunctionProtoType::ExceptionSpecInfo &ESI,
1697+
SmallVectorImpl<QualType> &ExceptionStorage,
1698+
const MultiLevelTemplateArgumentList &Args) {
1699+
assert(ESI.Type != EST_Uninstantiated);
1700+
1701+
bool Changed = false;
1702+
TemplateInstantiator Instantiator(*this, Args, Loc, DeclarationName());
1703+
return Instantiator.TransformExceptionSpec(Loc, ESI, ExceptionStorage,
1704+
Changed);
1705+
}
1706+
16951707
void Sema::SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto,
16961708
const MultiLevelTemplateArgumentList &Args) {
16971709
FunctionProtoType::ExceptionSpecInfo ESI =
16981710
Proto->getExtProtoInfo().ExceptionSpec;
1699-
assert(ESI.Type != EST_Uninstantiated);
1700-
1701-
TemplateInstantiator Instantiator(*this, Args, New->getLocation(),
1702-
New->getDeclName());
17031711

17041712
SmallVector<QualType, 4> ExceptionStorage;
1705-
bool Changed = false;
1706-
if (Instantiator.TransformExceptionSpec(
1707-
New->getTypeSourceInfo()->getTypeLoc().getLocEnd(), ESI,
1708-
ExceptionStorage, Changed))
1713+
if (SubstExceptionSpec(New->getTypeSourceInfo()->getTypeLoc().getLocEnd(),
1714+
ESI, ExceptionStorage, Args))
17091715
// On error, recover by dropping the exception specification.
17101716
ESI.Type = EST_None;
17111717

test/CXX/drs/dr13xx.cpp

+11-2
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ namespace dr1330 { // dr1330: 4 c++11
192192
static_assert(!noexcept(B<Q>().g()), "");
193193
#endif
194194

195-
template<typename T> int f() throw(typename T::error) { return 0; } // expected-error 1-4{{prior to '::'}} expected-note 0-1{{instantiation of}}
195+
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}}
196196
#if __cplusplus > 201402L
197197
// expected-error@-2 0-1{{C++1z}} expected-note@-2 0-1{{noexcept}}
198198
#endif
@@ -203,7 +203,16 @@ namespace dr1330 { // dr1330: 4 c++11
203203
decltype(f<char>()) f2; // expected-note {{instantiation of}}
204204
bool f3 = noexcept(f<float>()); // expected-note {{instantiation of}}
205205
#endif
206-
template int f<short>(); // expected-note {{instantiation of}}
206+
// In C++1z onwards, substituting explicit template arguments into the
207+
// function type substitutes into the exception specification (because it's
208+
// part of the type). In earlier languages, we don't notice there's a problem
209+
// until we've already started to instantiate.
210+
template int f<short>();
211+
#if __cplusplus >= 201703L
212+
// expected-error@-2 {{does not refer to a function template}}
213+
#else
214+
// expected-note@-4 {{instantiation of}}
215+
#endif
207216

208217
template<typename T> struct C {
209218
C() throw(typename T::type); // expected-error 1-2{{prior to '::'}}

test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p4.cpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@ namespace noexcept_conversion {
4646
template <class T> int h(T *, T *); // expected-note {{deduced conflicting types for parameter 'T' ('void () noexcept' vs. 'void ()')}}
4747
int x = h(g1, g2); // expected-error {{no matching function}}
4848

49-
// FIXME: It seems like a defect that B is not deducible here.
50-
template<bool B> int i(void () noexcept(B)); // expected-note 2{{couldn't infer template argument 'B'}}
51-
int i1 = i(g1); // expected-error {{no matching function}}
52-
int i2 = i(g2); // expected-error {{no matching function}}
49+
// We consider it a defect that deduction does not support the following.
50+
// FIXME: Check that the defect is resolved as we expect.
51+
template<bool B> int i(void () noexcept(B));
52+
int i1 = i(g1);
53+
int i2 = i(g2);
5354
}
5455
#else
5556
// expected-no-diagnostics

test/SemaCXX/cxx1z-noexcept-function-type.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ template<typename A, typename B> void redecl3() throw(B); // expected-error {{do
1616

1717
typedef int I;
1818
template<bool B> void redecl4(I) noexcept(B);
19-
template<bool B> void redecl4(I) noexcept(B); // expected-note {{failed template argument deduction}}
19+
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'}}
2020

2121
void (*init_with_exact_type_a)(int) noexcept = redecl4<true>;
2222
void (*init_with_mismatched_type_a)(int) = redecl4<true>;

test/SemaTemplate/temp_arg_type.cpp

+54
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// RUN: %clang_cc1 -fsyntax-only -verify %s
22
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
33
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
4+
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s
45

56
template<typename T> class A; // expected-note 2 {{template parameter is declared here}} expected-note{{template is declared here}}
67

@@ -53,3 +54,56 @@ A1<Array<int, 17>::type> ax;
5354

5455
// FIXME: [temp.arg.type]p3. The check doesn't really belong here (it
5556
// belongs somewhere in the template instantiation section).
57+
58+
#if __cplusplus >= 201703
59+
// As a defect resolution, we support deducing B in noexcept(B).
60+
namespace deduce_noexcept {
61+
template<typename> struct function;
62+
template<typename R, typename ...A, bool N>
63+
struct function<R(A...) noexcept(N)> {
64+
static constexpr bool Noexcept = N;
65+
};
66+
static_assert(function<int(float, double) noexcept>::Noexcept);
67+
static_assert(!function<int(float, double)>::Noexcept);
68+
69+
void noexcept_function() noexcept;
70+
void throwing_function();
71+
72+
template<typename T, bool B> float &deduce_function(T(*)() noexcept(B)); // expected-note {{candidate}}
73+
template<typename T> int &deduce_function(T(*)() noexcept); // expected-note {{candidate}}
74+
void test_function_deduction() {
75+
// FIXME: This should probably unambiguously select the second overload.
76+
int &r = deduce_function(noexcept_function); // expected-error {{ambiguous}}
77+
float &s = deduce_function(throwing_function);
78+
}
79+
80+
namespace low_priority_deduction {
81+
template<int> struct A {};
82+
template<auto B> void f(A<B>, void(*)() noexcept(B)) {
83+
using T = decltype(B);
84+
using T = int;
85+
}
86+
void g() { f(A<0>(), g); } // ok, deduce B as an int
87+
}
88+
89+
// FIXME: It's not clear whether this should work. We're told to deduce with
90+
// P being the function template type and A being the declared type, which
91+
// would accept this, but considering the exception specification in such
92+
// cases breaks new/delete matching.
93+
template<bool Noexcept> void dep() noexcept(Noexcept) {} // expected-note 3{{couldn't infer template argument 'Noexcept'}}
94+
template void dep(); // expected-error {{does not refer to a function template}}
95+
template void dep() noexcept(true); // expected-error {{does not refer to a function template}}
96+
template void dep() noexcept(false); // expected-error {{does not refer to a function template}}
97+
98+
// FIXME: It's also not clear whether this should be valid: do we substitute
99+
// into the function type (including the exception specification) or not?
100+
template<typename T> typename T::type1 f() noexcept(T::a);
101+
template<typename T> typename T::type2 f() noexcept(T::b) {}
102+
struct X {
103+
static constexpr bool b = true;
104+
using type1 = void;
105+
using type2 = void;
106+
};
107+
template void f<X>();
108+
}
109+
#endif

0 commit comments

Comments
 (0)