https://github.com/mizvekov updated https://github.com/llvm/llvm-project/pull/92855
>From 73d456c632a1419c39316d38dcdc358b8e4f9636 Mon Sep 17 00:00:00 2001 From: Matheus Izvekov <mizve...@gmail.com> Date: Mon, 20 May 2024 01:15:03 -0300 Subject: [PATCH] [clang] Implement CWG2398 provisional TTP matching to class templates 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, we extend the provisional wording introduced in https://github.com/llvm/llvm-project/pull/89807 so it also covers deduction of class templates. Given the following example: ```C++ template <class T1, class T2 = float> struct A; template <class T3> struct B; template <template <class T4> class TT1, class T5> struct B<TT1<T5>>; // #1 template <class T6, class T7> struct B<A<T6, T7>>; // #2 template struct B<A<int>>; ``` Prior to P0522, `#2` was picked. Afterwards, this became ambiguous. This patch restores the pre-P0522 behavior, `#2` is picked again. This has the beneficial side effect of making the following code valid: ```C++ template<class T, class U> struct A {}; A<int, float> v; template<template<class> class TT> void f(TT<int>); // OK: TT picks 'float' as the default argument for the second parameter. void g() { f(v); } ``` --- 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/lib/Sema/SemaTemplate.cpp | 5 +- clang/lib/Sema/SemaTemplateDeduction.cpp | 76 +++++++++++-------- .../CXX/temp/temp.decls/temp.alias/p2.cpp | 5 +- clang/test/SemaTemplate/cwg2398.cpp | 3 - 4 files changed, 53 insertions(+), 36 deletions(-) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 39e9dbed0c3e0..268f079980a6c 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1807,6 +1807,8 @@ static void SetNestedNameSpecifier(Sema &S, TagDecl *T, // Returns the template parameter list with all default template argument // information. static TemplateParameterList *GetTemplateParameterList(TemplateDecl *TD) { + if (TD->isImplicit()) + return TD->getTemplateParameters(); // Make sure we get the template parameter list from the most // recent declaration, since that is the only one that is guaranteed to // have all the default template argument information. @@ -1827,7 +1829,8 @@ static TemplateParameterList *GetTemplateParameterList(TemplateDecl *TD) { // template <class = void> friend struct C; // }; // template struct S<int>; - while (D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None && + while ((D->isImplicit() || + D->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None) && D->getPreviousDecl()) D = D->getPreviousDecl(); return cast<TemplateDecl>(D)->getTemplateParameters(); diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index f16a07e1a1b34..76f47e9cb2560 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -527,8 +527,8 @@ static NamedDecl *getTemplateParameterWithDefault(Sema &S, NamedDecl *A, R->setDefaultArgument( S.Context, S.getTrivialTemplateArgumentLoc(Default, QualType(), SourceLocation())); - if (R->hasTypeConstraint()) { - auto *C = R->getTypeConstraint(); + if (T->hasTypeConstraint()) { + auto *C = T->getTypeConstraint(); R->setTypeConstraint(C->getConceptReference(), C->getImmediatelyDeclaredConstraint()); } @@ -583,37 +583,53 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, return TemplateDeductionResult::Success; auto NewDeduced = DeducedTemplateArgument(Arg); - // Provisional resolution for CWG2398: If Arg is also a template template - // param, and it names a template specialization, then we deduce a - // synthesized template template parameter based on A, but using the TS's - // arguments as defaults. - if (auto *TempArg = dyn_cast_or_null<TemplateTemplateParmDecl>( - Arg.getAsTemplateDecl())) { + // Provisional resolution for CWG2398: If Arg names a template + // specialization, then we deduce a synthesized template template parameter + // based on A, but using the TS's arguments as defaults. + if (DefaultArguments.size() != 0) { assert(Arg.getKind() == TemplateName::Template); - assert(!TempArg->isExpandedParameterPack()); - + TemplateDecl *TempArg = Arg.getAsTemplateDecl(); TemplateParameterList *As = TempArg->getTemplateParameters(); - if (DefaultArguments.size() != 0) { - assert(DefaultArguments.size() <= As->size()); - SmallVector<NamedDecl *, 4> Params(As->size()); - for (unsigned I = 0; I < DefaultArguments.size(); ++I) - Params[I] = getTemplateParameterWithDefault(S, As->getParam(I), - DefaultArguments[I]); - for (unsigned I = DefaultArguments.size(); I < As->size(); ++I) - Params[I] = As->getParam(I); - // FIXME: We could unique these, and also the parameters, but we don't - // expect programs to contain a large enough amount of these deductions - // for that to be worthwhile. - auto *TPL = TemplateParameterList::Create( - S.Context, SourceLocation(), SourceLocation(), Params, - SourceLocation(), As->getRequiresClause()); - NewDeduced = DeducedTemplateArgument( - TemplateName(TemplateTemplateParmDecl::Create( - S.Context, TempArg->getDeclContext(), SourceLocation(), - TempArg->getDepth(), TempArg->getPosition(), - TempArg->isParameterPack(), TempArg->getIdentifier(), - TempArg->wasDeclaredWithTypename(), TPL))); + assert(DefaultArguments.size() <= As->size()); + + SmallVector<NamedDecl *, 4> Params(As->size()); + for (unsigned I = 0; I < DefaultArguments.size(); ++I) + Params[I] = getTemplateParameterWithDefault(S, As->getParam(I), + DefaultArguments[I]); + for (unsigned I = DefaultArguments.size(); I < As->size(); ++I) + Params[I] = As->getParam(I); + // FIXME: We could unique these, and also the parameters, but we don't + // expect programs to contain a large enough amount of these deductions + // for that to be worthwhile. + auto *TPL = TemplateParameterList::Create( + S.Context, SourceLocation(), SourceLocation(), Params, + SourceLocation(), As->getRequiresClause()); + + TemplateDecl *TD; + switch (TempArg->getKind()) { + case Decl::TemplateTemplateParm: { + auto *A = cast<TemplateTemplateParmDecl>(TempArg); + assert(!A->isExpandedParameterPack()); + TD = TemplateTemplateParmDecl::Create( + S.Context, A->getDeclContext(), SourceLocation(), A->getDepth(), + A->getPosition(), A->isParameterPack(), A->getIdentifier(), + A->wasDeclaredWithTypename(), TPL); + break; + } + case Decl::ClassTemplate: { + auto *A = cast<ClassTemplateDecl>(TempArg); + auto *CT = ClassTemplateDecl::Create(S.Context, A->getDeclContext(), + SourceLocation(), A->getDeclName(), + TPL, A->getTemplatedDecl()); + CT->setPreviousDecl(A); + TD = CT; + break; + } + default: + llvm_unreachable("Unexpected Template Kind"); } + TD->setImplicit(true); + NewDeduced = DeducedTemplateArgument(TemplateName(TD)); } DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context, diff --git a/clang/test/CXX/temp/temp.decls/temp.alias/p2.cpp b/clang/test/CXX/temp/temp.decls/temp.alias/p2.cpp index a5b39fe5c51f7..bc39431253880 100644 --- a/clang/test/CXX/temp/temp.decls/temp.alias/p2.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.alias/p2.cpp @@ -28,13 +28,14 @@ namespace StdExample { { /* ... */ } template<template<class> class TT> - void f(TT<int>); // expected-note {{candidate template ignored}} + void f(TT<int>); template<template<class,class> class TT> void g(TT<int, Alloc<int>>); int h() { - f(v); // expected-error {{no matching function for call to 'f'}} + f(v); // OK: TT = vector, Alloc<int> is used as the default argument for the + // second parameter. g(v); // OK: TT = vector } diff --git a/clang/test/SemaTemplate/cwg2398.cpp b/clang/test/SemaTemplate/cwg2398.cpp index e3b5e575374d3..4cc946735a1e2 100644 --- a/clang/test/SemaTemplate/cwg2398.cpp +++ b/clang/test/SemaTemplate/cwg2398.cpp @@ -65,13 +65,10 @@ namespace class_template { template <class T3> struct B; template <template <class T4> class TT1, class T5> struct B<TT1<T5>>; - // new-note@-1 {{partial specialization matches}} template <class T6, class T7> struct B<A<T6, T7>> {}; - // new-note@-1 {{partial specialization matches}} template struct B<A<int>>; - // new-error@-1 {{ambiguous partial specialization}} } // namespace class_template namespace type_pack1 { _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits