Skip to content

Commit b197268

Browse files
authored
[clang] fix template argument conversion (llvm#124386)
Converted template arguments need to be converted again, if the corresponding template parameter changed, as different conversions might apply in that case.
1 parent a255da0 commit b197268

File tree

5 files changed

+66
-68
lines changed

5 files changed

+66
-68
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,9 @@ Bug Fixes to C++ Support
10341034
- Fix immediate escalation not propagating through inherited constructors. (#GH112677)
10351035
- Fixed assertions or false compiler diagnostics in the case of C++ modules for
10361036
lambda functions or inline friend functions defined inside templates (#GH122493).
1037+
- Fix template argument checking so that converted template arguments are
1038+
converted again. This fixes some issues with partial ordering involving
1039+
template template parameters with non-type template parameters.
10371040
- Clang now rejects declaring an alias template with the same name as its template parameter. (#GH123423)
10381041
- Fixed the rejection of valid code when referencing an enumerator of an unscoped enum member with a prior declaration. (#GH124405)
10391042
- Fixed immediate escalation of non-dependent expressions. (#GH123405)

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11780,6 +11780,9 @@ class Sema final : public SemaBase {
1178011780
/// declaration and the type of its corresponding non-type template
1178111781
/// parameter, produce an expression that properly refers to that
1178211782
/// declaration.
11783+
/// FIXME: This is used in some contexts where the resulting expression
11784+
/// doesn't need to live too long. It would be useful if this function
11785+
/// could return a temporary expression.
1178311786
ExprResult BuildExpressionFromDeclTemplateArgument(
1178411787
const TemplateArgument &Arg, QualType ParamType, SourceLocation Loc,
1178511788
NamedDecl *TemplateParam = nullptr);

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 56 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5199,7 +5199,7 @@ convertTypeTemplateArgumentToTemplate(ASTContext &Context, TypeLoc TLoc) {
51995199
}
52005200

52015201
bool Sema::CheckTemplateArgument(
5202-
NamedDecl *Param, TemplateArgumentLoc &Arg, NamedDecl *Template,
5202+
NamedDecl *Param, TemplateArgumentLoc &ArgLoc, NamedDecl *Template,
52035203
SourceLocation TemplateLoc, SourceLocation RAngleLoc,
52045204
unsigned ArgumentPackIndex,
52055205
SmallVectorImpl<TemplateArgument> &SugaredConverted,
@@ -5208,9 +5208,10 @@ bool Sema::CheckTemplateArgument(
52085208
bool PartialOrderingTTP, bool *MatchedPackOnParmToNonPackOnArg) {
52095209
// Check template type parameters.
52105210
if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(Param))
5211-
return CheckTemplateTypeArgument(TTP, Arg, SugaredConverted,
5211+
return CheckTemplateTypeArgument(TTP, ArgLoc, SugaredConverted,
52125212
CanonicalConverted);
52135213

5214+
const TemplateArgument &Arg = ArgLoc.getArgument();
52145215
// Check non-type template parameters.
52155216
if (NonTypeTemplateParmDecl *NTTP =dyn_cast<NonTypeTemplateParmDecl>(Param)) {
52165217
// Do substitution on the type of the non-type template parameter
@@ -5252,63 +5253,73 @@ bool Sema::CheckTemplateArgument(
52525253
return true;
52535254
}
52545255

5255-
switch (Arg.getArgument().getKind()) {
5256-
case TemplateArgument::Null:
5257-
llvm_unreachable("Should never see a NULL template argument here");
5258-
5259-
case TemplateArgument::Expression: {
5260-
Expr *E = Arg.getArgument().getAsExpr();
5256+
auto checkExpr = [&](Expr *E) -> Expr * {
52615257
TemplateArgument SugaredResult, CanonicalResult;
52625258
unsigned CurSFINAEErrors = NumSFINAEErrors;
52635259
ExprResult Res =
52645260
CheckTemplateArgument(NTTP, NTTPType, E, SugaredResult,
52655261
CanonicalResult, PartialOrderingTTP, CTAK);
5266-
if (Res.isInvalid())
5267-
return true;
52685262
// If the current template argument causes an error, give up now.
5269-
if (CurSFINAEErrors < NumSFINAEErrors)
5270-
return true;
5263+
if (Res.isInvalid() || CurSFINAEErrors < NumSFINAEErrors)
5264+
return nullptr;
5265+
SugaredConverted.push_back(SugaredResult);
5266+
CanonicalConverted.push_back(CanonicalResult);
5267+
return Res.get();
5268+
};
5269+
5270+
switch (Arg.getKind()) {
5271+
case TemplateArgument::Null:
5272+
llvm_unreachable("Should never see a NULL template argument here");
52715273

5274+
case TemplateArgument::Expression: {
5275+
Expr *E = Arg.getAsExpr();
5276+
Expr *R = checkExpr(E);
5277+
if (!R)
5278+
return true;
52725279
// If the resulting expression is new, then use it in place of the
52735280
// old expression in the template argument.
5274-
if (Res.get() != E) {
5275-
TemplateArgument TA(Res.get());
5276-
Arg = TemplateArgumentLoc(TA, Res.get());
5281+
if (R != E) {
5282+
TemplateArgument TA(R);
5283+
ArgLoc = TemplateArgumentLoc(TA, R);
52775284
}
5278-
5279-
SugaredConverted.push_back(SugaredResult);
5280-
CanonicalConverted.push_back(CanonicalResult);
52815285
break;
52825286
}
52835287

5284-
case TemplateArgument::Declaration:
5288+
// As for the converted NTTP kinds, they still might need another
5289+
// conversion, as the new corresponding parameter might be different.
5290+
// Ideally, we would always perform substitution starting with sugared types
5291+
// and never need these, as we would still have expressions. Since these are
5292+
// needed so rarely, it's probably a better tradeoff to just convert them
5293+
// back to expressions.
52855294
case TemplateArgument::Integral:
5286-
case TemplateArgument::StructuralValue:
5295+
case TemplateArgument::Declaration:
52875296
case TemplateArgument::NullPtr:
5288-
// We've already checked this template argument, so just copy
5289-
// it to the list of converted arguments.
5290-
SugaredConverted.push_back(Arg.getArgument());
5291-
CanonicalConverted.push_back(
5292-
Context.getCanonicalTemplateArgument(Arg.getArgument()));
5297+
case TemplateArgument::StructuralValue: {
5298+
// FIXME: StructuralValue is untested here.
5299+
ExprResult R =
5300+
BuildExpressionFromNonTypeTemplateArgument(Arg, SourceLocation());
5301+
assert(R.isUsable());
5302+
if (!checkExpr(R.get()))
5303+
return true;
52935304
break;
5305+
}
52945306

52955307
case TemplateArgument::Template:
52965308
case TemplateArgument::TemplateExpansion:
52975309
// We were given a template template argument. It may not be ill-formed;
52985310
// see below.
5299-
if (DependentTemplateName *DTN
5300-
= Arg.getArgument().getAsTemplateOrTemplatePattern()
5301-
.getAsDependentTemplateName()) {
5311+
if (DependentTemplateName *DTN = Arg.getAsTemplateOrTemplatePattern()
5312+
.getAsDependentTemplateName()) {
53025313
// We have a template argument such as \c T::template X, which we
53035314
// parsed as a template template argument. However, since we now
53045315
// know that we need a non-type template argument, convert this
53055316
// template name into an expression.
53065317

53075318
DeclarationNameInfo NameInfo(DTN->getIdentifier(),
5308-
Arg.getTemplateNameLoc());
5319+
ArgLoc.getTemplateNameLoc());
53095320

53105321
CXXScopeSpec SS;
5311-
SS.Adopt(Arg.getTemplateQualifierLoc());
5322+
SS.Adopt(ArgLoc.getTemplateQualifierLoc());
53125323
// FIXME: the template-template arg was a DependentTemplateName,
53135324
// so it was provided with a template keyword. However, its source
53145325
// location is not stored in the template argument structure.
@@ -5319,8 +5330,8 @@ bool Sema::CheckTemplateArgument(
53195330

53205331
// If we parsed the template argument as a pack expansion, create a
53215332
// pack expansion expression.
5322-
if (Arg.getArgument().getKind() == TemplateArgument::TemplateExpansion){
5323-
E = ActOnPackExpansion(E.get(), Arg.getTemplateEllipsisLoc());
5333+
if (Arg.getKind() == TemplateArgument::TemplateExpansion) {
5334+
E = ActOnPackExpansion(E.get(), ArgLoc.getTemplateEllipsisLoc());
53245335
if (E.isInvalid())
53255336
return true;
53265337
}
@@ -5340,8 +5351,8 @@ bool Sema::CheckTemplateArgument(
53405351
// We have a template argument that actually does refer to a class
53415352
// template, alias template, or template template parameter, and
53425353
// therefore cannot be a non-type template argument.
5343-
Diag(Arg.getLocation(), diag::err_template_arg_must_be_expr)
5344-
<< Arg.getSourceRange();
5354+
Diag(ArgLoc.getLocation(), diag::err_template_arg_must_be_expr)
5355+
<< ArgLoc.getSourceRange();
53455356
NoteTemplateParameterLocation(*Param);
53465357

53475358
return true;
@@ -5357,8 +5368,8 @@ bool Sema::CheckTemplateArgument(
53575368
//
53585369
// We warn specifically about this case, since it can be rather
53595370
// confusing for users.
5360-
QualType T = Arg.getArgument().getAsType();
5361-
SourceRange SR = Arg.getSourceRange();
5371+
QualType T = Arg.getAsType();
5372+
SourceRange SR = ArgLoc.getSourceRange();
53625373
if (T->isFunctionType())
53635374
Diag(SR.getBegin(), diag::err_template_arg_nontype_ambig) << SR << T;
53645375
else
@@ -5409,34 +5420,33 @@ bool Sema::CheckTemplateArgument(
54095420
// When [the injected-class-name] is used [...] as a template-argument for
54105421
// a template template-parameter [...] it refers to the class template
54115422
// itself.
5412-
if (Arg.getArgument().getKind() == TemplateArgument::Type) {
5423+
if (Arg.getKind() == TemplateArgument::Type) {
54135424
TemplateArgumentLoc ConvertedArg = convertTypeTemplateArgumentToTemplate(
5414-
Context, Arg.getTypeSourceInfo()->getTypeLoc());
5425+
Context, ArgLoc.getTypeSourceInfo()->getTypeLoc());
54155426
if (!ConvertedArg.getArgument().isNull())
5416-
Arg = ConvertedArg;
5427+
ArgLoc = ConvertedArg;
54175428
}
54185429

5419-
switch (Arg.getArgument().getKind()) {
5430+
switch (Arg.getKind()) {
54205431
case TemplateArgument::Null:
54215432
llvm_unreachable("Should never see a NULL template argument here");
54225433

54235434
case TemplateArgument::Template:
54245435
case TemplateArgument::TemplateExpansion:
5425-
if (CheckTemplateTemplateArgument(TempParm, Params, Arg, PartialOrdering,
5436+
if (CheckTemplateTemplateArgument(TempParm, Params, ArgLoc, PartialOrdering,
54265437
MatchedPackOnParmToNonPackOnArg))
54275438
return true;
54285439

5429-
SugaredConverted.push_back(Arg.getArgument());
5430-
CanonicalConverted.push_back(
5431-
Context.getCanonicalTemplateArgument(Arg.getArgument()));
5440+
SugaredConverted.push_back(Arg);
5441+
CanonicalConverted.push_back(Context.getCanonicalTemplateArgument(Arg));
54325442
break;
54335443

54345444
case TemplateArgument::Expression:
54355445
case TemplateArgument::Type:
54365446
// We have a template template parameter but the template
54375447
// argument does not refer to a template.
5438-
Diag(Arg.getLocation(), diag::err_template_arg_must_be_template)
5439-
<< getLangOpts().CPlusPlus11;
5448+
Diag(ArgLoc.getLocation(), diag::err_template_arg_must_be_template)
5449+
<< getLangOpts().CPlusPlus11;
54405450
return true;
54415451

54425452
case TemplateArgument::Declaration:

clang/lib/Sema/TreeTransform.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7335,8 +7335,10 @@ QualType TreeTransform<Derived>::TransformTemplateSpecializationType(
73357335
NewTemplateArgs))
73367336
return QualType();
73377337

7338-
// FIXME: maybe don't rebuild if all the template arguments are the same.
7339-
7338+
// This needs to be rebuilt if either the arguments changed, or if the
7339+
// original template changed. If the template changed, and even if the
7340+
// arguments didn't change, these arguments might not correspond to their
7341+
// respective parameters, therefore needing conversions.
73407342
QualType Result =
73417343
getDerived().RebuildTemplateSpecializationType(Template,
73427344
TL.getTemplateNameLoc(),

clang/test/SemaTemplate/cwg2398.cpp

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -663,58 +663,38 @@ namespace nttp_auto {
663663

664664
namespace nttp_partial_order {
665665
namespace t1 {
666-
// FIXME: This should pick the second overload.
667666
template<template<short> class TT1> void f(TT1<0>);
668-
// new-note@-1 {{here}}
669667
template<template<int> class TT2> void f(TT2<0>) {}
670-
// new-note@-1 {{here}}
671668
template<int> struct B {};
672669
template void f<B>(B<0>);
673-
// new-error@-1 {{ambiguous}}
674670
} // namespace t1
675671
namespace t2 {
676-
// FIXME: This should pick the second overload.
677672
struct A {} a;
678673
template<template<A&> class TT1> void f(TT1<a>);
679-
// new-note@-1 {{here}}
680674
template<template<const A&> class TT2> void f(TT2<a>) {}
681-
// new-note@-1 {{here}}
682675
template<const A&> struct B {};
683676
template void f<B>(B<a>);
684-
// new-error@-1 {{ambiguous}}
685677
} // namespace t2
686678
namespace t3 {
687-
// FIXME: This should pick the second overload.
688679
enum A {};
689680
template<template<A> class TT1> void f(TT1<{}>);
690-
// new-note@-1 {{here}}
691681
template<template<int> class TT2> void f(TT2<{}>) {}
692-
// new-note@-1 {{here}}
693682
template<int> struct B {};
694683
template void f<B>(B<{}>);
695-
// new-error@-1 {{ambiguous}}
696684
} // namespace t3
697685
namespace t4 {
698-
// FIXME: This should pick the second overload.
699686
struct A {} a;
700687
template<template<A*> class TT1> void f(TT1<&a>);
701-
// new-note@-1 {{here}}
702688
template<template<const A*> class TT2> void f(TT2<&a>) {}
703-
// new-note@-1 {{here}}
704689
template<const A*> struct B {};
705690
template void f<B>(B<&a>);
706-
// new-error@-1 {{ambiguous}}
707691
} // namespace t4
708692
namespace t5 {
709-
// FIXME: This should pick the second overload.
710693
struct A { int m; };
711694
template<template<int A::*> class TT1> void f(TT1<&A::m>);
712-
// new-note@-1 {{here}}
713695
template<template<const int A::*> class TT2> void f(TT2<&A::m>) {}
714-
// new-note@-1 {{here}}
715696
template<const int A::*> struct B {};
716697
template void f<B>(B<&A::m>);
717-
// new-error@-1 {{ambiguous}}
718698
} // namespace t5
719699
namespace t6 {
720700
// FIXME: This should pick the second overload.

0 commit comments

Comments
 (0)