Skip to content

Commit a1a6a83

Browse files
authored
[clang] fix matching of nested template template parameters (#130447)
When checking the template template parameters of template template parameters, the PartialOrdering context was not correctly propagated. This also has a few drive-by fixes, such as checking the template parameter lists of template template parameters, which was previously missing and would have been it's own bug, but we need to fix it in order to prevent crashes in error recovery in a simple way. Fixes #130362
1 parent 507e0c3 commit a1a6a83

File tree

10 files changed

+61
-44
lines changed

10 files changed

+61
-44
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,9 @@ Bug Fixes to C++ Support
292292
direct-list-initialized from an array is corrected to direct-initialization.
293293
- Clang no longer crashes when a coroutine is declared ``[[noreturn]]``. (#GH127327)
294294
- Clang now uses the parameter location for abbreviated function templates in ``extern "C"``. (#GH46386)
295+
- Fixes matching of nested template template parameters. (#GH130362)
296+
- Correctly diagnoses template template paramters which have a pack parameter
297+
not in the last position.
295298
- Clang now correctly parses ``if constexpr`` expressions in immediate function context. (#GH123524)
296299

297300
Bug Fixes to AST Handling

clang/include/clang/Sema/Sema.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11356,14 +11356,16 @@ class Sema final : public SemaBase {
1135611356

1135711357
/// The context in which we are checking a template parameter list.
1135811358
enum TemplateParamListContext {
11359-
TPC_ClassTemplate,
11360-
TPC_VarTemplate,
11359+
// For this context, Class, Variable, TypeAlias, and non-pack Template
11360+
// Template Parameters are treated uniformly.
11361+
TPC_Other,
11362+
1136111363
TPC_FunctionTemplate,
1136211364
TPC_ClassTemplateMember,
1136311365
TPC_FriendClassTemplate,
1136411366
TPC_FriendFunctionTemplate,
1136511367
TPC_FriendFunctionTemplateDefinition,
11366-
TPC_TypeAliasTemplate
11368+
TPC_TemplateTemplateParameterPack,
1136711369
};
1136811370

1136911371
/// Checks the validity of a template parameter list, possibly

clang/lib/Sema/SemaDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8154,7 +8154,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
81548154
(D.getCXXScopeSpec().isSet() && DC && DC->isRecord() &&
81558155
DC->isDependentContext())
81568156
? TPC_ClassTemplateMember
8157-
: TPC_VarTemplate))
8157+
: TPC_Other))
81588158
NewVD->setInvalidDecl();
81598159

81608160
// If we are providing an explicit specialization of a static variable

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13586,7 +13586,7 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, AccessSpecifier AS,
1358613586
// Merge any previous default template arguments into our parameters,
1358713587
// and check the parameter list.
1358813588
if (CheckTemplateParameterList(TemplateParams, OldTemplateParams,
13589-
TPC_TypeAliasTemplate))
13589+
TPC_Other))
1359013590
return nullptr;
1359113591

