Skip to content

Commit b7df9a3

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. 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, #2 would be the only viable candidate. After P0522, #1 is also viable, and neither is considered more specialized, so this becomes ambiguous. With this change, #2 is considered more specialized, so that is what is picked and we remain compatible with pre-P0522 implementations. The problem we solve here is that the specialization rules in `[temp.arg.template]/4` are defined in terms of a rewrite to function templates, but in the case of partial ordering, the rules on how P and A lists are matched to each other is swapped regarding how specialization makes sense conceptually. --- 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 b86e099 commit b7df9a3

File tree

4 files changed

+68
-32
lines changed

4 files changed

+68
-32
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9238,7 +9238,7 @@ class Sema final : public SemaBase {
92389238
CheckTemplateArgumentKind CTAK);
92399239
bool CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
92409240
TemplateParameterList *Params,
9241-
TemplateArgumentLoc &Arg);
9241+
TemplateArgumentLoc &Arg, bool IsDeduced);
92429242

92439243
void NoteTemplateLocation(const NamedDecl &Decl,
92449244
std::optional<SourceRange> ParamRange = {});
@@ -9708,7 +9708,8 @@ class Sema final : public SemaBase {
97089708
sema::TemplateDeductionInfo &Info);
97099709

97109710
bool isTemplateTemplateParameterAtLeastAsSpecializedAs(
9711-
TemplateParameterList *PParam, TemplateDecl *AArg, SourceLocation Loc);
9711+
TemplateParameterList *PParam, TemplateDecl *AArg, SourceLocation Loc,
9712+
bool IsDeduced);
97129713

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

clang/lib/Sema/SemaTemplate.cpp

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

63856385
case TemplateArgument::Template:
63866386
case TemplateArgument::TemplateExpansion:
6387-
if (CheckTemplateTemplateArgument(TempParm, Params, Arg))
6387+
if (CheckTemplateTemplateArgument(TempParm, Params, Arg,
6388+
/*IsDeduced=*/CTAK != CTAK_Specified))
63886389
return true;
63896390

63906391
SugaredConverted.push_back(Arg.getArgument());
@@ -8296,7 +8297,8 @@ static void DiagnoseTemplateParameterListArityMismatch(
82968297
/// It returns true if an error occurred, and false otherwise.
82978298
bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
82988299
TemplateParameterList *Params,
8299-
TemplateArgumentLoc &Arg) {
8300+
TemplateArgumentLoc &Arg,
8301+
bool IsDeduced) {
83008302
TemplateName Name = Arg.getArgument().getAsTemplateOrTemplatePattern();
83018303
TemplateDecl *Template = Name.getAsTemplateDecl();
83028304
if (!Template) {
@@ -8348,8 +8350,8 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
83488350
!Template->hasAssociatedConstraints())
83498351
return false;
83508352

8351-
if (isTemplateTemplateParameterAtLeastAsSpecializedAs(Params, Template,
8352-
Arg.getLocation())) {
8353+
if (isTemplateTemplateParameterAtLeastAsSpecializedAs(
8354+
Params, Template, Arg.getLocation(), IsDeduced)) {
83538355
// P2113
83548356
// C++20[temp.func.order]p2
83558357
// [...] If both deductions succeed, the partial ordering selects the

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
145145
ArrayRef<TemplateArgument> As,
146146
TemplateDeductionInfo &Info,
147147
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
148-
bool NumberOfArgumentsMustMatch);
148+
bool NumberOfArgumentsMustMatch, bool Swapped);
149149

150150
static void MarkUsedTemplateParameters(ASTContext &Ctx,
151151
const TemplateArgument &TemplateArg,
@@ -703,9 +703,9 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams,
703703
// Perform template argument deduction on each template
704704
// argument. Ignore any missing/extra arguments, since they could be
705705
// filled in by default arguments.
706-
return DeduceTemplateArguments(S, TemplateParams, PResolved,
707-
SA->template_arguments(), Info, Deduced,
708-
/*NumberOfArgumentsMustMatch=*/false);
706+
return DeduceTemplateArguments(
707+
S, TemplateParams, PResolved, SA->template_arguments(), Info, Deduced,
708+
/*NumberOfArgumentsMustMatch=*/false, /*Swapped=*/false);
709709
}
710710

711711
// If the argument type is a class template specialization, we
@@ -731,7 +731,8 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams,
731731
// Perform template argument deduction for the template arguments.
732732
return DeduceTemplateArguments(S, TemplateParams, PResolved,
733733
SA->getTemplateArgs().asArray(), Info, Deduced,
734-
/*NumberOfArgumentsMustMatch=*/true);
734+
/*NumberOfArgumentsMustMatch=*/true,
735+
/*Swapped=*/false);
735736
}
736737

