[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
This commit is contained in:
parent
507e0c3b67
commit
a1a6a83976
@ -292,6 +292,9 @@ Bug Fixes to C++ Support
|
|||||||
direct-list-initialized from an array is corrected to direct-initialization.
|
direct-list-initialized from an array is corrected to direct-initialization.
|
||||||
- Clang no longer crashes when a coroutine is declared ``[[noreturn]]``. (#GH127327)
|
- Clang no longer crashes when a coroutine is declared ``[[noreturn]]``. (#GH127327)
|
||||||
- Clang now uses the parameter location for abbreviated function templates in ``extern "C"``. (#GH46386)
|
- Clang now uses the parameter location for abbreviated function templates in ``extern "C"``. (#GH46386)
|
||||||
|
- Fixes matching of nested template template parameters. (#GH130362)
|
||||||
|
- Correctly diagnoses template template paramters which have a pack parameter
|
||||||
|
not in the last position.
|
||||||
- Clang now correctly parses ``if constexpr`` expressions in immediate function context. (#GH123524)
|
- Clang now correctly parses ``if constexpr`` expressions in immediate function context. (#GH123524)
|
||||||
|
|
||||||
Bug Fixes to AST Handling
|
Bug Fixes to AST Handling
|
||||||
|
@ -11356,14 +11356,16 @@ public:
|
|||||||
|
|
||||||
/// The context in which we are checking a template parameter list.
|
/// The context in which we are checking a template parameter list.
|
||||||
enum TemplateParamListContext {
|
enum TemplateParamListContext {
|
||||||
TPC_ClassTemplate,
|
// For this context, Class, Variable, TypeAlias, and non-pack Template
|
||||||
TPC_VarTemplate,
|
// Template Parameters are treated uniformly.
|
||||||
|
TPC_Other,
|
||||||
|
|
||||||
TPC_FunctionTemplate,
|
TPC_FunctionTemplate,
|
||||||
TPC_ClassTemplateMember,
|
TPC_ClassTemplateMember,
|
||||||
TPC_FriendClassTemplate,
|
TPC_FriendClassTemplate,
|
||||||
TPC_FriendFunctionTemplate,
|
TPC_FriendFunctionTemplate,
|
||||||
TPC_FriendFunctionTemplateDefinition,
|
TPC_FriendFunctionTemplateDefinition,
|
||||||
TPC_TypeAliasTemplate
|
TPC_TemplateTemplateParameterPack,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Checks the validity of a template parameter list, possibly
|
/// Checks the validity of a template parameter list, possibly
|
||||||
|
@ -8154,7 +8154,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
|
|||||||
(D.getCXXScopeSpec().isSet() && DC && DC->isRecord() &&
|
(D.getCXXScopeSpec().isSet() && DC && DC->isRecord() &&
|
||||||
DC->isDependentContext())
|
DC->isDependentContext())
|
||||||
? TPC_ClassTemplateMember
|
? TPC_ClassTemplateMember
|
||||||
: TPC_VarTemplate))
|
: TPC_Other))
|
||||||
NewVD->setInvalidDecl();
|
NewVD->setInvalidDecl();
|
||||||
|
|
||||||
// If we are providing an explicit specialization of a static variable
|
// If we are providing an explicit specialization of a static variable
|
||||||
|
@ -13586,7 +13586,7 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, AccessSpecifier AS,
|
|||||||
// Merge any previous default template arguments into our parameters,
|
// Merge any previous default template arguments into our parameters,
|
||||||
// and check the parameter list.
|
// and check the parameter list.
|
||||||
if (CheckTemplateParameterList(TemplateParams, OldTemplateParams,
|
if (CheckTemplateParameterList(TemplateParams, OldTemplateParams,
|
||||||
TPC_TypeAliasTemplate))
|
TPC_Other))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
TypeAliasTemplateDecl *NewDecl =
|
TypeAliasTemplateDecl *NewDecl =
|
||||||
|
@ -1591,8 +1591,16 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(
|
|||||||
assert(S->isTemplateParamScope() &&
|
assert(S->isTemplateParamScope() &&
|
||||||
"Template template parameter not in template parameter scope!");
|
"Template template parameter not in template parameter scope!");
|
||||||
|
|
||||||
// Construct the parameter object.
|
|
||||||
bool IsParameterPack = EllipsisLoc.isValid();
|
bool IsParameterPack = EllipsisLoc.isValid();
|
||||||
|
|
||||||
|
bool Invalid = false;
|
||||||
|
if (CheckTemplateParameterList(
|
||||||
|
Params,
|
||||||
|
/*OldParams=*/nullptr,
|
||||||
|
IsParameterPack ? TPC_TemplateTemplateParameterPack : TPC_Other))
|
||||||
|
Invalid = true;
|
||||||
|
|
||||||
|
// Construct the parameter object.
|
||||||
TemplateTemplateParmDecl *Param = TemplateTemplateParmDecl::Create(
|
TemplateTemplateParmDecl *Param = TemplateTemplateParmDecl::Create(
|
||||||
Context, Context.getTranslationUnitDecl(),
|
Context, Context.getTranslationUnitDecl(),
|
||||||
NameLoc.isInvalid() ? TmpLoc : NameLoc, Depth, Position, IsParameterPack,
|
NameLoc.isInvalid() ? TmpLoc : NameLoc, Depth, Position, IsParameterPack,
|
||||||
@ -1615,9 +1623,12 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(
|
|||||||
if (Params->size() == 0) {
|
if (Params->size() == 0) {
|
||||||
Diag(Param->getLocation(), diag::err_template_template_parm_no_parms)
|
Diag(Param->getLocation(), diag::err_template_template_parm_no_parms)
|
||||||
<< SourceRange(Params->getLAngleLoc(), Params->getRAngleLoc());
|
<< SourceRange(Params->getLAngleLoc(), Params->getRAngleLoc());
|
||||||
Param->setInvalidDecl();
|
Invalid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Invalid)
|
||||||
|
Param->setInvalidDecl();
|
||||||
|
|
||||||
// C++0x [temp.param]p9:
|
// C++0x [temp.param]p9:
|
||||||
// A default template-argument may be specified for any kind of
|
// A default template-argument may be specified for any kind of
|
||||||
// template-parameter that is not a template parameter pack.
|
// template-parameter that is not a template parameter pack.
|
||||||
@ -2066,7 +2077,7 @@ DeclResult Sema::CheckClassTemplate(
|
|||||||
SemanticContext->isDependentContext())
|
SemanticContext->isDependentContext())
|
||||||
? TPC_ClassTemplateMember
|
? TPC_ClassTemplateMember
|
||||||
: TUK == TagUseKind::Friend ? TPC_FriendClassTemplate
|
: TUK == TagUseKind::Friend ? TPC_FriendClassTemplate
|
||||||
: TPC_ClassTemplate,
|
: TPC_Other,
|
||||||
SkipBody))
|
SkipBody))
|
||||||
Invalid = true;
|
Invalid = true;
|
||||||
|
|
||||||
@ -2208,9 +2219,8 @@ static bool DiagnoseDefaultTemplateArgument(Sema &S,
|
|||||||
SourceLocation ParamLoc,
|
SourceLocation ParamLoc,
|
||||||
SourceRange DefArgRange) {
|
SourceRange DefArgRange) {
|
||||||
switch (TPC) {
|
switch (TPC) {
|
||||||
case Sema::TPC_ClassTemplate:
|
case Sema::TPC_Other:
|
||||||
case Sema::TPC_VarTemplate:
|
case Sema::TPC_TemplateTemplateParameterPack:
|
||||||
case Sema::TPC_TypeAliasTemplate:
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case Sema::TPC_FunctionTemplate:
|
case Sema::TPC_FunctionTemplate:
|
||||||
@ -2383,8 +2393,11 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams,
|
|||||||
MissingDefaultArg = true;
|
MissingDefaultArg = true;
|
||||||
} else if (NonTypeTemplateParmDecl *NewNonTypeParm
|
} else if (NonTypeTemplateParmDecl *NewNonTypeParm
|
||||||
= dyn_cast<NonTypeTemplateParmDecl>(*NewParam)) {
|
= dyn_cast<NonTypeTemplateParmDecl>(*NewParam)) {
|
||||||
// Check for unexpanded parameter packs.
|
// Check for unexpanded parameter packs, except in a template template
|
||||||
if (!NewNonTypeParm->isParameterPack() &&
|
// parameter pack, as in those any unexpanded packs should be expanded
|
||||||
|
// along with the parameter itself.
|
||||||
|
if (TPC != TPC_TemplateTemplateParameterPack &&
|
||||||
|
!NewNonTypeParm->isParameterPack() &&
|
||||||
DiagnoseUnexpandedParameterPack(NewNonTypeParm->getLocation(),
|
DiagnoseUnexpandedParameterPack(NewNonTypeParm->getLocation(),
|
||||||
NewNonTypeParm->getTypeSourceInfo(),
|
NewNonTypeParm->getTypeSourceInfo(),
|
||||||
UPPC_NonTypeTemplateParameterType)) {
|
UPPC_NonTypeTemplateParameterType)) {
|
||||||
@ -2492,8 +2505,7 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams,
|
|||||||
// If a template parameter of a primary class template or alias template
|
// If a template parameter of a primary class template or alias template
|
||||||
// is a template parameter pack, it shall be the last template parameter.
|
// is a template parameter pack, it shall be the last template parameter.
|
||||||
if (SawParameterPack && (NewParam + 1) != NewParamEnd &&
|
if (SawParameterPack && (NewParam + 1) != NewParamEnd &&
|
||||||
(TPC == TPC_ClassTemplate || TPC == TPC_VarTemplate ||
|
(TPC == TPC_Other || TPC == TPC_TemplateTemplateParameterPack)) {
|
||||||
TPC == TPC_TypeAliasTemplate)) {
|
|
||||||
Diag((*NewParam)->getLocation(),
|
Diag((*NewParam)->getLocation(),
|
||||||
diag::err_template_param_pack_must_be_last_template_parameter);
|
diag::err_template_param_pack_must_be_last_template_parameter);
|
||||||
Invalid = true;
|
Invalid = true;
|
||||||
@ -2526,8 +2538,8 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams,
|
|||||||
<< PrevModuleName;
|
<< PrevModuleName;
|
||||||
Invalid = true;
|
Invalid = true;
|
||||||
} else if (MissingDefaultArg &&
|
} else if (MissingDefaultArg &&
|
||||||
(TPC == TPC_ClassTemplate || TPC == TPC_FriendClassTemplate ||
|
(TPC == TPC_Other || TPC == TPC_TemplateTemplateParameterPack ||
|
||||||
TPC == TPC_VarTemplate || TPC == TPC_TypeAliasTemplate)) {
|
TPC == TPC_FriendClassTemplate)) {
|
||||||
// C++ 23[temp.param]p14:
|
// C++ 23[temp.param]p14:
|
||||||
// If a template-parameter of a class template, variable template, or
|
// If a template-parameter of a class template, variable template, or
|
||||||
// alias template has a default template argument, each subsequent
|
// alias template has a default template argument, each subsequent
|
||||||
|
@ -3427,9 +3427,9 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction(
|
|||||||
if (!P.isPackExpansion() && !A.isPackExpansion()) {
|
if (!P.isPackExpansion() && !A.isPackExpansion()) {
|
||||||
Info.Param =
|
Info.Param =
|
||||||
makeTemplateParameter(Template->getTemplateParameters()->getParam(
|
makeTemplateParameter(Template->getTemplateParameters()->getParam(
|
||||||
(PsStack.empty() ? TemplateArgs.end()
|
(AsStack.empty() ? CTAI.CanonicalConverted.end()
|
||||||
: PsStack.front().begin()) -
|
: AsStack.front().begin()) -
|
||||||
TemplateArgs.begin()));
|
1 - CTAI.CanonicalConverted.begin()));
|
||||||
Info.FirstArg = P;
|
Info.FirstArg = P;
|
||||||
Info.SecondArg = A;
|
Info.SecondArg = A;
|
||||||
return TemplateDeductionResult::NonDeducedMismatch;
|
return TemplateDeductionResult::NonDeducedMismatch;
|
||||||
@ -6642,17 +6642,19 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
|
|||||||
|
|
||||||
TemplateDeductionResult TDK;
|
TemplateDeductionResult TDK;
|
||||||
runWithSufficientStackSpace(Info.getLocation(), [&] {
|
runWithSufficientStackSpace(Info.getLocation(), [&] {
|
||||||
TDK = ::FinishTemplateArgumentDeduction(
|
TDK = ::FinishTemplateArgumentDeduction(*this, AArg, PartialOrdering, PArgs,
|
||||||
*this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info);
|
Deduced, Info);
|
||||||
});
|
});
|
||||||
switch (TDK) {
|
switch (TDK) {
|
||||||
case TemplateDeductionResult::Success:
|
case TemplateDeductionResult::Success:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// It doesn't seem possible to get a non-deduced mismatch when partial
|
// It doesn't seem possible to get a non-deduced mismatch when partial
|
||||||
// ordering TTPs.
|
// ordering TTPs, except with an invalid template parameter list which has
|
||||||
|
// a parameter after a pack.
|
||||||
case TemplateDeductionResult::NonDeducedMismatch:
|
case TemplateDeductionResult::NonDeducedMismatch:
|
||||||
llvm_unreachable("Unexpected NonDeducedMismatch");
|
assert(PArg->isInvalidDecl() && "Unexpected NonDeducedMismatch");
|
||||||
|
return false;
|
||||||
|
|
||||||
// Substitution failures should have already been diagnosed.
|
// Substitution failures should have already been diagnosed.
|
||||||
case TemplateDeductionResult::AlreadyDiagnosed:
|
case TemplateDeductionResult::AlreadyDiagnosed:
|
||||||
|
@ -2176,7 +2176,7 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
|
|||||||
// Do some additional validation, then merge default arguments
|
// Do some additional validation, then merge default arguments
|
||||||
// from the existing declarations.
|
// from the existing declarations.
|
||||||
if (SemaRef.CheckTemplateParameterList(InstParams, PrevParams,
|
if (SemaRef.CheckTemplateParameterList(InstParams, PrevParams,
|
||||||
Sema::TPC_ClassTemplate))
|
Sema::TPC_Other))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Inst->setAccess(PrevClassTemplate->getAccess());
|
Inst->setAccess(PrevClassTemplate->getAccess());
|
||||||
|
@ -602,6 +602,11 @@ namespace regression3 {
|
|||||||
template <class...> class B {};
|
template <class...> class B {};
|
||||||
template struct A<B, Node<None>>;
|
template struct A<B, Node<None>>;
|
||||||
} // namespace regression3
|
} // namespace regression3
|
||||||
|
namespace GH130362 {
|
||||||
|
template <template <template <class... T1> class TT1> class TT2> struct A {};
|
||||||
|
template <template <class U1> class UU1> struct B {};
|
||||||
|
template struct A<B>;
|
||||||
|
} // namespace GH130362
|
||||||
|
|
||||||
namespace nttp_auto {
|
namespace nttp_auto {
|
||||||
namespace t1 {
|
namespace t1 {
|
||||||
@ -610,24 +615,16 @@ namespace nttp_auto {
|
|||||||
template struct A<B>;
|
template struct A<B>;
|
||||||
} // namespace t1
|
} // namespace t1
|
||||||
namespace t2 {
|
namespace t2 {
|
||||||
// FIXME: Shouldn't accept parameters after a parameter pack.
|
|
||||||
template<template<auto... Va1, auto Va2> class> struct A {};
|
template<template<auto... Va1, auto Va2> class> struct A {};
|
||||||
// expected-error@-1 {{deduced non-type template argument does not have the same type as the corresponding template parameter ('auto' vs 'int')}}
|
// expected-error@-1 {{template parameter pack must be the last template parameter}}
|
||||||
// expected-note@-2 {{previous template template parameter is here}}
|
|
||||||
template<int... Vi> struct B;
|
template<int... Vi> struct B;
|
||||||
// expected-note@-1 {{template parameter is declared here}}
|
|
||||||
template struct A<B>;
|
template struct A<B>;
|
||||||
// expected-note@-1 {{different template parameters}}
|
|
||||||
} // namespace t2
|
} // namespace t2
|
||||||
namespace t3 {
|
namespace t3 {
|
||||||
// FIXME: Shouldn't accept parameters after a parameter pack.
|
|
||||||
template<template<auto... Va1, auto... Va2> class> struct A {};
|
template<template<auto... Va1, auto... Va2> class> struct A {};
|
||||||
// expected-error@-1 {{deduced non-type template argument does not have the same type as the corresponding template parameter ('auto' vs 'int')}}
|
// expected-error@-1 {{template parameter pack must be the last template parameter}}
|
||||||
// expected-note@-2 {{previous template template parameter is here}}
|
|
||||||
template<int... Vi> struct B;
|
template<int... Vi> struct B;
|
||||||
// expected-note@-1 {{template parameter is declared here}}
|
|
||||||
template struct A<B>;
|
template struct A<B>;
|
||||||
// expected-note@-1 {{different template parameters}}
|
|
||||||
} // namespace t3
|
} // namespace t3
|
||||||
} // namespace nttp_auto
|
} // namespace nttp_auto
|
||||||
|
|
||||||
|
@ -7,7 +7,8 @@
|
|||||||
template<template<int> typename> struct Ti; // #Ti
|
template<template<int> typename> struct Ti; // #Ti
|
||||||
template<template<int...> typename> struct TPi; // #TPi
|
template<template<int...> typename> struct TPi; // #TPi
|
||||||
template<template<int, int...> typename> struct TiPi;
|
template<template<int, int...> typename> struct TiPi;
|
||||||
template<template<int..., int...> typename> struct TPiPi; // FIXME: Why is this not ill-formed?
|
template<template<int..., int...> typename> struct TPiPi;
|
||||||
|
// expected-error@-1 {{template parameter pack must be the last template parameter}}
|
||||||
|
|
||||||
template<typename T, template<T> typename> struct tT0; // #tT0
|
template<typename T, template<T> typename> struct tT0; // #tT0
|
||||||
template<template<typename T, T> typename> struct Tt0; // #Tt0
|
template<template<typename T, T> typename> struct Tt0; // #Tt0
|
||||||
|
@ -1196,21 +1196,21 @@ TEST(DeclPrinter, TestUnnamedTemplateParameters) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(DeclPrinter, TestUnnamedTemplateParametersPacks) {
|
TEST(DeclPrinter, TestUnnamedTemplateParametersPacks) {
|
||||||
ASSERT_TRUE(PrintedDeclCXX17Matches(
|
ASSERT_TRUE(
|
||||||
"template <typename ..., int ...,"
|
PrintedDeclCXX17Matches("template <typename ..., int ...,"
|
||||||
" template <typename ..., bool ...> class ...> void A();",
|
" template <typename ...> class ...> void A();",
|
||||||
functionTemplateDecl(hasName("A")).bind("id"),
|
functionTemplateDecl(hasName("A")).bind("id"),
|
||||||
"template <typename ..., int ...,"
|
"template <typename ..., int ...,"
|
||||||
" template <typename ..., bool ...> class ...> void A()"));
|
" template <typename ...> class ...> void A()"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(DeclPrinter, TestNamedTemplateParametersPacks) {
|
TEST(DeclPrinter, TestNamedTemplateParametersPacks) {
|
||||||
ASSERT_TRUE(PrintedDeclCXX17Matches(
|
ASSERT_TRUE(PrintedDeclCXX17Matches(
|
||||||
"template <typename ...T, int ...I,"
|
"template <typename ...T, int ...I,"
|
||||||
" template <typename ...X, bool ...B> class ...Z> void A();",
|
" template <typename ...X> class ...Z> void A();",
|
||||||
functionTemplateDecl(hasName("A")).bind("id"),
|
functionTemplateDecl(hasName("A")).bind("id"),
|
||||||
"template <typename ...T, int ...I,"
|
"template <typename ...T, int ...I,"
|
||||||
" template <typename ...X, bool ...B> class ...Z> void A()"));
|
" template <typename ...X> class ...Z> void A()"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(DeclPrinter, TestTemplateTemplateParameterWrittenWithTypename) {
|
TEST(DeclPrinter, TestTemplateTemplateParameterWrittenWithTypename) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user