Skip to content

[clang] Handle template argument conversions for non-pack param to pack argument #110963

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5579,9 +5579,6 @@ bool Sema::CheckTemplateArgumentList(
return true;
}

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

if ((*Param)->isTemplateParameterPack()) {
// The template parameter was a template parameter pack, so take the
// deduced argument and place it on the argument pack. Note that we
Expand All @@ -5592,8 +5589,19 @@ bool Sema::CheckTemplateArgumentList(
} else {
// Move to the next template parameter.
++Param;
if (PartialOrderingTTP && PackExpansionIntoNonPack) {
// Keep converting the pattern in the argument against
// subsequent parameters. The argument is converted
// in place and will be added back when we are done.
SugaredConverted.pop_back();
CanonicalConverted.pop_back();
continue;
}
}

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

// If we just saw a pack expansion into a non-pack, then directly convert
// the remaining arguments, because we don't know what parameters they'll
// match up with.
Expand Down Expand Up @@ -5727,15 +5735,10 @@ bool Sema::CheckTemplateArgumentList(
// pack expansions; they might be empty. This can happen even if
// PartialTemplateArgs is false (the list of arguments is complete but
// still dependent).
if (PartialOrderingTTP ||
(CurrentInstantiationScope &&
CurrentInstantiationScope->getPartiallySubstitutedPack())) {
while (ArgIdx < NumArgs &&
NewArgs[ArgIdx].getArgument().isPackExpansion()) {
const TemplateArgument &Arg = NewArgs[ArgIdx++].getArgument();
SugaredConverted.push_back(Arg);
CanonicalConverted.push_back(Context.getCanonicalTemplateArgument(Arg));
}
while (ArgIdx < NumArgs && NewArgs[ArgIdx].getArgument().isPackExpansion()) {
const TemplateArgument &Arg = NewArgs[ArgIdx++].getArgument();
SugaredConverted.push_back(Arg);
CanonicalConverted.push_back(Context.getCanonicalTemplateArgument(Arg));
}

// If we have any leftover arguments, then there were too many arguments.
Expand Down
59 changes: 32 additions & 27 deletions clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3377,35 +3377,40 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction(
return Result;

// Check that we produced the correct argument list.
TemplateParameterList *TemplateParams = Template->getTemplateParameters();
auto isSame = [&](unsigned I, const TemplateArgument &P,
const TemplateArgument &A) {
if (isSameTemplateArg(S.Context, P, A, PartialOrdering,
/*PackExpansionMatchesPack=*/true))
return true;
Info.Param = makeTemplateParameter(TemplateParams->getParam(I));
for (ArrayRef<TemplateArgument> Ps = TemplateArgs, As = CanonicalBuilder;
!Ps.empty() && !As.empty();
/**/) {
TemplateArgument P = Ps.front(), A = As.front();
if (P.getKind() == TemplateArgument::Pack) {
assert(Ps.size() == 1 && "Pack not last element?");
Ps = P.getPackAsArray();
continue;
}
if (A.getKind() == TemplateArgument::Pack) {
assert(As.size() == 1 && "Pack not last element?");
As = A.getPackAsArray();
continue;
}

if (P.isPackExpansion())
P = P.getPackExpansionPattern();
else
Ps = Ps.drop_front();
if (A.isPackExpansion())
A = A.getPackExpansionPattern();
else
As = As.drop_front();

if (isSameTemplateArg(S.Context, P, A, PartialOrdering))
continue;
unsigned I = As.end() == CanonicalBuilder.end()
? As.begin() - CanonicalBuilder.begin()
: CanonicalBuilder.size() - 1;
Info.Param =
makeTemplateParameter(Template->getTemplateParameters()->getParam(I));
Info.FirstArg = P;
Info.SecondArg = A;
return false;
};
for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) {
const TemplateArgument &P = TemplateArgs[I];
if (P.isPackExpansion()) {
assert(I == TemplateArgs.size() - 1);
for (/**/; I != E; ++I) {
const TemplateArgument &A = CanonicalBuilder[I];
if (A.getKind() == TemplateArgument::Pack) {
for (const TemplateArgument &Ai : A.getPackAsArray())
if (!isSame(I, P, Ai))
return TemplateDeductionResult::NonDeducedMismatch;
} else if (!isSame(I, P, A)) {
return TemplateDeductionResult::NonDeducedMismatch;
}
}
break;
}
if (!isSame(I, P, CanonicalBuilder[I]))
return TemplateDeductionResult::NonDeducedMismatch;
return TemplateDeductionResult::NonDeducedMismatch;
}

if (!PartialOrdering) {
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CXX/temp/temp.arg/temp.arg.template/p3-0x.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ eval<D<int, 17>> eD; // expected-error{{implicit instantiation of undefined temp
eval<E<int, float>> eE; // expected-error{{implicit instantiation of undefined template 'eval<E<int, float>>}}

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

Expand All @@ -31,7 +31,7 @@ X0<X0b> inst_x0b;
X0<X0c> inst_x0c; // expected-note{{template template argument has different template parameters than its corresponding template template parameter}}

template<typename T,
template <T ...N> // expected-error{{deduced non-type template argument does not have the same type as the corresponding template parameter ('short' vs 'long')}}
template <T ...N> // expected-error{{deduced non-type template argument does not have the same type as the corresponding template parameter ('long' vs 'short')}}
class TT // expected-note {{previous template template parameter is here}}
> struct X1 { };
template<int I, int J, int ...Rest> struct X1a;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@ namespace PackExpansionNotAtEnd {
>::value? 1 : -1];

template<typename ... Types> struct UselessPartialSpec;
// expected-note@-1 {{template is declared here}}

// FIXME: We should simply disallow a pack expansion which is not at
// the end of the partial spec template argument list.
template<typename ... Types, // expected-note{{non-deducible template parameter 'Types'}}
typename Tail> // expected-note{{non-deducible template parameter 'Tail'}}
struct UselessPartialSpec<Types..., Tail>; // expected-error{{class template partial specialization contains template parameters that cannot be deduced; this partial specialization will never be used}}
// expected-error@-1 {{is not more specialized than the primary template}}
}

// When a pack expansion occurs within a template argument list, the entire
Expand Down
6 changes: 6 additions & 0 deletions clang/test/SemaTemplate/cwg2398.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -541,3 +541,9 @@ namespace regression2 {
template <typename, int> struct Matrix;
template struct D<Matrix<double, 3>>;
} // namespace regression2

namespace regression3 {
template <template <auto...> class TT> struct A {};
template <auto, int> struct B;
template struct A<B>;
} // namespace regression3
6 changes: 3 additions & 3 deletions clang/test/SemaTemplate/temp_arg_template_p0522.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ template<int, int> struct ii;
template<int...> struct Pi;
template<int, int, int...> struct iiPi;

template<int, typename = int> struct iDt; // #iDt
template<int, typename = int> struct iDt;
template<int, typename> struct it; // #it

template<typename T, T v> struct t0;
Expand Down Expand Up @@ -50,9 +50,9 @@ namespace IntPackParam {
using ok_compat = Pt<TPi<i>, TPi<iDi>, TPi<ii>, TPi<iiPi>>;
using err1 = TPi<t0>; // expected-error@#TPi {{template argument for template type parameter must be a type}}
// expected-note@-1 {{different template parameters}}
using err2 = TPi<iDt>; // expected-error@#iDt {{could not match 'type-parameter-0-1' against}}
using err2 = TPi<iDt>; // expected-error@#TPi {{template argument for template type parameter must be a type}}
// expected-note@-1 {{different template parameters}}
using err3 = TPi<it>; // expected-error@#it {{could not match 'type-parameter-0-1' against}}
using err3 = TPi<it>; // expected-error@#TPi {{template argument for template type parameter must be a type}}
// expected-note@-1 {{different template parameters}}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
#include <array>
#include <utility>

// expected-error-re@*:* {{{{could not match _Size against 'type-parameter-0-0'|different template parameters}}}}
// expected-error-re@*:* {{{{must be an expression|different template parameters}}}}
static_assert(!std::__is_specialization_v<std::pair<int, std::size_t>, std::array>);
Loading