737738
static bool IsPossiblyOpaquelyQualifiedTypeInternal(const Type *T) {
@@ -2552,7 +2553,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
25522553
ArrayRef<TemplateArgument> As,
25532554
TemplateDeductionInfo &Info,
25542555
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
2555-
bool NumberOfArgumentsMustMatch) {
2556+
bool NumberOfArgumentsMustMatch, bool Swapped) {
25562557
// C++0x [temp.deduct.type]p9:
25572558
// If the template argument list of P contains a pack expansion that is not
25582559
// the last template argument, the entire template argument list is a
@@ -2583,8 +2584,11 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
25832584
return TemplateDeductionResult::MiscellaneousDeductionFailure;
25842585

25852586
// Perform deduction for this Pi/Ai pair.
2586-
if (auto Result = DeduceTemplateArguments(S, TemplateParams, P,
2587-
As[ArgIdx], Info, Deduced);
2587+
TemplateArgument Pi = P, Ai = As[ArgIdx];
2588+
if (Swapped)
2589+
std::swap(Pi, Ai);
2590+
if (auto Result =
2591+
DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, Deduced);
25882592
Result != TemplateDeductionResult::Success)
25892593
return Result;
25902594

@@ -2611,9 +2615,12 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
26112615
for (; hasTemplateArgumentForDeduction(As, ArgIdx) &&
26122616
PackScope.hasNextElement();
26132617
++ArgIdx) {
2618+
TemplateArgument Pi = Pattern, Ai = As[ArgIdx];
2619+
if (Swapped)
2620+
std::swap(Pi, Ai);
26142621
// Deduce template arguments from the pattern.
2615-
if (auto Result = DeduceTemplateArguments(S, TemplateParams, Pattern,
2616-
As[ArgIdx], Info, Deduced);
2622+
if (auto Result =
2623+
DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, Deduced);
26172624
Result != TemplateDeductionResult::Success)
26182625
return Result;
26192626

@@ -2636,7 +2643,8 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
26362643
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
26372644
bool NumberOfArgumentsMustMatch) {
26382645
return ::DeduceTemplateArguments(*this, TemplateParams, Ps, As, Info, Deduced,
2639-
NumberOfArgumentsMustMatch);
2646+
NumberOfArgumentsMustMatch,
2647+
/*Swapped=*/false);
26402648
}
26412649

26422650
/// Determine whether two template arguments are the same.
@@ -3272,7 +3280,7 @@ DeduceTemplateArguments(Sema &S, T *Partial,
32723280
if (TemplateDeductionResult Result = ::DeduceTemplateArguments(
32733281
S, Partial->getTemplateParameters(),
32743282
Partial->getTemplateArgs().asArray(), TemplateArgs, Info, Deduced,
3275-
/*NumberOfArgumentsMustMatch=*/false);
3283+
/*NumberOfArgumentsMustMatch=*/false, /*Swapped=*/false);
32763284
Result != TemplateDeductionResult::Success)
32773285
return Result;
32783286

@@ -6187,7 +6195,8 @@ bool Sema::isMoreSpecializedThanPrimary(
61876195
}
61886196

61896197
bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
6190-
TemplateParameterList *P, TemplateDecl *AArg, SourceLocation Loc) {
6198+
TemplateParameterList *P, TemplateDecl *AArg, SourceLocation Loc,
6199+
bool IsDeduced) {
61916200
// C++1z [temp.arg.template]p4: (DR 150)
61926201
// A template template-parameter P is at least as specialized as a
61936202
// template template-argument A if, given the following rewrite to two
@@ -6197,11 +6206,10 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
61976206
// equivalent partial ordering by performing deduction directly on
61986207
// the template parameter lists of the template template parameters.
61996208
//
6200-
// Given an invented class template X with the template parameter list of
6201-
// A (including default arguments):
6202-
TemplateName X = Context.getCanonicalTemplateName(TemplateName(AArg));
62036209
TemplateParameterList *A = AArg->getTemplateParameters();
62046210

6211+
// Given an invented class template X with the template parameter list of
6212+
// A (including default arguments):
62056213
// - Each function template has a single function parameter whose type is
62066214
// a specialization of X with template arguments corresponding to the
62076215
// template parameters from the respective function template
@@ -6242,14 +6250,45 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
62426250
return false;
62436251
}
62446252

6245-
QualType AType = Context.getCanonicalTemplateSpecializationType(X, AArgs);
6246-
QualType PType = Context.getCanonicalTemplateSpecializationType(X, PArgs);
6253+
// Determine whether P1 is at least as specialized as P2.
6254+
TemplateDeductionInfo Info(Loc, A->getDepth());
6255+
SmallVector<DeducedTemplateArgument, 4> Deduced;
6256+
Deduced.resize(A->size());
62476257

62486258
// ... the function template corresponding to P is at least as specialized
62496259
// as the function template corresponding to A according to the partial
62506260
// ordering rules for function templates.
6251-
TemplateDeductionInfo Info(Loc, A->getDepth());
6252-
return isAtLeastAsSpecializedAs(*this, PType, AType, AArg, Info);
6261+
6262+
// Provisional resolution for CWG2398: Regarding temp.arg.template]p4, when
6263+
// applying the partial ordering rules for function templates on
6264+
// the rewritten template template parameters:
6265+
// - In a deduced context, the specific rules in [temp.deduct.type]p9
6266+
// regarding how the argument lists themselves are matched is swapped
6267+
// between P and A.
6268+
// Note: The roles of the elements themselves are not swapped.
6269+
ArrayRef<TemplateArgument> Ps = AArgs, As = PArgs;
6270+
if (IsDeduced)
6271+
std::swap(Ps, As);
6272+
if (::DeduceTemplateArguments(*this, A, Ps, As, Info, Deduced,
6273+
/*NumberOfArgumentsMustMatch=*/false,
6274+
/*Swapped=*/IsDeduced) !=
6275+
TemplateDeductionResult::Success)
6276+
return false;
6277+
6278+
SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end());
6279+
Sema::InstantiatingTemplate Inst(*this, Info.getLocation(), AArg, DeducedArgs,
6280+
Info);
6281+
if (Inst.isInvalid())
6282+
return false;
6283+
6284+
bool AtLeastAsSpecialized;
6285+
runWithSufficientStackSpace(Info.getLocation(), [&] {
6286+
AtLeastAsSpecialized =
6287+
::FinishTemplateArgumentDeduction(
6288+
*this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info) ==
6289+
TemplateDeductionResult::Success;
6290+
});
6291+
return AtLeastAsSpecialized;
62536292
}
62546293

62556294
namespace {

clang/test/SemaTemplate/cwg2398.cpp

Lines changed: 0 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 {

0 commit comments

Comments
 (0)