Skip to content

Commit e29c085

Browse files
authored
[clang] disallow narrowing when matching template template parameters (#124313)
This fixes the core issue described in P3579, following the design intent of P0522 to not introduce any new cases where a template template parameter match is allowed for a template which is not valid for all possible uses. With this patch, narrowing conversions are disallowed for TTP matching. This reuses the existing machinery for diagnosing narrowing in a converted constant expression. Since P0522 is a DR and we apply it all the way back to C++98, this brings that machinery to use in older standards, in this very narrow scope of TTP matching. This still doesn't solve the ambiguity when partial ordering NTTPs of different integral types, this is blocked by a different bug which will be fixed in a subsequent patch (but the test cases are added).
1 parent eb10e94 commit e29c085

25 files changed

+230
-98
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,9 @@ C++17 Feature Support
337337
^^^^^^^^^^^^^^^^^^^^^
338338
- The implementation of the relaxed template template argument matching rules is
339339
more complete and reliable, and should provide more accurate diagnostics.
340+
This implements:
341+
- `P3310R5: Solving issues introduced by relaxed template template parameter matching <https://wg21.link/p3310r5>`_.
342+
- `P3579R0: Fix matching of non-type template parameters when matching template template parameters <https://wg21.link/p3579r0>`_.
340343

341344
Resolutions to C++ Defect Reports
342345
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,11 @@ def err_typecheck_converted_constant_expression_indirect : Error<
8282
"conversion from %0 to %1 in converted constant expression would "
8383
"bind reference to a temporary">;
8484
def err_expr_not_cce : Error<
85-
"%select{case value|enumerator value|non-type template argument|"
85+
"%select{case value|enumerator value|non-type template argument|non-type parameter of template template parameter|"
8686
"array size|explicit specifier argument|noexcept specifier argument|"
8787
"call to 'size()'|call to 'data()'}0 is not a constant expression">;
8888
def ext_cce_narrowing : ExtWarn<
89-
"%select{case value|enumerator value|non-type template argument|"
89+
"%select{case value|enumerator value|non-type template argument|non-type parameter of template template parameter|"
9090
"array size|explicit specifier argument|noexcept specifier argument|"
9191
"call to 'size()'|call to 'data()'}0 %select{cannot be narrowed from "
9292
"type %2 to %3|evaluates to %2, which cannot be narrowed to type %3}1">,

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10000,6 +10000,7 @@ class Sema final : public SemaBase {
1000010000
CCEK_CaseValue, ///< Expression in a case label.
1000110001
CCEK_Enumerator, ///< Enumerator value with fixed underlying type.
1000210002
CCEK_TemplateArg, ///< Value of a non-type template parameter.
10003+
CCEK_InjectedTTP, ///< Injected parameter of a template template parameter.
1000310004
CCEK_ArrayBound, ///< Array bound in array declarator or new-expression.
1000410005
CCEK_ExplicitBool, ///< Condition in an explicit(bool) specifier.
1000510006
CCEK_Noexcept, ///< Condition in a noexcept(bool) specifier.
@@ -11683,6 +11684,7 @@ class Sema final : public SemaBase {
1168311684
SmallVectorImpl<TemplateArgument> &SugaredConverted,
1168411685
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
1168511686
CheckTemplateArgumentKind CTAK, bool PartialOrdering,
11687+
bool PartialOrderingTTP,
1168611688
bool *MatchedPackOnParmToNonPackOnArg);
1168711689

1168811690
/// Check that the given template arguments can be provided to
@@ -11756,6 +11758,7 @@ class Sema final : public SemaBase {
1175611758
QualType InstantiatedParamType, Expr *Arg,
1175711759
TemplateArgument &SugaredConverted,
1175811760
TemplateArgument &CanonicalConverted,
11761+
bool PartialOrderingTTP,
1175911762
CheckTemplateArgumentKind CTAK);
1176011763

1176111764
/// Check a template argument against its corresponding

clang/lib/Sema/SemaLookup.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3726,7 +3726,7 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
37263726
if (CheckTemplateArgument(
37273727
Params->getParam(0), Arg, FD, R.getNameLoc(), R.getNameLoc(),
37283728
0, SugaredChecked, CanonicalChecked, CTAK_Specified,
3729-
/*PartialOrdering=*/false,
3729+
/*PartialOrdering=*/false, /*PartialOrderingTTP=*/false,
37303730
/*MatchedPackOnParmToNonPackOnArg=*/nullptr) ||
37313731
Trap.hasErrorOccurred())
37323732
IsTemplate = false;

clang/lib/Sema/SemaOverload.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6151,8 +6151,8 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From,
61516151
Sema::CCEKind CCE,
61526152
NamedDecl *Dest,
61536153
APValue &PreNarrowingValue) {
6154-
assert(S.getLangOpts().CPlusPlus11 &&
6155-
"converted constant expression outside C++11");
6154+
assert((S.getLangOpts().CPlusPlus11 || CCE == Sema::CCEK_InjectedTTP) &&
6155+
"converted constant expression outside C++11 or TTP matching");
61566156

61576157
if (checkPlaceholderForOverload(S, From))
61586158
return ExprError();
@@ -6221,8 +6221,10 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From,
62216221
// earlier, but that's not guaranteed to work when initializing an object of
62226222
// class type.
62236223
ExprResult Result;
6224+
bool IsTemplateArgument =
6225+
CCE == Sema::CCEK_TemplateArg || CCE == Sema::CCEK_InjectedTTP;
62246226
if (T->isRecordType()) {
6225-
assert(CCE == Sema::CCEK_TemplateArg &&
6227+
assert(IsTemplateArgument &&
62266228
"unexpected class type converted constant expr");
62276229
Result = S.PerformCopyInitialization(
62286230
InitializedEntity::InitializeTemplateParameter(
@@ -6239,7 +6241,7 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From,
62396241
// A full-expression is [...] a constant-expression [...]
62406242
Result = S.ActOnFinishFullExpr(Result.get(), From->getExprLoc(),
62416243
/*DiscardedValue=*/false, /*IsConstexpr=*/true,
6242-
CCE == Sema::CCEKind::CCEK_TemplateArg);
6244+
IsTemplateArgument);
62436245
if (Result.isInvalid())
62446246
return Result;
62456247

@@ -6248,9 +6250,6 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From,
62486250
QualType PreNarrowingType;
62496251
switch (SCS->getNarrowingKind(S.Context, Result.get(), PreNarrowingValue,
62506252
PreNarrowingType)) {
6251-
case NK_Dependent_Narrowing:
6252-
// Implicit conversion to a narrower type, but the expression is
6253-
// value-dependent so we can't tell whether it's actually narrowing.
62546253
case NK_Variable_Narrowing:
62556254
// Implicit conversion to a narrower type, and the value is not a constant
62566255
// expression. We'll diagnose this in a moment.
@@ -6271,6 +6270,14 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From,
62716270
<< PreNarrowingValue.getAsString(S.Context, PreNarrowingType) << T;
62726271
break;
62736272

6273+
case NK_Dependent_Narrowing:
6274+
// Implicit conversion to a narrower type, but the expression is
6275+
// value-dependent so we can't tell whether it's actually narrowing.
6276+
// For matching the parameters of a TTP, the conversion is ill-formed
6277+
// if it may narrow.
6278+
if (CCE != Sema::CCEK_InjectedTTP)
6279+
break;
6280+
[[fallthrough]];
62746281
case NK_Type_Narrowing:
62756282
// FIXME: It would be better to diagnose that the expression is not a
62766283
// constant expression.
@@ -6343,6 +6350,8 @@ Sema::EvaluateConvertedConstantExpression(Expr *E, QualType T, APValue &Value,
63436350
Expr::EvalResult Eval;
63446351
Eval.Diag = &Notes;
63456352

6353+
assert(CCE != Sema::CCEK_InjectedTTP && "unnexpected CCE Kind");
6354+
63466355
ConstantExprKind Kind;
63476356
if (CCE == Sema::CCEK_TemplateArg && T->isRecordType())
63486357
Kind = ConstantExprKind::ClassTemplateArgument;

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5205,7 +5205,7 @@ bool Sema::CheckTemplateArgument(
52055205
SmallVectorImpl<TemplateArgument> &SugaredConverted,
52065206
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
52075207
CheckTemplateArgumentKind CTAK, bool PartialOrdering,
5208-
bool *MatchedPackOnParmToNonPackOnArg) {
5208+
bool PartialOrderingTTP, bool *MatchedPackOnParmToNonPackOnArg) {
52095209
// Check template type parameters.
52105210
if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(Param))
52115211
return CheckTemplateTypeArgument(TTP, Arg, SugaredConverted,
@@ -5260,8 +5260,9 @@ bool Sema::CheckTemplateArgument(
52605260
Expr *E = Arg.getArgument().getAsExpr();
52615261
TemplateArgument SugaredResult, CanonicalResult;
52625262
unsigned CurSFINAEErrors = NumSFINAEErrors;
5263-
ExprResult Res = CheckTemplateArgument(NTTP, NTTPType, E, SugaredResult,
5264-
CanonicalResult, CTAK);
5263+
ExprResult Res =
5264+
CheckTemplateArgument(NTTP, NTTPType, E, SugaredResult,
5265+
CanonicalResult, PartialOrderingTTP, CTAK);
52655266
if (Res.isInvalid())
52665267
return true;
52675268
// If the current template argument causes an error, give up now.
@@ -5326,7 +5327,8 @@ bool Sema::CheckTemplateArgument(
53265327

53275328
TemplateArgument SugaredResult, CanonicalResult;
53285329
E = CheckTemplateArgument(NTTP, NTTPType, E.get(), SugaredResult,
5329-
CanonicalResult, CTAK_Specified);
5330+
CanonicalResult, /*PartialOrderingTTP=*/false,
5331+
CTAK_Specified);
53305332
if (E.isInvalid())
53315333
return true;
53325334

@@ -5585,11 +5587,11 @@ bool Sema::CheckTemplateArgumentList(
55855587
getExpandedPackSize(*Param))
55865588
Arg = Arg.getPackExpansionPattern();
55875589
TemplateArgumentLoc NewArgLoc(Arg, ArgLoc.getLocInfo());
5588-
if (CheckTemplateArgument(*Param, NewArgLoc, Template, TemplateLoc,
5589-
RAngleLoc, SugaredArgumentPack.size(),
5590-
SugaredConverted, CanonicalConverted,
5591-
CTAK_Specified, /*PartialOrdering=*/false,
5592-
MatchedPackOnParmToNonPackOnArg))
5590+
if (CheckTemplateArgument(
5591+
*Param, NewArgLoc, Template, TemplateLoc, RAngleLoc,
5592+
SugaredArgumentPack.size(), SugaredConverted,
5593+
CanonicalConverted, CTAK_Specified, /*PartialOrdering=*/false,
5594+
/*PartialOrderingTTP=*/true, MatchedPackOnParmToNonPackOnArg))
55935595
return true;
55945596
Arg = NewArgLoc.getArgument();
55955597
CanonicalConverted.back().setIsDefaulted(
@@ -5601,11 +5603,11 @@ bool Sema::CheckTemplateArgumentList(
56015603
TemplateArgumentLoc(TemplateArgument::CreatePackCopy(Context, Args),
56025604
ArgLoc.getLocInfo());
56035605
} else {
5604-
if (CheckTemplateArgument(*Param, ArgLoc, Template, TemplateLoc,
5605-
RAngleLoc, SugaredArgumentPack.size(),
5606-
SugaredConverted, CanonicalConverted,
5607-
CTAK_Specified, /*PartialOrdering=*/false,
5608-
MatchedPackOnParmToNonPackOnArg))
5606+
if (CheckTemplateArgument(
5607+
*Param, ArgLoc, Template, TemplateLoc, RAngleLoc,
5608+
SugaredArgumentPack.size(), SugaredConverted,
5609+
CanonicalConverted, CTAK_Specified, /*PartialOrdering=*/false,
5610+
PartialOrderingTTP, MatchedPackOnParmToNonPackOnArg))
56095611
return true;
56105612
CanonicalConverted.back().setIsDefaulted(
56115613
clang::isSubstitutedDefaultArgument(Context, ArgLoc.getArgument(),
@@ -5753,6 +5755,7 @@ bool Sema::CheckTemplateArgumentList(
57535755
if (CheckTemplateArgument(*Param, Arg, Template, TemplateLoc, RAngleLoc, 0,
57545756
SugaredConverted, CanonicalConverted,
57555757
CTAK_Specified, /*PartialOrdering=*/false,
5758+
/*PartialOrderingTTP=*/false,
57565759
/*MatchedPackOnParmToNonPackOnArg=*/nullptr))
57575760
return true;
57585761

@@ -6740,6 +6743,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
67406743
QualType ParamType, Expr *Arg,
67416744
TemplateArgument &SugaredConverted,
67426745
TemplateArgument &CanonicalConverted,
6746+
bool PartialOrderingTTP,
67436747
CheckTemplateArgumentKind CTAK) {
67446748
SourceLocation StartLoc = Arg->getBeginLoc();
67456749

@@ -6930,17 +6934,21 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
69306934
IsConvertedConstantExpression = false;
69316935
}
69326936

6933-
if (getLangOpts().CPlusPlus17) {
6937+
if (getLangOpts().CPlusPlus17 || PartialOrderingTTP) {
69346938
// C++17 [temp.arg.nontype]p1:
69356939
// A template-argument for a non-type template parameter shall be
69366940
// a converted constant expression of the type of the template-parameter.
69376941
APValue Value;
69386942
ExprResult ArgResult;
69396943
if (IsConvertedConstantExpression) {
6940-
ArgResult = BuildConvertedConstantExpression(Arg, ParamType,
6941-
CCEK_TemplateArg, Param);
6942-
if (ArgResult.isInvalid())
6944+
ArgResult = BuildConvertedConstantExpression(
6945+
Arg, ParamType,
6946+
PartialOrderingTTP ? CCEK_InjectedTTP : CCEK_TemplateArg, Param);
6947+
assert(!ArgResult.isUnset());
6948+
if (ArgResult.isInvalid()) {
6949+
NoteTemplateParameterLocation(*Param);
69436950
return ExprError();
6951+
}
69446952
} else {
69456953
ArgResult = Arg;
69466954
}

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2979,7 +2979,8 @@ static bool ConvertDeducedTemplateArgument(
29792979
? (Arg.wasDeducedFromArrayBound() ? Sema::CTAK_DeducedFromArrayBound
29802980
: Sema::CTAK_Deduced)
29812981
: Sema::CTAK_Specified,
2982-
PartialOrdering, &MatchedPackOnParmToNonPackOnArg);
2982+
PartialOrdering, /*PartialOrderingTTP=*/false,
2983+
&MatchedPackOnParmToNonPackOnArg);
29832984
if (MatchedPackOnParmToNonPackOnArg)
29842985
Info.setMatchedPackOnParmToNonPackOnArg();
29852986
return Res;
@@ -3179,6 +3180,7 @@ static TemplateDeductionResult ConvertDeducedTemplateArguments(
31793180
Param, DefArg, TD, TD->getLocation(), TD->getSourceRange().getEnd(),
31803181
/*ArgumentPackIndex=*/0, SugaredBuilder, CanonicalBuilder,
31813182
Sema::CTAK_Specified, /*PartialOrdering=*/false,
3183+
/*PartialOrderingTTP=*/false,
31823184
/*MatchedPackOnParmToNonPackOnArg=*/nullptr)) {
31833185
Info.Param = makeTemplateParameter(
31843186
const_cast<NamedDecl *>(TemplateParams->getParam(I)));

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2373,9 +2373,10 @@ TemplateInstantiator::TransformSubstNonTypeTemplateParmExpr(
23732373
// The call to CheckTemplateArgument here produces the ImpCast.
23742374
TemplateArgument SugaredConverted, CanonicalConverted;
23752375
if (SemaRef
2376-
.CheckTemplateArgument(E->getParameter(), SubstType,
2377-
SubstReplacement.get(), SugaredConverted,
2378-
CanonicalConverted, Sema::CTAK_Specified)
2376+
.CheckTemplateArgument(
2377+
E->getParameter(), SubstType, SubstReplacement.get(),
2378+
SugaredConverted, CanonicalConverted,
2379+
/*PartialOrderingTTP=*/false, Sema::CTAK_Specified)
23792380
.isInvalid())
23802381
return true;
23812382
return transformNonTypeTemplateParmRef(E->getAssociatedDecl(),

clang/test/CXX/drs/cwg0xx.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,12 +521,12 @@ namespace example1 {
521521
namespace A {
522522
int i;
523523
}
524-
524+
525525
namespace A1 {
526526
using A::i;
527527
using A::i;
528528
}
529-
529+
530530
void f()
531531
{
532532
using A::i;
@@ -1371,7 +1371,7 @@ namespace cwg92 { // cwg92: 4 c++17
13711371
// considered in this context. In C++17, we *do* perform an implicit
13721372
// conversion (which performs initialization), and the exception specification
13731373
// is part of the type of the parameter, so this is invalid.
1374-
template<void() throw()> struct X {};
1374+
template<void() throw()> struct X {}; // since-cxx17-note {{template parameter is declared here}}
13751375
X<&f> xp;
13761376
// since-cxx17-error@-1 {{value of type 'void (*)() throw(int, float)' is not implicitly convertible to 'void (*)() throw()'}}
13771377

clang/test/CXX/drs/cwg12xx.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ namespace cwg1295 { // cwg1295: 4
155155
// cxx98-14-error@-1 {{non-type template argument does not refer to any declaration}}
156156
// cxx98-14-note@#cwg1295-Y {{template parameter is declared here}}
157157
// since-cxx17-error@#cwg1295-y {{reference cannot bind to bit-field in converted constant expression}}
158+
// since-cxx17-note@#cwg1295-Y {{template parameter is declared here}}
159+
158160

159161
#if __cplusplus >= 201103L
160162
const unsigned other = 0;

0 commit comments

Comments
 (0)