Skip to content

Commit 4f4e658

Browse files
committed
[clang] fix template argument conversion
Converted template arguments need to be converted again, if the corresponding template parameter changed, as different conversions might apply in that case.
1 parent 8210ac8 commit 4f4e658

File tree

7 files changed

+122
-76
lines changed

7 files changed

+122
-76
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,9 @@ Bug Fixes to C++ Support
993993
- Fix immediate escalation not propagating through inherited constructors. (#GH112677)
994994
- Fixed assertions or false compiler diagnostics in the case of C++ modules for
995995
lambda functions or inline friend functions defined inside templates (#GH122493).
996+
- Fix template argument checking so that converted template arguments are
997+
converted again. This fixes some issues with partial ordering involving
998+
template template parameters with non-type template parameters.
996999

9971000
Bug Fixes to AST Handling
9981001
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/Expr.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2262,6 +2262,16 @@ class UnaryOperator final
22622262
}
22632263

22642264
public:
2265+
enum OnStack_t { OnStack };
2266+
UnaryOperator(OnStack_t _, const ASTContext &Ctx, Expr *input, Opcode opc,
2267+
QualType type, ExprValueKind VK, ExprObjectKind OK,
2268+
SourceLocation l, bool CanOverflow,
2269+
FPOptionsOverride FPFeatures)
2270+
: UnaryOperator(Ctx, input, opc, type, VK, OK, l, CanOverflow,
2271+
FPFeatures) {
2272+
assert(!FPFeatures.requiresTrailingStorage());
2273+
}
2274+
22652275
static UnaryOperator *CreateEmpty(const ASTContext &C, bool hasFPFeatures);
22662276

22672277
static UnaryOperator *Create(const ASTContext &C, Expr *input, Opcode opc,

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11753,6 +11753,8 @@ class Sema final : public SemaBase {
1175311753
/// If an error occurred, it returns ExprError(); otherwise, it
1175411754
/// returns the converted template argument. \p ParamType is the
1175511755
/// type of the non-type template parameter after it has been instantiated.
11756+
/// \p Arg is the input template argument expression to be converted.
11757+
/// this expression may not necessarily outlive the converted result.
1175611758
ExprResult CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
1175711759
QualType InstantiatedParamType, Expr *Arg,
1175811760
TemplateArgument &SugaredConverted,

clang/lib/AST/TemplateBase.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -515,19 +515,17 @@ void TemplateArgument::print(const PrintingPolicy &Policy, raw_ostream &Out,
515515
}
516516

517517
case Declaration: {
518-
NamedDecl *ND = getAsDecl();
518+
ValueDecl *VD = getAsDecl();
519519
if (getParamTypeForDecl()->isRecordType()) {
520-
if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(ND)) {
520+
if (auto *TPO = dyn_cast<TemplateParamObjectDecl>(VD)) {
521521
TPO->getType().getUnqualifiedType().print(Out, Policy);
522522
TPO->printAsInit(Out, Policy);
523523
break;
524524
}
525525
}
526-
if (auto *VD = dyn_cast<ValueDecl>(ND)) {
527-
if (needsAmpersandOnTemplateArg(getParamTypeForDecl(), VD->getType()))
528-
Out << "&";
529-
}
530-
ND->printQualifiedName(Out);
526+
if (needsAmpersandOnTemplateArg(getParamTypeForDecl(), VD->getType()))
527+
Out << "&";
528+
VD->printQualifiedName(Out);
531529
break;
532530
}
533531

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 98 additions & 47 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,114 @@ 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+
};
52715269

5270+
switch (Arg.getKind()) {
5271+
case TemplateArgument::Null:
5272+
llvm_unreachable("Should never see a NULL template argument here");
5273+
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:
5285-
case TemplateArgument::Integral:
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.
5294+
case TemplateArgument::Integral: {
5295+
IntegerLiteral ILE(Context, Arg.getAsIntegral(), Arg.getIntegralType(),
5296+
SourceLocation());
5297+
if (!checkExpr(&ILE))
5298+
return true;
5299+
break;
5300+
}
5301+
case TemplateArgument::Declaration: {
5302+
bool needsAddrOf = false;
5303+
ValueDecl *VD = Arg.getAsDecl();
5304+
QualType ParamType = Arg.getParamTypeForDecl(), DeclType = VD->getType();
5305+
QualType DeclRefType;
5306+
auto handlePtrType = [&](auto *P) {
5307+
QualType PointeeType = P->getPointeeType();
5308+
if (!Context.hasSameType(P->getPointeeType(), DeclType))
5309+
return;
5310+
needsAddrOf = true;
5311+
DeclRefType = PointeeType;
5312+
};
5313+
if (auto *P = ParamType->getAs<PointerType>())
5314+
handlePtrType(P);
5315+
else if (auto *P = ParamType->getAs<MemberPointerType>())
5316+
handlePtrType(P);
5317+
else
5318+
DeclRefType = ParamType->castAs<ReferenceType>()->getPointeeType();
5319+
5320+
DeclRefExpr DRE(Context, Arg.getAsDecl(),
5321+
/*RefersToEnclosingVariableOrCapture=*/false, DeclRefType,
5322+
VK_LValue, SourceLocation());
5323+
if (needsAddrOf) {
5324+
UnaryOperator UOE(UnaryOperator::OnStack, Context, &DRE, UO_AddrOf,
5325+
ParamType, VK_PRValue, OK_Ordinary, SourceLocation(),
5326+
/*CanOverflow=*/false, /*FPFeatures=*/{});
5327+
if (!checkExpr(&UOE))
5328+
return true;
5329+
} else {
5330+
if (!checkExpr(&DRE))
5331+
return true;
5332+
}
5333+
break;
5334+
}
5335+
case TemplateArgument::NullPtr: {
5336+
CXXNullPtrLiteralExpr NPLE(Arg.getNullPtrType(), SourceLocation());
5337+
if (!checkExpr(&NPLE))
5338+
return true;
5339+
break;
5340+
}
52865341
case TemplateArgument::StructuralValue:
5287-
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()));
5342+
// FIXME: Is it even possible to reach here?
5343+
// If this is actually used, this needs to convert the argument again.
5344+
SugaredConverted.push_back(Arg);
5345+
CanonicalConverted.push_back(Context.getCanonicalTemplateArgument(Arg));
52935346
break;
52945347

52955348
case TemplateArgument::Template:
52965349
case TemplateArgument::TemplateExpansion:
52975350
// We were given a template template argument. It may not be ill-formed;
52985351
// see below.
5299-
if (DependentTemplateName *DTN
5300-
= Arg.getArgument().getAsTemplateOrTemplatePattern()
5301-
.getAsDependentTemplateName()) {
5352+
if (DependentTemplateName *DTN = Arg.getAsTemplateOrTemplatePattern()
5353+
.getAsDependentTemplateName()) {
53025354
// We have a template argument such as \c T::template X, which we
53035355
// parsed as a template template argument. However, since we now
53045356
// know that we need a non-type template argument, convert this
53055357
// template name into an expression.
53065358

53075359
DeclarationNameInfo NameInfo(DTN->getIdentifier(),
5308-
Arg.getTemplateNameLoc());
5360+
ArgLoc.getTemplateNameLoc());
53095361

53105362
CXXScopeSpec SS;
5311-
SS.Adopt(Arg.getTemplateQualifierLoc());
5363+
SS.Adopt(ArgLoc.getTemplateQualifierLoc());
53125364
// FIXME: the template-template arg was a DependentTemplateName,
53135365
// so it was provided with a template keyword. However, its source
53145366
// location is not stored in the template argument structure.
@@ -5319,8 +5371,8 @@ bool Sema::CheckTemplateArgument(
53195371

53205372
// If we parsed the template argument as a pack expansion, create a
53215373
// pack expansion expression.
5322-
if (Arg.getArgument().getKind() == TemplateArgument::TemplateExpansion){
5323-
E = ActOnPackExpansion(E.get(), Arg.getTemplateEllipsisLoc());
5374+
if (Arg.getKind() == TemplateArgument::TemplateExpansion) {
5375+
E = ActOnPackExpansion(E.get(), ArgLoc.getTemplateEllipsisLoc());
53245376
if (E.isInvalid())
53255377
return true;
53265378
}
@@ -5340,8 +5392,8 @@ bool Sema::CheckTemplateArgument(
53405392
// We have a template argument that actually does refer to a class
53415393
// template, alias template, or template template parameter, and
53425394
// therefore cannot be a non-type template argument.
5343-
Diag(Arg.getLocation(), diag::err_template_arg_must_be_expr)
5344-
<< Arg.getSourceRange();
5395+
Diag(ArgLoc.getLocation(), diag::err_template_arg_must_be_expr)
5396+
<< ArgLoc.getSourceRange();
53455397
NoteTemplateParameterLocation(*Param);
53465398

53475399
return true;
@@ -5357,8 +5409,8 @@ bool Sema::CheckTemplateArgument(
53575409
//
53585410
// We warn specifically about this case, since it can be rather
53595411
// confusing for users.
5360-
QualType T = Arg.getArgument().getAsType();
5361-
SourceRange SR = Arg.getSourceRange();
5412+
QualType T = Arg.getAsType();
5413+
SourceRange SR = ArgLoc.getSourceRange();
53625414
if (T->isFunctionType())
53635415
Diag(SR.getBegin(), diag::err_template_arg_nontype_ambig) << SR << T;
53645416
else
@@ -5409,34 +5461,33 @@ bool Sema::CheckTemplateArgument(
54095461
// When [the injected-class-name] is used [...] as a template-argument for
54105462
// a template template-parameter [...] it refers to the class template
54115463
// itself.
5412-
if (Arg.getArgument().getKind() == TemplateArgument::Type) {
5464+
if (Arg.getKind() == TemplateArgument::Type) {
54135465
TemplateArgumentLoc ConvertedArg = convertTypeTemplateArgumentToTemplate(
5414-
Context, Arg.getTypeSourceInfo()->getTypeLoc());
5466+
Context, ArgLoc.getTypeSourceInfo()->getTypeLoc());
54155467
if (!ConvertedArg.getArgument().isNull())
5416-
Arg = ConvertedArg;
5468+
ArgLoc = ConvertedArg;
54175469
}
54185470

5419-
switch (Arg.getArgument().getKind()) {
5471+
switch (Arg.getKind()) {
54205472
case TemplateArgument::Null:
54215473
llvm_unreachable("Should never see a NULL template argument here");
54225474

54235475
case TemplateArgument::Template:
54245476
case TemplateArgument::TemplateExpansion:
5425-
if (CheckTemplateTemplateArgument(TempParm, Params, Arg, PartialOrdering,
5477+
if (CheckTemplateTemplateArgument(TempParm, Params, ArgLoc, PartialOrdering,
54265478
MatchedPackOnParmToNonPackOnArg))
54275479
return true;
54285480

5429-
SugaredConverted.push_back(Arg.getArgument());
5430-
CanonicalConverted.push_back(
5431-
Context.getCanonicalTemplateArgument(Arg.getArgument()));
5481+
SugaredConverted.push_back(Arg);
5482+
CanonicalConverted.push_back(Context.getCanonicalTemplateArgument(Arg));
54325483
break;
54335484

54345485
case TemplateArgument::Expression:
54355486
case TemplateArgument::Type:
54365487
// We have a template template parameter but the template
54375488
// argument does not refer to a template.
5438-
Diag(Arg.getLocation(), diag::err_template_arg_must_be_template)
5439-
<< getLangOpts().CPlusPlus11;
5489+
Diag(ArgLoc.getLocation(), diag::err_template_arg_must_be_template)
5490+
<< getLangOpts().CPlusPlus11;
54405491
return true;
54415492

54425493
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)