Skip to content

Commit c4b72af

Browse files
committed
[clang] Implement provisional wording for CWG2398 regarding packs
This solves some ambuguity introduced in P0522 regarding how template template parameters are partially ordered, and should reduce the negative impact of enabling `-frelaxed-template-template-args` by default. When performing template argument deduction, a template template parameter containing no packs should be more specialized than one that does. Given the following example: ```C++ template<class T2> struct A; template<template<class ...T3s> class TT1, class T4> struct A<TT1<T4>>; // #1 template<template<class T5 > class TT2, class T6> struct A<TT2<T6>>; // #2 template<class T1> struct B; template struct A<B<char>>; ``` Prior to P0522, candidate #2 would be more specialized. After P0522, neither is more specialized, so this becomes ambiguous. With this change, #2 becomes more specialized again, maintaining compatibility with pre-P0522 implementations. The problem is that in P0522, candidates are at least as specialized when matching packs to fixed-size lists both ways, whereas before, a fixed-size list is more specialized. This patch keeps the original behavior when checking template arguments outside deduction, but restores this aspect of pre-P0522 matching during deduction. --- Since this changes provisional implementation of CWG2398 which has not been released yet, and already contains a changelog entry, we don't provide a changelog entry here.
1 parent 5bbb63b commit c4b72af

File tree

4 files changed

+80
-26
lines changed

4 files changed

+80
-26
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9133,7 +9133,7 @@ class Sema final : public SemaBase {
91339133
CheckTemplateArgumentKind CTAK);
91349134
bool CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
91359135
TemplateParameterList *Params,
9136-
TemplateArgumentLoc &Arg);
9136+
TemplateArgumentLoc &Arg, bool IsDeduced);
91379137

91389138
void NoteTemplateLocation(const NamedDecl &Decl,
91399139
std::optional<SourceRange> ParamRange = {});
@@ -9603,7 +9603,8 @@ class Sema final : public SemaBase {
96039603
sema::TemplateDeductionInfo &Info);
96049604

96059605
bool isTemplateTemplateParameterAtLeastAsSpecializedAs(
9606-
TemplateParameterList *PParam, TemplateDecl *AArg, SourceLocation Loc);
9606+
TemplateParameterList *PParam, TemplateDecl *AArg, SourceLocation Loc,
9607+
bool IsDeduced);
96079608

