llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Vish (Ishaya) Abrams (vishvananda) <details> <summary>Changes</summary> Fixes #<!-- -->216092. During class template partial ordering, P3310's invented default arguments can make a partial with a trailing template-template parameter pack appear more specialized than one whose template-template parameter exactly matches the argument template head. Prefer the exact match when each competing candidate either matches the template head or differs through a trailing template-template parameter pack. Keep the existing partial ordering for all other candidate sets. This preserves the `type_pack3` case in `clang/test/SemaTemplate/cwg2398.cpp`, where a template with a defaulted second parameter should select the variadic partial. The added classification runs only after more than one partial specialization has matched and only on the set that proceeds to partial ordering. The common zero-or-one-match path adds one size check. Candidate kinds use a stack-backed `SmallVector` with inline capacity four. ## Testing - Current `main` release build: `obj.clangSema` passes. - External C++11 reproducer: passes. - All 14 RUN configurations in `clang/test/CXX/drs/cwg14xx.cpp`: pass. - `clang/test/SemaTemplate/cwg2398.cpp`: passes. - Full `clang/test/SemaTemplate`: 322 passed, one existing XFAIL. - Combined lit run of the CWG test and full SemaTemplate directory: 323 passed, one existing XFAIL. - `git diff --check` and diff-only `clang-format`: clean. For the performance check, patched and unpatched release builds from the same base compiled 500 template instantiations over 20 interleaved runs per binary. Mean user CPU time was 0.314 s patched versus 0.316 s unpatched for the common one-match case, 0.162 s versus 0.164 s for an unaffected multi-match case, and 0.395 s versus 0.423 s for the bug case. No regression was observed. --- Full diff: https://github.com/llvm/llvm-project/pull/216113.diff 3 Files Affected: - (modified) clang/docs/ReleaseNotes.md (+5) - (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+78) - (modified) clang/test/CXX/drs/cwg14xx.cpp (+19) ``````````diff diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index e4a6f72f8fec5..0134d79070b2d 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -422,6 +422,11 @@ features cannot lower the translation-unit ABI level; #### Bug Fixes to C++ Support +- Clang now prefers a class template partial specialization whose + template-template parameter forms an exact match with the argument template + head when P3310's invented defaults would favor a partial with a trailing + parameter pack. (#GH216092) + - Fixed an issue where `__typeof__` incorrectly rejected cv-qualified function types. - Fixed a bug where top-level CV qualifiers (such as ``const``) were dropped from pointers modified by Microsoft pointer attributes (like ``__ptr32`` and ``__ptr64``) and WebAssembly's ``__funcref``. diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 93824b9ef9da7..2bb0b6367836b 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -3958,6 +3958,82 @@ namespace { ClassTemplatePartialSpecializationDecl *Partial; TemplateArgumentList *Args; }; + + enum class ActualTemplateTemplateMatchKind { Other, InexactPack, Exact }; + + static ActualTemplateTemplateMatchKind + getActualTemplateTemplateMatchKind(Sema &S, + const PartialSpecMatchResult &Match) { + bool HasTemplateTemplateParameter = false; + bool HasInexactPack = false; + TemplateParameterList *Params = Match.Partial->getTemplateParameters(); + for (unsigned I = 0; I != Params->size(); ++I) { + auto *TTP = dyn_cast<TemplateTemplateParmDecl>(Params->getParam(I)); + if (!TTP) + continue; + + HasTemplateTemplateParameter = true; + const TemplateArgument &Arg = Match.Args->get(I); + if (Arg.getKind() != TemplateArgument::Template && + Arg.getKind() != TemplateArgument::TemplateExpansion) + return ActualTemplateTemplateMatchKind::Other; + + TemplateName Name = Arg.getAsTemplateOrTemplatePattern() + .getTemplateDeclAndDefaultArgs() + .first; + TemplateDecl *Template = Name.getAsTemplateDecl(); + if (!Template) + return ActualTemplateTemplateMatchKind::Other; + if (S.TemplateParameterListsAreEqual( + Template->getTemplateParameters(), TTP->getTemplateParameters(), + /*Complain=*/false, Sema::TPL_TemplateParamsEquivalent)) + continue; + + TemplateParameterList *TTPParams = TTP->getTemplateParameters(); + if (TTPParams->empty() || + !TTPParams->getParam(TTPParams->size() - 1)->isParameterPack()) + return ActualTemplateTemplateMatchKind::Other; + HasInexactPack = true; + } + if (HasInexactPack) + return ActualTemplateTemplateMatchKind::InexactPack; + return HasTemplateTemplateParameter + ? ActualTemplateTemplateMatchKind::Exact + : ActualTemplateTemplateMatchKind::Other; + } + + static void preferExactTemplateTemplateMatches( + Sema &S, SmallVectorImpl<PartialSpecMatchResult> &Matches) { + SmallVector<ActualTemplateTemplateMatchKind, 4> MatchKinds; + bool HasExactMatch = false; + // Preserve ordinary partial ordering if any candidate has no + // template-template parameter or has a mismatch other than a trailing + // template-template parameter pack. + for (const PartialSpecMatchResult &Match : Matches) { + ActualTemplateTemplateMatchKind Kind = + getActualTemplateTemplateMatchKind(S, Match); + if (Kind == ActualTemplateTemplateMatchKind::Other) + return; + HasExactMatch |= Kind == ActualTemplateTemplateMatchKind::Exact; + MatchKinds.push_back(Kind); + } + if (!HasExactMatch) + return; + + // P3310's invented default arguments can make a variadic template-template + // parameter candidate appear more specialized than a candidate that + // exactly matches the actual template head. Preserve the exact match in + // that case. + unsigned Out = 0; + for (unsigned I = 0; I != Matches.size(); ++I) { + if (MatchKinds[I] != ActualTemplateTemplateMatchKind::Exact) + continue; + if (Out != I) + Matches[Out] = std::move(Matches[I]); + ++Out; + } + Matches.resize(Out); + } } bool Sema::usesPartialOrExplicitSpecialization( @@ -4056,6 +4132,8 @@ static ActionResult<CXXRecordDecl *> getPatternForClassTemplateSpecialization( } if (Matched.empty() && PrimaryStrictPackMatch) Matched = std::move(ExtraMatched); + if (Matched.size() > 1) + preferExactTemplateTemplateMatches(S, Matched); // If we're dealing with a member template where the template parameters // have been instantiated, this provides the original template parameters diff --git a/clang/test/CXX/drs/cwg14xx.cpp b/clang/test/CXX/drs/cwg14xx.cpp index f3b2bab8e7f3a..d9da4262b3c94 100644 --- a/clang/test/CXX/drs/cwg14xx.cpp +++ b/clang/test/CXX/drs/cwg14xx.cpp @@ -107,6 +107,25 @@ namespace cwg1432 { // cwg1432: 16 open 2022-11-11 f(A<0>()); } } // namespace function_template + namespace combined_template_template_and_template_id_packs { + template <class> + struct select; + + template <template <class, class...> class C, class A, class... Rest> + struct select<C<A, Rest...>> { + static const int value = 1; + }; + + template <template <class> class C, class A> + struct select<C<A>> { + static const int value = 2; + }; + + template <class> + struct unary {}; + + static_assert(select<unary<int>>::value == 2, ""); + } // namespace combined_template_template_and_template_id_packs #endif } // namespace cwg1432 `````````` </details> https://github.com/llvm/llvm-project/pull/216113 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
