Skip to content

Commit 8210ac8

Browse files
committed
[clang] disallow narrowing when matching template template parameters
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 is 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.
1 parent 28ad897 commit 8210ac8

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
@@ -329,6 +329,9 @@ C++17 Feature Support
329329
^^^^^^^^^^^^^^^^^^^^^
330330
- The implementation of the relaxed template template argument matching rules is
331331
more complete and reliable, and should provide more accurate diagnostics.
332+
This implements:
333+
- `P3310R5: Solving issues introduced by relaxed template template parameter matching <https://wg21.link/p3310r5>`_.
334+
- `P3579R0: Fix matching of non-type template parameters when matching template template parameters <https://wg21.link/p3579r0>`_.
332335

333336
Resolutions to C++ Defect Reports
334337
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

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
@@ -9999,6 +9999,7 @@ class Sema final : public SemaBase {
99999999
CCEK_CaseValue, ///< Expression in a case label.
1000010000
CCEK_Enumerator, ///< Enumerator value with fixed underlying type.
1000110001
CCEK_TemplateArg, ///< Value of a non-type template parameter.
10002+
CCEK_InjectedTTP, ///< Injected parameter of a template template parameter.
1000210003
CCEK_ArrayBound, ///< Array bound in array declarator or new-expression.
1000310004
CCEK_ExplicitBool, ///< Condition in an explicit(bool) specifier.
1000410005
CCEK_Noexcept, ///< Condition in a noexcept(bool) specifier.
@@ -11682,6 +11683,7 @@ class Sema final : public SemaBase {
1168211683
SmallVectorImpl<TemplateArgument> &SugaredConverted,
1168311684
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
1168411685
CheckTemplateArgumentKind CTAK, bool PartialOrdering,
11686+
bool PartialOrderingTTP,
1168511687
bool *MatchedPackOnParmToNonPackOnArg);
1168611688

1168711689
/// Check that the given template arguments can be provided to
@@ -11755,6 +11757,7 @@ class Sema final : public SemaBase {
1175511757
QualType InstantiatedParamType, Expr *Arg,
1175611758
TemplateArgument &SugaredConverted,
1175711759
TemplateArgument &CanonicalConverted,
11760+
bool PartialOrderingTTP,
1175811761
CheckTemplateArgumentKind CTAK);
1175911762

1176011763
/// 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
@@ -3676,7 +3676,7 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
36763676
if (CheckTemplateArgument(
36773677
Params->getParam(0), Arg, FD, R.getNameLoc(), R.getNameLoc(),
36783678
0, SugaredChecked, CanonicalChecked, CTAK_Specified,
3679-
/*PartialOrdering=*/false,
3679+
/*PartialOrdering=*/false, /*PartialOrderingTTP=*/false,
36803680
/*MatchedPackOnParmToNonPackOnArg=*/nullptr) ||
36813681
Trap.hasErrorOccurred())
36823682
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
@@ -2399,9 +2399,10 @@ TemplateInstantiator::TransformSubstNonTypeTemplateParmExpr(
23992399
// The call to CheckTemplateArgument here produces the ImpCast.
24002400
TemplateArgument SugaredConverted, CanonicalConverted;
24012401
if (SemaRef
2402-
.CheckTemplateArgument(E->getParameter(), SubstType,
2403-
SubstReplacement.get(), SugaredConverted,
2404-
CanonicalConverted, Sema::CTAK_Specified)
2402+
.CheckTemplateArgument(
2403+
E->getParameter(), SubstType, SubstReplacement.get(),
2404+
SugaredConverted, CanonicalConverted,
2405+
/*PartialOrderingTTP=*/false, Sema::CTAK_Specified)
24052406
.isInvalid())
24062407
return true;
24072408
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)