Skip to content

Commit ecde8c2

Browse files
mizvekovtstellar
authored andcommitted
[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 Backport of: #130447
1 parent c86df91 commit ecde8c2

File tree

10 files changed

+64
-46
lines changed

10 files changed

+64
-46
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,9 @@ Bug Fixes to C++ Support
10581058
- Fixed a substitution bug in transforming CTAD aliases when the type alias contains a non-pack template argument
10591059
corresponding to a pack parameter (#GH124715)
10601060
- Clang is now better at keeping track of friend function template instance contexts. (#GH55509)
1061+
- Fixes matching of nested template template parameters. (#GH130362)
1062+
- Correctly diagnoses template template paramters which have a pack parameter
1063+
not in the last position.
10611064
- Fixed an integer overflow bug in computing template parameter depths when synthesizing CTAD guides. (#GH128691)
10621065
- Fixed an incorrect pointer access when checking access-control on concepts. (#GH131530)
10631066
- Fixed various alias CTAD bugs involving variadic template arguments. (#GH123591), (#GH127539), (#GH129077),

clang/include/clang/Sema/Sema.h

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

1128011280
/// The context in which we are checking a template parameter list.
1128111281
enum TemplateParamListContext {
11282-
TPC_ClassTemplate,
11283-
TPC_VarTemplate,
11282+
// For this context, Class, Variable, TypeAlias, and non-pack Template
11283+
// Template Parameters are treated uniformly.
11284+
TPC_Other,
11285+
1128411286
TPC_FunctionTemplate,
1128511287
TPC_ClassTemplateMember,
1128611288
TPC_FriendClassTemplate,
1128711289
TPC_FriendFunctionTemplate,
1128811290
TPC_FriendFunctionTemplateDefinition,
11289-
TPC_TypeAliasTemplate
11291+
TPC_TemplateTemplateParameterPack,
1129011292
};
1129111293

1129211294
/// 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
@@ -8145,7 +8145,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
81458145
(D.getCXXScopeSpec().isSet() && DC && DC->isRecord() &&
81468146
DC->isDependentContext())
81478147
? TPC_ClassTemplateMember
8148-
: TPC_VarTemplate))
8148+
: TPC_Other))
81498149
NewVD->setInvalidDecl();
81508150

81518151
// 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
@@ -13533,7 +13533,7 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, AccessSpecifier AS,
1353313533
// Merge any previous default template arguments into our parameters,
1353413534
// and check the parameter list.
1353513535
if (CheckTemplateParameterList(TemplateParams, OldTemplateParams,
13536-
TPC_TypeAliasTemplate))
13536+
TPC_Other))
1353713537
return nullptr;
1353813538

1353913539
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;
@@ -6625,17 +6625,19 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
66256625

66266626
TemplateDeductionResult TDK;
66276627
runWithSufficientStackSpace(Info.getLocation(), [&] {
6628-
TDK = ::FinishTemplateArgumentDeduction(
6629-
*this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info);
6628+
TDK = ::FinishTemplateArgumentDeduction(*this, AArg, PartialOrdering, PArgs,
6629+
Deduced, Info);
66306630
});
66316631
switch (TDK) {
66326632
case TemplateDeductionResult::Success:
66336633
return true;
66346634

66356635
// It doesn't seem possible to get a non-deduced mismatch when partial
6636-
// ordering TTPs.
6636+
// ordering TTPs, except with an invalid template parameter list which has
6637+
// a parameter after a pack.
66376638
case TemplateDeductionResult::NonDeducedMismatch:
6638-
llvm_unreachable("Unexpected NonDeducedMismatch");
6639+
assert(PArg->isInvalidDecl() && "Unexpected NonDeducedMismatch");
6640+
return false;
66396641

66406642
// Substitution failures should have already been diagnosed.
66416643
case TemplateDeductionResult::AlreadyDiagnosed:

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1827,7 +1827,7 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
18271827
// Do some additional validation, then merge default arguments
18281828
// from the existing declarations.
18291829
if (SemaRef.CheckTemplateParameterList(InstParams, PrevParams,
1830-
Sema::TPC_ClassTemplate))
1830+
Sema::TPC_Other))
18311831
return nullptr;
18321832

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

clang/test/SemaTemplate/cwg2398.cpp

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,11 @@ namespace regression3 {
650650
template struct A<B, Node<None>>;
651651
// old-error@-1 {{different template}}
652652
} // namespace regression3
653+
namespace GH130362 {
654+
template <template <template <class... T1> class TT1> class TT2> struct A {};
655+
template <template <class U1> class UU1> struct B {};
656+
template struct A<B>;
657+
} // namespace GH130362
653658

654659
namespace nttp_auto {
655660
namespace t1 {
@@ -658,26 +663,19 @@ namespace nttp_auto {
658663
template struct A<B>;
659664
} // namespace t1
660665
namespace t2 {
661-
// FIXME: Shouldn't accept parameters after a parameter pack.
662666
template<template<auto... Va1, auto Va2> class> struct A {};
663-
// new-error@-1 {{deduced non-type template argument does not have the same type as the corresponding template parameter ('auto' vs 'int')}}
664-
// expected-note@-2 {{previous template template parameter is here}}
667+
// expected-error@-1 {{template parameter pack must be the last template parameter}}
668+
// old-note@-2 {{previous template template parameter is here}}
665669
template<int... Vi> struct B;
666-
// new-note@-1 {{template parameter is declared here}}
667-
// old-note@-2 {{too few template parameters}}
670+
// old-note@-1 {{too few template parameters}}
668671
template struct A<B>;
669-
// new-note@-1 {{different template parameters}}
670-
// old-error@-2 {{different template parameters}}
672+
// old-error@-1 {{different template parameters}}
671673
} // namespace t2
672674
namespace t3 {
673-
// FIXME: Shouldn't accept parameters after a parameter pack.
674675
template<template<auto... Va1, auto... Va2> class> struct A {};
675-
// new-error@-1 {{deduced non-type template argument does not have the same type as the corresponding template parameter ('auto' vs 'int')}}
676-
// new-note@-2 {{previous template template parameter is here}}
676+
// expected-error@-1 {{template parameter pack must be the last template parameter}}
677677
template<int... Vi> struct B;
678-
// new-note@-1 {{template parameter is declared here}}
679678
template struct A<B>;
680-
// new-note@-1 {{different template parameters}}
681679
} // namespace t3
682680
} // namespace nttp_auto
683681

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)