Skip to content

Commit eedbdbd

Browse files
committed
[clang] Changes to template argument list checking
Implement some missing changes to support checking of argument-side template parameter packs matching parameter-side non-pack parameters, which are required to support non-exact matches in these cases, and applies appropriate conversions which help deducing NTTPs of placeholder types.
1 parent 358b4a4 commit eedbdbd

File tree

5 files changed

+165
-86
lines changed

5 files changed

+165
-86
lines changed

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 69 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5509,7 +5509,7 @@ bool Sema::CheckTemplateArgumentList(
55095509

55105510
SourceLocation RAngleLoc = NewArgs.getRAngleLoc();
55115511

5512-
// C++ [temp.arg]p1:
5512+
// C++23 [temp.arg.general]p1:
55135513
// [...] The type and form of each template-argument specified in
55145514
// a template-id shall match the type and form specified for the
55155515
// corresponding parameter declared by the template in its
@@ -5570,61 +5570,69 @@ bool Sema::CheckTemplateArgumentList(
55705570
}
55715571

55725572
if (ArgIdx < NumArgs) {
5573-
// Check the template argument we were given.
5574-
if (CheckTemplateArgument(*Param, NewArgs[ArgIdx], Template, TemplateLoc,
5575-
RAngleLoc, SugaredArgumentPack.size(),
5576-
SugaredConverted, CanonicalConverted,
5577-
CTAK_Specified, /*PartialOrdering=*/false,
5578-
MatchedPackOnParmToNonPackOnArg))
5579-
return true;
5580-
5581-
CanonicalConverted.back().setIsDefaulted(
5582-
clang::isSubstitutedDefaultArgument(
5583-
Context, NewArgs[ArgIdx].getArgument(), *Param,
5584-
CanonicalConverted, Params->getDepth()));
5585-
5586-
bool PackExpansionIntoNonPack =
5587-
NewArgs[ArgIdx].getArgument().isPackExpansion() &&
5588-
(!(*Param)->isTemplateParameterPack() || getExpandedPackSize(*Param));
5589-
// CWG1430: Don't diagnose this pack expansion when partial
5590-
// ordering template template parameters. Some uses of the template could
5591-
// be valid, and invalid uses will be diagnosed later during
5592-
// instantiation.
5593-
if (PackExpansionIntoNonPack && !PartialOrderingTTP &&
5594-
(isa<TypeAliasTemplateDecl>(Template) ||
5595-
isa<ConceptDecl>(Template))) {
5596-
// CWG1430: we have a pack expansion as an argument to an
5597-
// alias template, and it's not part of a parameter pack. This
5598-
// can't be canonicalized, so reject it now.
5599-
// As for concepts - we cannot normalize constraints where this
5600-
// situation exists.
5601-
Diag(NewArgs[ArgIdx].getLocation(),
5602-
diag::err_template_expansion_into_fixed_list)
5603-
<< (isa<ConceptDecl>(Template) ? 1 : 0)
5604-
<< NewArgs[ArgIdx].getSourceRange();
5605-
NoteTemplateParameterLocation(**Param);
5606-
return true;
5573+
TemplateArgumentLoc &ArgLoc = NewArgs[ArgIdx];
5574+
bool NonPackParameter =
5575+
!(*Param)->isTemplateParameterPack() || getExpandedPackSize(*Param);
5576+
bool ArgIsExpansion = ArgLoc.getArgument().isPackExpansion();
5577+
5578+
if (ArgIsExpansion && PartialOrderingTTP) {
5579+
SmallVector<TemplateArgument, 4> Args(ParamEnd - Param);
5580+
for (TemplateParameterList::iterator First = Param; Param != ParamEnd;
5581+
++Param) {
5582+
TemplateArgument &Arg = Args[Param - First];
5583+
Arg = ArgLoc.getArgument();
5584+
if (!(*Param)->isTemplateParameterPack() ||
5585+
getExpandedPackSize(*Param))
5586+
Arg = Arg.getPackExpansionPattern();
5587+
TemplateArgumentLoc NewArgLoc(Arg, ArgLoc.getLocInfo());
5588+
if (CheckTemplateArgument(*Param, NewArgLoc, Template, TemplateLoc,
5589+
RAngleLoc, SugaredArgumentPack.size(),
5590+
SugaredConverted, CanonicalConverted,
5591+
CTAK_Specified, /*PartialOrdering=*/false,
5592+
MatchedPackOnParmToNonPackOnArg))
5593+
return true;
5594+
Arg = NewArgLoc.getArgument();
5595+
CanonicalConverted.back().setIsDefaulted(
5596+
clang::isSubstitutedDefaultArgument(Context, Arg, *Param,
5597+
CanonicalConverted,
5598+
Params->getDepth()));
5599+
}
5600+
ArgLoc =
5601+
TemplateArgumentLoc(TemplateArgument::CreatePackCopy(Context, Args),
5602+
ArgLoc.getLocInfo());
5603+
} else {
5604+
if (CheckTemplateArgument(*Param, ArgLoc, Template, TemplateLoc,
5605+
RAngleLoc, SugaredArgumentPack.size(),
5606+
SugaredConverted, CanonicalConverted,
5607+
CTAK_Specified, /*PartialOrdering=*/false,
5608+
MatchedPackOnParmToNonPackOnArg))
5609+
return true;
5610+
CanonicalConverted.back().setIsDefaulted(
5611+
clang::isSubstitutedDefaultArgument(Context, ArgLoc.getArgument(),
5612+
*Param, CanonicalConverted,
5613+
Params->getDepth()));
5614+
if (ArgIsExpansion && NonPackParameter) {
5615+
// CWG1430/CWG2686: we have a pack expansion as an argument to an
5616+
// alias template or concept, and it's not part of a parameter pack.
5617+
// This can't be canonicalized, so reject it now.
5618+
if (isa<TypeAliasTemplateDecl, ConceptDecl>(Template)) {
5619+
Diag(ArgLoc.getLocation(),
5620+
diag::err_template_expansion_into_fixed_list)
5621+
<< (isa<ConceptDecl>(Template) ? 1 : 0)
5622+
<< ArgLoc.getSourceRange();
5623+
NoteTemplateParameterLocation(**Param);
5624+
return true;
5625+
}
5626+
}
56075627
}
56085628

56095629
// We're now done with this argument.
56105630
++ArgIdx;
56115631

5612-
if ((*Param)->isTemplateParameterPack()) {
5613-
// The template parameter was a template parameter pack, so take the
5614-
// deduced argument and place it on the argument pack. Note that we
5615-
// stay on the same template parameter so that we can deduce more
5616-
// arguments.
5617-
SugaredArgumentPack.push_back(SugaredConverted.pop_back_val());
5618-
CanonicalArgumentPack.push_back(CanonicalConverted.pop_back_val());
5619-
} else {
5620-
// Move to the next template parameter.
5621-
++Param;
5622-
}
5632+
if (ArgIsExpansion && (PartialOrderingTTP || NonPackParameter)) {
5633+
// Directly convert the remaining arguments, because we don't know what
5634+
// parameters they'll match up with.
56235635

5624-
// If we just saw a pack expansion into a non-pack, then directly convert
5625-
// the remaining arguments, because we don't know what parameters they'll
5626-
// match up with.
5627-
if (PackExpansionIntoNonPack) {
56285636
if (!SugaredArgumentPack.empty()) {
56295637
// If we were part way through filling in an expanded parameter pack,
56305638
// fall back to just producing individual arguments.
@@ -5650,6 +5658,17 @@ bool Sema::CheckTemplateArgumentList(
56505658
return false;
56515659
}
56525660

5661+
if ((*Param)->isTemplateParameterPack()) {
5662+
// The template parameter was a template parameter pack, so take the
5663+
// deduced argument and place it on the argument pack. Note that we
5664+
// stay on the same template parameter so that we can deduce more
5665+
// arguments.
5666+
SugaredArgumentPack.push_back(SugaredConverted.pop_back_val());
5667+
CanonicalArgumentPack.push_back(CanonicalConverted.pop_back_val());
5668+
} else {
5669+
// Move to the next template parameter.
5670+
++Param;
5671+
}
56535672
continue;
56545673
}
56555674

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2696,9 +2696,6 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
26962696
if (FoldPackParameter && hasPackExpansionBeforeEnd(Ps))
26972697
return TemplateDeductionResult::Success;
26982698

2699-
if (FoldPackArgument && hasPackExpansionBeforeEnd(As))
2700-
return TemplateDeductionResult::Success;
2701-
27022699
// C++0x [temp.deduct.type]p9:
27032700
// If P has a form that contains <T> or <i>, then each argument Pi of the
27042701
// respective template argument list P is compared with the corresponding
@@ -3400,36 +3397,61 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction(
34003397
return Result;
34013398

34023399
// Check that we produced the correct argument list.
3403-
TemplateParameterList *TemplateParams = Template->getTemplateParameters();
3404-
auto isSame = [&](unsigned I, const TemplateArgument &P,
3405-
const TemplateArgument &A) {
3406-
if (isSameTemplateArg(S.Context, P, A, PartialOrdering,
3407-
/*PackExpansionMatchesPack=*/true))
3408-
return true;
3409-
Info.Param = makeTemplateParameter(TemplateParams->getParam(I));
3410-
Info.FirstArg = P;
3411-
Info.SecondArg = A;
3412-
return false;
3413-
};
3414-
for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) {
3415-
const TemplateArgument &P = TemplateArgs[I];
3416-
if (P.isPackExpansion()) {
3417-
assert(I == TemplateArgs.size() - 1);
3418-
for (/**/; I != E; ++I) {
3419-
const TemplateArgument &A = CanonicalBuilder[I];
3420-
if (A.getKind() == TemplateArgument::Pack) {
3421-
for (const TemplateArgument &Ai : A.getPackAsArray())
3422-
if (!isSame(I, P, Ai))
3423-
return TemplateDeductionResult::NonDeducedMismatch;
3424-
} else if (!isSame(I, P, A)) {
3425-
return TemplateDeductionResult::NonDeducedMismatch;
3400+
SmallVector<ArrayRef<TemplateArgument>, 4> PsStack{TemplateArgs},
3401+
AsStack{CanonicalBuilder};
3402+
for (;;) {
3403+
auto take = [](SmallVectorImpl<ArrayRef<TemplateArgument>> &Stack)
3404+
-> std::tuple<ArrayRef<TemplateArgument> &, TemplateArgument> {
3405+
while (!Stack.empty()) {
3406+
auto &Xs = Stack.back();
3407+
if (Xs.empty()) {
3408+
Stack.pop_back();
3409+
continue;
34263410
}
3411+
auto &X = Xs.front();
3412+
if (X.getKind() == TemplateArgument::Pack) {
3413+
Stack.emplace_back(X.getPackAsArray());
3414+
Xs = Xs.drop_front();
3415+
continue;
3416+
}
3417+
assert(!X.isNull());
3418+
return {Xs, X};
34273419
}
3420+
static constexpr ArrayRef<TemplateArgument> None;
3421+
return {const_cast<ArrayRef<TemplateArgument> &>(None),
3422+
TemplateArgument()};
3423+
};
3424+
auto [Ps, P] = take(PsStack);
3425+
auto [As, A] = take(AsStack);
3426+
if (P.isNull() && A.isNull())
34283427
break;
3428+
TemplateArgument PP = P.isPackExpansion() ? P.getPackExpansionPattern() : P,
3429+
PA = A.isPackExpansion() ? A.getPackExpansionPattern() : A;
3430+
if (!isSameTemplateArg(S.Context, PP, PA, /*PartialOrdering=*/false)) {
3431+
if (!P.isPackExpansion() && !A.isPackExpansion()) {
3432+
Info.Param =
3433+
makeTemplateParameter(Template->getTemplateParameters()->getParam(
3434+
(PsStack.empty() ? TemplateArgs.end()
3435+
: PsStack.front().begin()) -
3436+
TemplateArgs.begin()));
3437+
Info.FirstArg = P;
3438+
Info.SecondArg = A;
3439+
return TemplateDeductionResult::NonDeducedMismatch;
3440+
}
3441+
if (P.isPackExpansion()) {
3442+
Ps = Ps.drop_front();
3443+
continue;
3444+
}
3445+
if (A.isPackExpansion()) {
3446+
As = As.drop_front();
3447+
continue;
3448+
}
34293449
}
3430-
if (!isSame(I, P, CanonicalBuilder[I]))
3431-
return TemplateDeductionResult::NonDeducedMismatch;
3450+
Ps = Ps.drop_front(P.isPackExpansion() ? 0 : 1);
3451+
As = As.drop_front(A.isPackExpansion() && !P.isPackExpansion() ? 0 : 1);
34323452
}
3453+
assert(PsStack.empty());
3454+
assert(AsStack.empty());
34333455

34343456
if (!PartialOrdering) {
34353457
if (auto Result = CheckDeducedArgumentConstraints(

clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,31 +18,39 @@ eval<D<int, 17>> eD; // expected-error{{implicit instantiation of undefined temp
1818
eval<E<int, float>> eE; // expected-error{{implicit instantiation of undefined template 'eval<E<int, float>>}}
1919

2020
template<
21-
template <int ...N> // expected-error{{deduced non-type template argument does not have the same type as the corresponding template parameter ('int' vs 'long')}}
21+
template <int ...N> // expected-error{{deduced non-type template argument does not have the same type as the corresponding template parameter ('int' vs 'void *')}}
2222
class TT // expected-note {{previous template template parameter is here}}
2323
> struct X0 { };
2424

2525
template<int I, int J, int ...Rest> struct X0a;
2626
template<int ...Rest> struct X0b;
27-
template<int I, long J> struct X0c; // expected-note{{template parameter is declared here}}
27+
template<int I, long J> struct X0c;
28+
template<int I, short J> struct X0d;
29+
template<int I, void *J> struct X0e; // expected-note{{template parameter is declared here}}
2830

2931
X0<X0a> inst_x0a;
3032
X0<X0b> inst_x0b;
31-
X0<X0c> inst_x0c; // expected-note{{template template argument has different template parameters than its corresponding template template parameter}}
33+
X0<X0c> inst_x0c;
34+
X0<X0d> inst_x0d;
35+
X0<X0e> inst_x0e; // expected-note{{template template argument has different template parameters than its corresponding template template parameter}}
3236

3337
template<typename T,
34-
template <T ...N> // expected-error{{deduced non-type template argument does not have the same type as the corresponding template parameter ('short' vs 'long')}}
38+
template <T ...N> // expected-error{{deduced non-type template argument does not have the same type as the corresponding template parameter ('short' vs 'void *')}}
3539
class TT // expected-note {{previous template template parameter is here}}
3640
> struct X1 { };
41+
3742
template<int I, int J, int ...Rest> struct X1a;
3843
template<long I, long ...Rest> struct X1b;
3944
template<short I, short J> struct X1c;
40-
template<short I, long J> struct X1d; // expected-note{{template parameter is declared here}}
45+
template<short I, long J> struct X1d;
46+
template<short I, void *J> struct X1e; // expected-note{{template parameter is declared here}}
4147

4248
X1<int, X1a> inst_x1a;
4349
X1<long, X1b> inst_x1b;
4450
X1<short, X1c> inst_x1c;
45-
X1<short, X1d> inst_x1d; // expected-note{{template template argument has different template parameters than its corresponding template template parameter}}
51+
X1<short, X1d> inst_sx1d;
52+
X1<int, X1d> inst_ix1d;
53+
X1<short, X1e> inst_x1e; // expected-note{{template template argument has different template parameters than its corresponding template template parameter}}
4654

4755
template <int> class X2; // expected-note{{template is declared here}} \
4856
// expected-note{{template is declared here}}

clang/test/SemaTemplate/cwg2398.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,3 +618,33 @@ namespace regression2 {
618618
template <typename, int> struct Matrix;
619619
template struct D<Matrix<double, 3>>;
620620
} // namespace regression2
621+
622+
namespace nttp_auto {
623+
namespace t1 {
624+
template <template <auto... Va> class TT> struct A {};
625+
template <int Vi, short Vs> struct B;
626+
template struct A<B>;
627+
} // namespace t1
628+
namespace t2 {
629+
// FIXME: Shouldn't accept parameters after a parameter pack.
630+
template<template<auto... Va1, auto Va2> class> struct A {};
631+
// new-error@-1 {{deduced non-type template argument does not have the same type as the corresponding template parameter ('auto' vs 'int')}}
632+
// expected-note@-2 {{previous template template parameter is here}}
633+
template<int... Vi> struct B;
634+
// new-note@-1 {{template parameter is declared here}}
635+
// old-note@-2 {{too few template parameters}}
636+
template struct A<B>;
637+
// new-note@-1 {{different template parameters}}
638+
// old-error@-2 {{different template parameters}}
639+
} // namespace t2
640+
namespace t3 {
641+
// FIXME: Shouldn't accept parameters after a parameter pack.
642+
template<template<auto... Va1, auto... Va2> class> struct A {};
643+
// new-error@-1 {{deduced non-type template argument does not have the same type as the corresponding template parameter ('auto' vs 'int')}}
644+
// new-note@-2 {{previous template template parameter is here}}
645+
template<int... Vi> struct B;
646+
// new-note@-1 {{template parameter is declared here}}
647+
template struct A<B>;
648+
// new-note@-1 {{different template parameters}}
649+
} // namespace t3
650+
} // namespace nttp_auto

clang/test/SemaTemplate/temp_arg_template_p0522.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ namespace IntPackParam {
5050
using ok_compat = Pt<TPi<i>, TPi<iDi>, TPi<ii>, TPi<iiPi>>;
5151
using err1 = TPi<t0>; // expected-error@#TPi {{template argument for template type parameter must be a type}}
5252
// expected-note@-1 {{different template parameters}}
53-
using err2 = TPi<iDt>; // expected-error@#iDt {{could not match 'type-parameter-0-1' against}}
53+
using err2 = TPi<iDt>; // expected-error@#TPi {{template argument for template type parameter must be a type}}
5454
// expected-note@-1 {{different template parameters}}
55-
using err3 = TPi<it>; // expected-error@#it {{could not match 'type-parameter-0-1' against}}
55+
using err3 = TPi<it>; // expected-error@#TPi {{template argument for template type parameter must be a type}}
5656
// expected-note@-1 {{different template parameters}}
5757
}
5858

0 commit comments

Comments
 (0)