96089609
void MarkUsedTemplateParameters(const Expr *E, bool OnlyDeduced,
96099610
unsigned Depth, llvm::SmallBitVector &Used);

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6484,7 +6484,8 @@ bool Sema::CheckTemplateArgument(
64846484

64856485
case TemplateArgument::Template:
64866486
case TemplateArgument::TemplateExpansion:
6487-
if (CheckTemplateTemplateArgument(TempParm, Params, Arg))
6487+
if (CheckTemplateTemplateArgument(TempParm, Params, Arg,
6488+
/*IsDeduced=*/CTAK != CTAK_Specified))
64886489
return true;
64896490

64906491
SugaredConverted.push_back(Arg.getArgument());
@@ -8402,7 +8403,8 @@ static void DiagnoseTemplateParameterListArityMismatch(
84028403
/// It returns true if an error occurred, and false otherwise.
84038404
bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
84048405
TemplateParameterList *Params,
8405-
TemplateArgumentLoc &Arg) {
8406+
TemplateArgumentLoc &Arg,
8407+
bool IsDeduced) {
84068408
TemplateName Name = Arg.getArgument().getAsTemplateOrTemplatePattern();
84078409
TemplateDecl *Template = Name.getAsTemplateDecl();
84088410
if (!Template) {
@@ -8454,8 +8456,8 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
84548456
!Template->hasAssociatedConstraints())
84558457
return false;
84568458

8457-
if (isTemplateTemplateParameterAtLeastAsSpecializedAs(Params, Template,
8458-
Arg.getLocation())) {
8459+
if (isTemplateTemplateParameterAtLeastAsSpecializedAs(
8460+
Params, Template, Arg.getLocation(), IsDeduced)) {
84598461
// P2113
84608462
// C++20[temp.func.order]p2
84618463
// [...] If both deductions succeed, the partial ordering selects the

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,15 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
139139
SmallVectorImpl<DeducedTemplateArgument> &Deduced, unsigned TDF,
140140
bool PartialOrdering = false, bool DeducedFromArrayBound = false);
141141

142+
enum class PackFold { ParameterToArgument, ArgumentToParameter };
142143
static TemplateDeductionResult
143144
DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
144145
ArrayRef<TemplateArgument> Ps,
145146
ArrayRef<TemplateArgument> As,
146147
TemplateDeductionInfo &Info,
147148
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
148-
bool NumberOfArgumentsMustMatch);
149+
bool NumberOfArgumentsMustMatch,
150+
PackFold PackFold = PackFold::ParameterToArgument);
149151

150152
static void MarkUsedTemplateParameters(ASTContext &Ctx,
151153
const TemplateArgument &TemplateArg,
@@ -2550,7 +2552,9 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
25502552
ArrayRef<TemplateArgument> As,
25512553
TemplateDeductionInfo &Info,
25522554
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
2553-
bool NumberOfArgumentsMustMatch) {
2555+
bool NumberOfArgumentsMustMatch, PackFold PackFold) {
2556+
if (PackFold == PackFold::ArgumentToParameter)
2557+
std::swap(Ps, As);
25542558
// C++0x [temp.deduct.type]p9:
25552559
// If the template argument list of P contains a pack expansion that is not
25562560
// the last template argument, the entire template argument list is a
@@ -2581,8 +2585,11 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
25812585
return TemplateDeductionResult::MiscellaneousDeductionFailure;
25822586

25832587
// Perform deduction for this Pi/Ai pair.
2584-
if (auto Result = DeduceTemplateArguments(S, TemplateParams, P,
2585-
As[ArgIdx], Info, Deduced);
2588+
TemplateArgument Pi = P, Ai = As[ArgIdx];
2589+
if (PackFold == PackFold::ArgumentToParameter)
2590+
std::swap(Pi, Ai);
2591+
if (auto Result =
2592+
DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, Deduced);
25862593
Result != TemplateDeductionResult::Success)
25872594
return Result;
25882595

@@ -2609,9 +2616,12 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
26092616
for (; hasTemplateArgumentForDeduction(As, ArgIdx) &&
26102617
PackScope.hasNextElement();
26112618
++ArgIdx) {
2619+
TemplateArgument Pi = Pattern, Ai = As[ArgIdx];
2620+
if (PackFold == PackFold::ArgumentToParameter)
2621+
std::swap(Pi, Ai);
26122622
// Deduce template arguments from the pattern.
2613-
if (auto Result = DeduceTemplateArguments(S, TemplateParams, Pattern,
2614-
As[ArgIdx], Info, Deduced);
2623+
if (auto Result =
2624+
DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, Deduced);
26152625
Result != TemplateDeductionResult::Success)
26162626
return Result;
26172627

@@ -6213,7 +6223,8 @@ bool Sema::isMoreSpecializedThanPrimary(
62136223
}
62146224

62156225
bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
6216-
TemplateParameterList *P, TemplateDecl *AArg, SourceLocation Loc) {
6226+
TemplateParameterList *P, TemplateDecl *AArg, SourceLocation Loc,
6227+
bool IsDeduced) {
62176228
// C++1z [temp.arg.template]p4: (DR 150)
62186229
// A template template-parameter P is at least as specialized as a
62196230
// template template-argument A if, given the following rewrite to two
@@ -6223,11 +6234,10 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
62236234
// equivalent partial ordering by performing deduction directly on
62246235
// the template parameter lists of the template template parameters.
62256236
//
6226-
// Given an invented class template X with the template parameter list of
6227-
// A (including default arguments):
6228-
TemplateName X = Context.getCanonicalTemplateName(TemplateName(AArg));
62296237
TemplateParameterList *A = AArg->getTemplateParameters();
62306238

6239+
// Given an invented class template X with the template parameter list of
6240+
// A (including default arguments):
62316241
// - Each function template has a single function parameter whose type is
62326242
// a specialization of X with template arguments corresponding to the
62336243
// template parameters from the respective function template
@@ -6270,14 +6280,43 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
62706280
return false;
62716281
}
62726282

6273-
QualType AType = Context.getCanonicalTemplateSpecializationType(X, AArgs);
6274-
QualType PType = Context.getCanonicalTemplateSpecializationType(X, PArgs);
6283+
// Determine whether P1 is at least as specialized as P2.
6284+
TemplateDeductionInfo Info(Loc, A->getDepth());
6285+
SmallVector<DeducedTemplateArgument, 4> Deduced;
6286+
Deduced.resize(A->size());
62756287

62766288
// ... the function template corresponding to P is at least as specialized
62776289
// as the function template corresponding to A according to the partial
62786290
// ordering rules for function templates.
6279-
TemplateDeductionInfo Info(Loc, A->getDepth());
6280-
return isAtLeastAsSpecializedAs(*this, PType, AType, AArg, Info);
6291+
6292+
// Provisional resolution for CWG2398: Regarding temp.arg.template]p4, when
6293+
// applying the partial ordering rules for function templates on
6294+
// the rewritten template template parameters:
6295+
// - In a deduced context, the matching of packs versus fixed-size needs to
6296+
// be inverted between Ps and As. On non-deduced context, matching needs to
6297+
// happen both ways, according to [temp.arg.template]p3, but this is
6298+
// currently implemented as a special case elsewhere.
6299+
if (::DeduceTemplateArguments(*this, A, AArgs, PArgs, Info, Deduced,
6300+
/*NumberOfArgumentsMustMatch=*/false,
6301+
IsDeduced ? PackFold::ArgumentToParameter
6302+
: PackFold::ParameterToArgument) !=
6303+
TemplateDeductionResult::Success)
6304+
return false;
6305+
6306+
SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end());
6307+
Sema::InstantiatingTemplate Inst(*this, Info.getLocation(), AArg, DeducedArgs,
6308+
Info);
6309+
if (Inst.isInvalid())
6310+
return false;
6311+
6312+
bool AtLeastAsSpecialized;
6313+
runWithSufficientStackSpace(Info.getLocation(), [&] {
6314+
AtLeastAsSpecialized =
6315+
::FinishTemplateArgumentDeduction(
6316+
*this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info) ==
6317+
TemplateDeductionResult::Success;
6318+
});
6319+
return AtLeastAsSpecialized;
62816320
}
62826321

