Skip to content

Commit

Permalink
[clang] Handle template argument conversions for non-pack param to pa…
Browse files Browse the repository at this point in the history
…ck argument (llvm#110963)

This fixes a regression introduced in llvm#96023, reported in
llvm#110231 (comment)
  • Loading branch information
mizvekov authored and xgupta committed Oct 4, 2024
1 parent 2aa341b commit 5beaea7
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 45 deletions.
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>);

0 comments on commit 5beaea7

Please sign in to comment.