[clang] [clang] Implement provisional wording for CWG2398 regarding packs (PR #90820)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Wed May 15 17:50:47 PDT 2024
https://github.com/mizvekov updated https://github.com/llvm/llvm-project/pull/90820
>From c4b72afa655c0e35005dca8aea18e651189f8938 Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Wed, 1 May 2024 22:29:45 -0300
Subject: [PATCH] [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.
---
clang/include/clang/Sema/Sema.h | 5 +-
clang/lib/Sema/SemaTemplate.cpp | 10 ++--
clang/lib/Sema/SemaTemplateDeduction.cpp | 67 +++++++++++++++++++-----
clang/test/SemaTemplate/cwg2398.cpp | 24 ++++++---
4 files changed, 80 insertions(+), 26 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 6a414aa57f32b..7d7eb6c559110 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9133,7 +9133,7 @@ class Sema final : public SemaBase {
CheckTemplateArgumentKind CTAK);
bool CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
TemplateParameterList *Params,
- TemplateArgumentLoc &Arg);
+ TemplateArgumentLoc &Arg, bool IsDeduced);
void NoteTemplateLocation(const NamedDecl &Decl,
std::optional<SourceRange> ParamRange = {});
@@ -9603,7 +9603,8 @@ class Sema final : public SemaBase {
sema::TemplateDeductionInfo &Info);
bool isTemplateTemplateParameterAtLeastAsSpecializedAs(
- TemplateParameterList *PParam, TemplateDecl *AArg, SourceLocation Loc);
+ TemplateParameterList *PParam, TemplateDecl *AArg, SourceLocation Loc,
+ bool IsDeduced);
void MarkUsedTemplateParameters(const Expr *E, bool OnlyDeduced,
unsigned Depth, llvm::SmallBitVector &Used);
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index c7aac068e264b..116d1ac077b29 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -6484,7 +6484,8 @@ bool Sema::CheckTemplateArgument(
case TemplateArgument::Template:
case TemplateArgument::TemplateExpansion:
- if (CheckTemplateTemplateArgument(TempParm, Params, Arg))
+ if (CheckTemplateTemplateArgument(TempParm, Params, Arg,
+ /*IsDeduced=*/CTAK != CTAK_Specified))
return true;
SugaredConverted.push_back(Arg.getArgument());
@@ -8402,7 +8403,8 @@ static void DiagnoseTemplateParameterListArityMismatch(
/// It returns true if an error occurred, and false otherwise.
bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
TemplateParameterList *Params,
- TemplateArgumentLoc &Arg) {
+ TemplateArgumentLoc &Arg,
+ bool IsDeduced) {
TemplateName Name = Arg.getArgument().getAsTemplateOrTemplatePattern();
TemplateDecl *Template = Name.getAsTemplateDecl();
if (!Template) {
@@ -8454,8 +8456,8 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
!Template->hasAssociatedConstraints())
return false;
- if (isTemplateTemplateParameterAtLeastAsSpecializedAs(Params, Template,
- Arg.getLocation())) {
+ if (isTemplateTemplateParameterAtLeastAsSpecializedAs(
+ Params, Template, Arg.getLocation(), IsDeduced)) {
// P2113
// C++20[temp.func.order]p2
// [...] If both deductions succeed, the partial ordering selects the
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index b5d405111fe4c..c8319b8a4bc51 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -139,13 +139,15 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
SmallVectorImpl<DeducedTemplateArgument> &Deduced, unsigned TDF,
bool PartialOrdering = false, bool DeducedFromArrayBound = false);
+enum class PackFold { ParameterToArgument, ArgumentToParameter };
static TemplateDeductionResult
DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
ArrayRef<TemplateArgument> Ps,
ArrayRef<TemplateArgument> As,
TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
- bool NumberOfArgumentsMustMatch);
+ bool NumberOfArgumentsMustMatch,
+ PackFold PackFold = PackFold::ParameterToArgument);
static void MarkUsedTemplateParameters(ASTContext &Ctx,
const TemplateArgument &TemplateArg,
@@ -2550,7 +2552,9 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
ArrayRef<TemplateArgument> As,
TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
- bool NumberOfArgumentsMustMatch) {
+ bool NumberOfArgumentsMustMatch, PackFold PackFold) {
+ if (PackFold == PackFold::ArgumentToParameter)
+ std::swap(Ps, As);
// C++0x [temp.deduct.type]p9:
// If the template argument list of P contains a pack expansion that is not
// the last template argument, the entire template argument list is a
@@ -2581,8 +2585,11 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
return TemplateDeductionResult::MiscellaneousDeductionFailure;
// Perform deduction for this Pi/Ai pair.
- if (auto Result = DeduceTemplateArguments(S, TemplateParams, P,
- As[ArgIdx], Info, Deduced);
+ TemplateArgument Pi = P, Ai = As[ArgIdx];
+ if (PackFold == PackFold::ArgumentToParameter)
+ std::swap(Pi, Ai);
+ if (auto Result =
+ DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, Deduced);
Result != TemplateDeductionResult::Success)
return Result;
@@ -2609,9 +2616,12 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
for (; hasTemplateArgumentForDeduction(As, ArgIdx) &&
PackScope.hasNextElement();
++ArgIdx) {
+ TemplateArgument Pi = Pattern, Ai = As[ArgIdx];
+ if (PackFold == PackFold::ArgumentToParameter)
+ std::swap(Pi, Ai);
// Deduce template arguments from the pattern.
- if (auto Result = DeduceTemplateArguments(S, TemplateParams, Pattern,
- As[ArgIdx], Info, Deduced);
+ if (auto Result =
+ DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, Deduced);
Result != TemplateDeductionResult::Success)
return Result;
@@ -6213,7 +6223,8 @@ bool Sema::isMoreSpecializedThanPrimary(
}
bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
- TemplateParameterList *P, TemplateDecl *AArg, SourceLocation Loc) {
+ TemplateParameterList *P, TemplateDecl *AArg, SourceLocation Loc,
+ bool IsDeduced) {
// C++1z [temp.arg.template]p4: (DR 150)
// A template template-parameter P is at least as specialized as a
// template template-argument A if, given the following rewrite to two
@@ -6223,11 +6234,10 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
// equivalent partial ordering by performing deduction directly on
// the template parameter lists of the template template parameters.
//
- // Given an invented class template X with the template parameter list of
- // A (including default arguments):
- TemplateName X = Context.getCanonicalTemplateName(TemplateName(AArg));
TemplateParameterList *A = AArg->getTemplateParameters();
+ // Given an invented class template X with the template parameter list of
+ // A (including default arguments):
// - Each function template has a single function parameter whose type is
// a specialization of X with template arguments corresponding to the
// template parameters from the respective function template
@@ -6270,14 +6280,43 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
return false;
}
- QualType AType = Context.getCanonicalTemplateSpecializationType(X, AArgs);
- QualType PType = Context.getCanonicalTemplateSpecializationType(X, PArgs);
+ // Determine whether P1 is at least as specialized as P2.
+ TemplateDeductionInfo Info(Loc, A->getDepth());
+ SmallVector<DeducedTemplateArgument, 4> Deduced;
+ Deduced.resize(A->size());
// ... the function template corresponding to P is at least as specialized
// as the function template corresponding to A according to the partial
// ordering rules for function templates.
- TemplateDeductionInfo Info(Loc, A->getDepth());
- return isAtLeastAsSpecializedAs(*this, PType, AType, AArg, Info);
+
+ // Provisional resolution for CWG2398: Regarding temp.arg.template]p4, when
+ // applying the partial ordering rules for function templates on
+ // the rewritten template template parameters:
+ // - In a deduced context, the matching of packs versus fixed-size needs to
+ // be inverted between Ps and As. On non-deduced context, matching needs to
+ // happen both ways, according to [temp.arg.template]p3, but this is
+ // currently implemented as a special case elsewhere.
+ if (::DeduceTemplateArguments(*this, A, AArgs, PArgs, Info, Deduced,
+ /*NumberOfArgumentsMustMatch=*/false,
+ IsDeduced ? PackFold::ArgumentToParameter
+ : PackFold::ParameterToArgument) !=
+ TemplateDeductionResult::Success)
+ return false;
+
+ SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end());
+ Sema::InstantiatingTemplate Inst(*this, Info.getLocation(), AArg, DeducedArgs,
+ Info);
+ if (Inst.isInvalid())
+ return false;
+
+ bool AtLeastAsSpecialized;
+ runWithSufficientStackSpace(Info.getLocation(), [&] {
+ AtLeastAsSpecialized =
+ ::FinishTemplateArgumentDeduction(
+ *this, AArg, /*IsPartialOrdering=*/true, PArgs, Deduced, Info) ==
+ TemplateDeductionResult::Success;
+ });
+ return AtLeastAsSpecialized;
}
namespace {
diff --git a/clang/test/SemaTemplate/cwg2398.cpp b/clang/test/SemaTemplate/cwg2398.cpp
index a20155486b123..937edf8b5f2a0 100644
--- a/clang/test/SemaTemplate/cwg2398.cpp
+++ b/clang/test/SemaTemplate/cwg2398.cpp
@@ -62,25 +62,19 @@ namespace templ {
namespace type_pack1 {
template<class T2> struct A;
template<template<class ...T3s> class TT1, class T4> struct A<TT1<T4>> ;
- // new-note at -1 {{partial specialization matches}}
template<template<class T5 > class TT2, class T6> struct A<TT2<T6>> {};
- // new-note at -1 {{partial specialization matches}}
template<class T1> struct B;
template struct A<B<char>>;
- // new-error at -1 {{ambiguous partial specialization}}
} // namespace type_pack1
namespace type_pack2 {
template<class T2> struct A;
template<template<class ...T3s> class TT1, class ...T4> struct A<TT1<T4...>> ;
- // new-note at -1 {{partial specialization matches}}
template<template<class T5 > class TT2, class ...T6> struct A<TT2<T6...>> {};
- // new-note at -1 {{partial specialization matches}}
template<class T1> struct B;
template struct A<B<char>>;
- // new-error at -1 {{ambiguous partial specialization}}
} // namespace type_pack2
namespace type_pack3 {
@@ -137,3 +131,21 @@ namespace ttp_defaults {
// old-error at -2 {{template template argument has different template parameters}}
// old-error at -3 {{explicit instantiation of 'f' does not refer to a function template}}
} // namespace ttp_defaults
+
+namespace ttp_only {
+ template <template <class... > class TT1> struct A { static constexpr int V = 0; };
+ template <template <class > class TT2> struct A<TT2> { static constexpr int V = 1; };
+ // new-note at -1 {{partial specialization matches}}
+ template <template <class, class> class TT3> struct A<TT3> { static constexpr int V = 2; };
+ // new-note at -1 {{partial specialization matches}}
+
+ template <class ... > struct B;
+ template <class > struct C;
+ template <class, class > struct D;
+ template <class, class, class> struct E;
+
+ static_assert(A<B>::V == 0); // new-error {{ambiguous partial specializations}}
+ static_assert(A<C>::V == 1);
+ static_assert(A<D>::V == 2);
+ static_assert(A<E>::V == 0);
+} // namespace ttp_only
More information about the cfe-commits
mailing list