62836322
namespace {

clang/test/SemaTemplate/cwg2398.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,25 +62,19 @@ namespace templ {
6262
namespace type_pack1 {
6363
template<class T2> struct A;
6464
template<template<class ...T3s> class TT1, class T4> struct A<TT1<T4>> ;
65-
// new-note@-1 {{partial specialization matches}}
6665
template<template<class T5 > class TT2, class T6> struct A<TT2<T6>> {};
67-
// new-note@-1 {{partial specialization matches}}
6866

6967
template<class T1> struct B;
7068
template struct A<B<char>>;
71-
// new-error@-1 {{ambiguous partial specialization}}
7269
} // namespace type_pack1
7370

7471
namespace type_pack2 {
7572
template<class T2> struct A;
7673
template<template<class ...T3s> class TT1, class ...T4> struct A<TT1<T4...>> ;
77-
// new-note@-1 {{partial specialization matches}}
7874
template<template<class T5 > class TT2, class ...T6> struct A<TT2<T6...>> {};
79-
// new-note@-1 {{partial specialization matches}}
8075

8176
template<class T1> struct B;
8277
template struct A<B<char>>;
83-
// new-error@-1 {{ambiguous partial specialization}}
8478
} // namespace type_pack2
8579

8680
namespace type_pack3 {
@@ -137,3 +131,21 @@ namespace ttp_defaults {
137131
// old-error@-2 {{template template argument has different template parameters}}
138132
// old-error@-3 {{explicit instantiation of 'f' does not refer to a function template}}
139133
} // namespace ttp_defaults
134+
135+
namespace ttp_only {
136+
template <template <class... > class TT1> struct A { static constexpr int V = 0; };
137+
template <template <class > class TT2> struct A<TT2> { static constexpr int V = 1; };
138+
// new-note@-1 {{partial specialization matches}}
139+
template <template <class, class> class TT3> struct A<TT3> { static constexpr int V = 2; };
140+
// new-note@-1 {{partial specialization matches}}
141+
142+
template <class ... > struct B;
143+
template <class > struct C;
144+
template <class, class > struct D;
145+
template <class, class, class> struct E;
146+
147+
static_assert(A<B>::V == 0); // new-error {{ambiguous partial specializations}}
148+
static_assert(A<C>::V == 1);
149+
static_assert(A<D>::V == 2);
150+
static_assert(A<E>::V == 0);
151+
} // namespace ttp_only

0 commit comments

Comments
 (0)