1359213592
TypeAliasTemplateDecl *NewDecl =

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,8 +1591,16 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(
15911591
assert(S->isTemplateParamScope() &&
15921592
"Template template parameter not in template parameter scope!");
15931593

1594-
// Construct the parameter object.
15951594
bool IsParameterPack = EllipsisLoc.isValid();
1595+
1596+
bool Invalid = false;
1597+
if (CheckTemplateParameterList(
1598+
Params,
1599+
/*OldParams=*/nullptr,
1600+
IsParameterPack ? TPC_TemplateTemplateParameterPack : TPC_Other))
1601+
Invalid = true;
1602+
1603+
// Construct the parameter object.
15961604
TemplateTemplateParmDecl *Param = TemplateTemplateParmDecl::Create(
15971605
Context, Context.getTranslationUnitDecl(),
15981606
NameLoc.isInvalid() ? TmpLoc : NameLoc, Depth, Position, IsParameterPack,
@@ -1615,9 +1623,12 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(
16151623
if (Params->size() == 0) {
16161624
Diag(Param->getLocation(), diag::err_template_template_parm_no_parms)
16171625
<< SourceRange(Params->getLAngleLoc(), Params->getRAngleLoc());
1618-
Param->setInvalidDecl();
1626+
Invalid = true;
16191627
}
16201628

1629+
if (Invalid)
1630+
Param->setInvalidDecl();
1631+
16211632
// C++0x [temp.param]p9:
16221633
// A default template-argument may be specified for any kind of
16231634
// template-parameter that is not a template parameter pack.
@@ -2066,7 +2077,7 @@ DeclResult Sema::CheckClassTemplate(
20662077
SemanticContext->isDependentContext())
20672078
? TPC_ClassTemplateMember
20682079
: TUK == TagUseKind::Friend ? TPC_FriendClassTemplate
2069-
: TPC_ClassTemplate,
2080+
: TPC_Other,
20702081
SkipBody))
20712082
Invalid = true;
20722083

@@ -2208,9 +2219,8 @@ static bool DiagnoseDefaultTemplateArgument(Sema &S,
22082219
SourceLocation ParamLoc,
22092220
SourceRange DefArgRange) {
22102221
switch (TPC) {
2211-
case Sema::TPC_ClassTemplate:
2212-
case Sema::TPC_VarTemplate:
2213-
case Sema::TPC_TypeAliasTemplate:
2222+
case Sema::TPC_Other:
2223+
case Sema::TPC_TemplateTemplateParameterPack:
22142224
return false;
22152225

22162226
case Sema::TPC_FunctionTemplate:
@@ -2383,8 +2393,11 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams,
23832393
MissingDefaultArg = true;
23842394
} else if (NonTypeTemplateParmDecl *NewNonTypeParm
23852395
= dyn_cast<NonTypeTemplateParmDecl>(*NewParam)) {
2386-
// Check for unexpanded parameter packs.
2387-
if (!NewNonTypeParm->isParameterPack() &&
2396+
// Check for unexpanded parameter packs, except in a template template
2397+
// parameter pack, as in those any unexpanded packs should be expanded
2398+
// along with the parameter itself.
2399+
if (TPC != TPC_TemplateTemplateParameterPack &&
2400+
!NewNonTypeParm->isParameterPack() &&
23882401
DiagnoseUnexpandedParameterPack(NewNonTypeParm->getLocation(),
23892402
NewNonTypeParm->getTypeSourceInfo(),
23902403
UPPC_NonTypeTemplateParameterType)) {
@@ -2492,8 +2505,7 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams,
24922505
// If a template parameter of a primary class template or alias template
24932506
// is a template parameter pack, it shall be the last template parameter.
24942507
if (SawParameterPack && (NewParam + 1) != NewParamEnd &&
2495-
(TPC == TPC_ClassTemplate || TPC == TPC_VarTemplate ||
2496-
TPC == TPC_TypeAliasTemplate)) {
2508+
(TPC == TPC_Other || TPC == TPC_TemplateTemplateParameterPack)) {
24972509
Diag((*NewParam)->getLocation(),
24982510
diag::err_template_param_pack_must_be_last_template_parameter);
24992511
Invalid = true;
@@ -2526,8 +2538,8 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams,
25262538
<< PrevModuleName;
25272539
Invalid = true;
25282540
} else if (MissingDefaultArg &&
2529-
(TPC == TPC_ClassTemplate || TPC == TPC_FriendClassTemplate ||
2530-
TPC == TPC_VarTemplate || TPC == TPC_TypeAliasTemplate)) {
2541+
(TPC == TPC_Other || TPC == TPC_TemplateTemplateParameterPack ||
2542+
TPC == TPC_FriendClassTemplate)) {
25312543
// C++ 23[temp.param]p14:
25322544
// If a template-parameter of a class template, variable template, or
25332545
// alias template has a default template argument, each subsequent

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3427,9 +3427,9 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction(
34273427
if (!P.isPackExpansion() && !A.isPackExpansion()) {
34283428
Info.Param =
34293429
makeTemplateParameter(Template->getTemplateParameters()->getParam(
3430-
(PsStack.empty() ? TemplateArgs.end()
3431-
: PsStack.front().begin()) -
3432-
TemplateArgs.begin()));
3430+
(AsStack.empty() ? CTAI.CanonicalConverted.end()
3431+
: AsStack.front().begin()) -
3432+
1 - CTAI.CanonicalConverted.begin()));
34333433
Info.FirstArg = P;
34343434
Info.SecondArg = A;
34353435
return TemplateDeductionResult::NonDeducedMismatch;
@@ -6642,17 +6642,19 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
66426642

66436643
TemplateDeductionResult TDK;
66446644
runWithSufficientStackSpace(Info.getLocation(), [&] {
6645-
TDK = ::FinishTemplateArgumentDeduction(
6646-
*this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info);
6645+
TDK = ::FinishTemplateArgumentDeduction(*this, AArg, PartialOrdering, PArgs,
6646+
Deduced, Info);
66476647
});
66486648
switch (TDK) {
66496649
case TemplateDeductionResult::Success:
66506650
return true;
66516651

66526652
// It doesn't seem possible to get a non-deduced mismatch when partial
6653-
// ordering TTPs.
6653+
// ordering TTPs, except with an invalid template parameter list which has
6654+
// a parameter after a pack.
66546655
case TemplateDeductionResult::NonDeducedMismatch:
6655-
llvm_unreachable("Unexpected NonDeducedMismatch");
6656+
assert(PArg->isInvalidDecl() && "Unexpected NonDeducedMismatch");
6657+
return false;
66566658

66576659
// Substitution failures should have already been diagnosed.
66586660
case TemplateDeductionResult::AlreadyDiagnosed:

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2176,7 +2176,7 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
21762176
// Do some additional validation, then merge default arguments
21772177
// from the existing declarations.
21782178
if (SemaRef.CheckTemplateParameterList(InstParams, PrevParams,
2179-
Sema::TPC_ClassTemplate))
2179+
Sema::TPC_Other))
21802180
return nullptr;
21812181

21822182
Inst->setAccess(PrevClassTemplate->getAccess());

clang/test/SemaTemplate/cwg2398.cpp

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,11 @@ namespace regression3 {
602602
template <class...> class B {};
603603
template struct A<B, Node<None>>;
604604
} // namespace regression3
605+
namespace GH130362 {
606+
template <template <template <class... T1> class TT1> class TT2> struct A {};
607+
template <template <class U1> class UU1> struct B {};
608+
template struct A<B>;
609+
} // namespace GH130362
605610

606611
namespace nttp_auto {
607612
namespace t1 {
@@ -610,24 +615,16 @@ namespace nttp_auto {
610615
template struct A<B>;
611616
} // namespace t1
612617
namespace t2 {
613-
// FIXME: Shouldn't accept parameters after a parameter pack.
614618
template<template<auto... Va1, auto Va2> class> struct A {};
615-
// expected-error@-1 {{deduced non-type template argument does not have the same type as the corresponding template parameter ('auto' vs 'int')}}
616-
// expected-note@-2 {{previous template template parameter is here}}
619+
// expected-error@-1 {{template parameter pack must be the last template parameter}}
617620
template<int... Vi> struct B;
618-
// expected-note@-1 {{template parameter is declared here}}
619621
template struct A<B>;
620-
// expected-note@-1 {{different template parameters}}
621622
} // namespace t2
622623
namespace t3 {
623-
// FIXME: Shouldn't accept parameters after a parameter pack.
624624
template<template<auto... Va1, auto... Va2> class> struct A {};
625-
// expected-error@-1 {{deduced non-type template argument does not have the same type as the corresponding template parameter ('auto' vs 'int')}}
626-
// expected-note@-2 {{previous template template parameter is here}}
625+
// expected-error@-1 {{template parameter pack must be the last template parameter}}
627626
template<int... Vi> struct B;
628-
// expected-note@-1 {{template parameter is declared here}}
629627
template struct A<B>;
630-
// expected-note@-1 {{different template parameters}}
631628
} // namespace t3
632629
} // namespace nttp_auto
633630

clang/test/SemaTemplate/temp_arg_template_p0522.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
template<template<int> typename> struct Ti; // #Ti
88
template<template<int...> typename> struct TPi; // #TPi
99
template<template<int, int...> typename> struct TiPi;
10-
template<template<int..., int...> typename> struct TPiPi; // FIXME: Why is this not ill-formed?
10+
template<template<int..., int...> typename> struct TPiPi;
11+
// expected-error@-1 {{template parameter pack must be the last template parameter}}
1112

1213
template<typename T, template<T> typename> struct tT0; // #tT0
1314
template<template<typename T, T> typename> struct Tt0; // #Tt0

clang/unittests/AST/DeclPrinterTest.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,21 +1196,21 @@ TEST(DeclPrinter, TestUnnamedTemplateParameters) {
11961196
}
11971197

11981198
TEST(DeclPrinter, TestUnnamedTemplateParametersPacks) {
1199-
ASSERT_TRUE(PrintedDeclCXX17Matches(
1200-
"template <typename ..., int ...,"
1201-
" template <typename ..., bool ...> class ...> void A();",
1202-
functionTemplateDecl(hasName("A")).bind("id"),
1203-
"template <typename ..., int ...,"
1204-
" template <typename ..., bool ...> class ...> void A()"));
1199+
ASSERT_TRUE(
1200+
PrintedDeclCXX17Matches("template <typename ..., int ...,"
1201+
" template <typename ...> class ...> void A();",
1202+
functionTemplateDecl(hasName("A")).bind("id"),
1203+
"template <typename ..., int ...,"
1204+
" template <typename ...> class ...> void A()"));
12051205
}
12061206

12071207
TEST(DeclPrinter, TestNamedTemplateParametersPacks) {
12081208
ASSERT_TRUE(PrintedDeclCXX17Matches(
12091209
"template <typename ...T, int ...I,"
1210-
" template <typename ...X, bool ...B> class ...Z> void A();",
1210+
" template <typename ...X> class ...Z> void A();",
12111211
functionTemplateDecl(hasName("A")).bind("id"),
12121212
"template <typename ...T, int ...I,"
1213-
" template <typename ...X, bool ...B> class ...Z> void A()"));
1213+
" template <typename ...X> class ...Z> void A()"));
12141214
}
12151215

12161216
TEST(DeclPrinter, TestTemplateTemplateParameterWrittenWithTypename) {

0 commit comments

Comments
 (0)