llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Matheus Izvekov (mizvekov) <details> <summary>Changes</summary> 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. --- Full diff: https://github.com/llvm/llvm-project/pull/90820.diff 4 Files Affected: - (modified) clang/include/clang/Sema/Sema.h (+3-2) - (modified) clang/lib/Sema/SemaTemplate.cpp (+6-4) - (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+59-20) - (modified) clang/test/SemaTemplate/cwg2398.cpp (-6) ``````````diff diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index a80ac6dbc76137..2bea8732f61fcd 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9238,7 +9238,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 = {}); @@ -9708,7 +9708,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 989f3995ca5991..2c921d4365db0a 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -6384,7 +6384,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()); @@ -8296,7 +8297,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) { @@ -8348,8 +8350,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 9f9e4422827173..4fbaecd7dba960 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -145,7 +145,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, ArrayRef<TemplateArgument> As, TemplateDeductionInfo &Info, SmallVectorImpl<DeducedTemplateArgument> &Deduced, - bool NumberOfArgumentsMustMatch); + bool NumberOfArgumentsMustMatch, bool Swapped); static void MarkUsedTemplateParameters(ASTContext &Ctx, const TemplateArgument &TemplateArg, @@ -703,9 +703,9 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams, // Perform template argument deduction on each template // argument. Ignore any missing/extra arguments, since they could be // filled in by default arguments. - return DeduceTemplateArguments(S, TemplateParams, PResolved, - SA->template_arguments(), Info, Deduced, - /*NumberOfArgumentsMustMatch=*/false); + return DeduceTemplateArguments( + S, TemplateParams, PResolved, SA->template_arguments(), Info, Deduced, + /*NumberOfArgumentsMustMatch=*/false, /*Swapped=*/false); } // If the argument type is a class template specialization, we @@ -731,7 +731,8 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams, // Perform template argument deduction for the template arguments. return DeduceTemplateArguments(S, TemplateParams, PResolved, SA->getTemplateArgs().asArray(), Info, Deduced, - /*NumberOfArgumentsMustMatch=*/true); + /*NumberOfArgumentsMustMatch=*/true, + /*Swapped=*/false); } static bool IsPossiblyOpaquelyQualifiedTypeInternal(const Type *T) { @@ -2552,7 +2553,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, ArrayRef<TemplateArgument> As, TemplateDeductionInfo &Info, SmallVectorImpl<DeducedTemplateArgument> &Deduced, - bool NumberOfArgumentsMustMatch) { + bool NumberOfArgumentsMustMatch, bool Swapped) { // 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 @@ -2583,8 +2584,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 (Swapped) + std::swap(Pi, Ai); + if (auto Result = + DeduceTemplateArguments(S, TemplateParams, Pi, Ai, Info, Deduced); Result != TemplateDeductionResult::Success) return Result; @@ -2611,9 +2615,12 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, for (; hasTemplateArgumentForDeduction(As, ArgIdx) && PackScope.hasNextElement(); ++ArgIdx) { + TemplateArgument Pi = Pattern, Ai = As[ArgIdx]; + if (Swapped) + 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; @@ -2636,7 +2643,8 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( SmallVectorImpl<DeducedTemplateArgument> &Deduced, bool NumberOfArgumentsMustMatch) { return ::DeduceTemplateArguments(*this, TemplateParams, Ps, As, Info, Deduced, - NumberOfArgumentsMustMatch); + NumberOfArgumentsMustMatch, + /*Swapped=*/false); } /// Determine whether two template arguments are the same. @@ -3272,7 +3280,7 @@ DeduceTemplateArguments(Sema &S, T *Partial, if (TemplateDeductionResult Result = ::DeduceTemplateArguments( S, Partial->getTemplateParameters(), Partial->getTemplateArgs().asArray(), TemplateArgs, Info, Deduced, - /*NumberOfArgumentsMustMatch=*/false); + /*NumberOfArgumentsMustMatch=*/false, /*Swapped=*/false); Result != TemplateDeductionResult::Success) return Result; @@ -6187,7 +6195,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 @@ -6197,11 +6206,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 @@ -6242,14 +6250,45 @@ 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 specific rules in [temp.deduct.type]p9 + // regarding how the argument lists themselves are matched is swapped + // between P and A. + // Note: The roles of the elements themselves are not swapped. + ArrayRef<TemplateArgument> Ps = AArgs, As = PArgs; + if (IsDeduced) + std::swap(Ps, As); + if (::DeduceTemplateArguments(*this, A, Ps, As, Info, Deduced, + /*NumberOfArgumentsMustMatch=*/false, + /*Swapped=*/IsDeduced) != + 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 a20155486b123d..9f1c6baa0a5a1f 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@-1 {{partial specialization matches}} template<template<class T5 > class TT2, class T6> struct A<TT2<T6>> {}; - // new-note@-1 {{partial specialization matches}} template<class T1> struct B; template struct A<B<char>>; - // new-error@-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@-1 {{partial specialization matches}} template<template<class T5 > class TT2, class ...T6> struct A<TT2<T6...>> {}; - // new-note@-1 {{partial specialization matches}} template<class T1> struct B; template struct A<B<char>>; - // new-error@-1 {{ambiguous partial specialization}} } // namespace type_pack2 namespace type_pack3 { `````````` </details> https://github.com/llvm/llvm-project/pull/90820 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits