Skip to content

Commit bcce55d

Browse files
committed
[clang] fix matching of nested template template parameters
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 52225d2 commit bcce55d

File tree

10 files changed

+63
-46
lines changed

10 files changed

+63
-46
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,8 @@ related warnings within the method body.
161161
``__attribute__((model("large")))`` on non-TLS globals in x86-64 compilations.
162162
This forces the global to be considered small or large in regards to the
163163
x86-64 code model, regardless of the code model specified for the compilation.
164-
- Clang now emits a warning ``-Wreserved-init-priority`` instead of a hard error
165-
when ``__attribute__((init_priority(n)))`` is used with values of n in the
164+
- Clang now emits a warning ``-Wreserved-init-priority`` instead of a hard error
165+
when ``__attribute__((init_priority(n)))`` is used with values of n in the
166166
reserved range [0, 100]. The warning will be treated as an error by default.
167167

168168
- There is a new ``format_matches`` attribute to complement the existing
@@ -294,6 +294,9 @@ Bug Fixes to C++ Support
294294
direct-list-initialized from an array is corrected to direct-initialization.
295295
- Clang no longer crashes when a coroutine is declared ``[[noreturn]]``. (#GH127327)
296296
- Clang now uses the parameter location for abbreviated function templates in ``extern "C"``. (#GH46386)
297+
- Fixes matching of nested template template parameters. (#GH130362)
298+
- Correctly diagnoses template template paramters which have a pack parameter
299+
not in the last position.
297300
- Clang now correctly parses ``if constexpr`` expressions in immediate function context. (#GH123524)
298301

299302
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
@@ -11363,14 +11363,16 @@ class Sema final : public SemaBase {
1136311363

1136411364
/// The context in which we are checking a template parameter list.
1136511365
enum TemplateParamListContext {
11366-
TPC_ClassTemplate,
11367-
TPC_VarTemplate,
11366+
// For this context, Class, Variable, TypeAlias, and non-pack Template
11367+
// Template Parameters are treated uniformly.
11368+
TPC_Other,
11369+
1136811370
TPC_FunctionTemplate,
1136911371
TPC_ClassTemplateMember,
1137011372
TPC_FriendClassTemplate,
1137111373
TPC_FriendFunctionTemplate,
1137211374
TPC_FriendFunctionTemplateDefinition,
11373-
TPC_TypeAliasTemplate
11375+
TPC_TemplateTemplateParameterPack,
1137411376
};
1137511377

1137611378
/// 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
@@ -8165,7 +8165,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
81658165
(D.getCXXScopeSpec().isSet() && DC && DC->isRecord() &&
81668166
DC->isDependentContext())
81678167
? TPC_ClassTemplateMember
8168-
: TPC_VarTemplate))
8168+
: TPC_Other))
81698169
NewVD->setInvalidDecl();
81708170

81718171
// 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)