llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Corentin Jabot (cor3ntin) <details> <summary>Changes</summary> As a DR to C++20. Fixes #<!-- -->191380 --- Full diff: https://github.com/llvm/llvm-project/pull/191409.diff 6 Files Affected: - (modified) clang/docs/ReleaseNotes.rst (+2) - (modified) clang/include/clang/Sema/Sema.h (+6) - (modified) clang/lib/Sema/SemaInit.cpp (+54-26) - (modified) clang/lib/Sema/SemaTemplateDeductionGuide.cpp (+56) - (added) clang/test/SemaCXX/cxx2c-ctad-type-template.cpp (+102) - (modified) clang/www/cxx_status.html (+1-1) ``````````diff diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 2da7175b51ea3..ddc8c50afcd46 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -127,6 +127,8 @@ C++ Language Changes C++2c Feature Support ^^^^^^^^^^^^^^^^^^^^^ +- Implemented `P3865R1 <https://wg21.link/P3865R1>`_ CTAD for type template template parameters. + C++23 Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1b8a9803be472..a599845d2c014 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9130,6 +9130,12 @@ class Sema final : public SemaBase { TypeSourceInfo *TInfo, const InitializedEntity &Entity, const InitializationKind &Kind, MultiExprArg Init); + /// Build an alias for CTAD from type template parameters (C++26). + TypeAliasTemplateDecl * + BuildAliasForCTADFromTypeTemplateParameter(TemplateTemplateParmDecl *D, + TemplateName Replacement, + SourceLocation Loc); + ///@} // diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index e54a25405c816..aa8bda0eb2c99 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -10140,32 +10140,60 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( if (TemplateName.isDependent()) return SubstAutoTypeSourceInfoDependent(TSInfo)->getType(); - // We can only perform deduction for class templates or alias templates. - auto *Template = - dyn_cast_or_null<ClassTemplateDecl>(TemplateName.getAsTemplateDecl()); - TemplateDecl *LookupTemplateDecl = Template; - if (!Template) { - if (auto *AliasTemplate = dyn_cast_or_null<TypeAliasTemplateDecl>( - TemplateName.getAsTemplateDecl())) { - DiagCompat(Kind.getLocation(), diag_compat::ctad_for_alias_templates); - LookupTemplateDecl = AliasTemplate; - auto UnderlyingType = AliasTemplate->getTemplatedDecl() - ->getUnderlyingType() - .getCanonicalType(); - // C++ [over.match.class.deduct#3]: ..., the defining-type-id of A must be - // of the form - // [typename] [nested-name-specifier] [template] simple-template-id - if (const auto *TST = - UnderlyingType->getAs<TemplateSpecializationType>()) { - Template = dyn_cast_or_null<ClassTemplateDecl>( - TST->getTemplateName().getAsTemplateDecl()); - } else if (const auto *RT = UnderlyingType->getAs<RecordType>()) { - // Cases where template arguments in the RHS of the alias are not - // dependent. e.g. - // using AliasFoo = Foo<bool>; - if (const auto *CTSD = - llvm::dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl())) - Template = CTSD->getSpecializedTemplate(); + TemplateDecl *LookupTemplateDecl = nullptr; + ClassTemplateDecl *Template = nullptr; + + // [C++26] [over.match.class.deduct]p3 + // When resolving a placeholder for a deduced class type where the + // template-name designates a type template template parameter P. + // + // This is applied as a DR to C++20 (Aliases templates are technically a C++20 + // feature) + if (const SubstTemplateTemplateParmStorage *SubstitutedTTP = + TemplateName.getAsSubstTemplateTemplateParm(); + SubstitutedTTP && getLangOpts().CPlusPlus20) { + + TypeAliasTemplateDecl *Alias = BuildAliasForCTADFromTypeTemplateParameter( + SubstitutedTTP->getParameter(), SubstitutedTTP->getReplacement(), + Kind.getLocation()); + if (!Alias) + return QualType(); + + LookupTemplateDecl = Alias; + auto UnderlyingType = + Alias->getTemplatedDecl()->getUnderlyingType().getCanonicalType(); + const auto *TST = UnderlyingType->getAs<TemplateSpecializationType>(); + Template = dyn_cast_or_null<ClassTemplateDecl>( + TST->getTemplateName().getAsTemplateDecl()); + } else { + // We can only perform deduction for class templates or alias templates. + Template = + dyn_cast_or_null<ClassTemplateDecl>(TemplateName.getAsTemplateDecl()); + LookupTemplateDecl = Template; + if (!Template) { + if (auto *AliasTemplate = dyn_cast_or_null<TypeAliasTemplateDecl>( + TemplateName.getAsTemplateDecl())) { + DiagCompat(Kind.getLocation(), diag_compat::ctad_for_alias_templates); + LookupTemplateDecl = AliasTemplate; + auto UnderlyingType = AliasTemplate->getTemplatedDecl() + ->getUnderlyingType() + .getCanonicalType(); + // C++ [over.match.class.deduct#3]: ..., the defining-type-id of A must + // be of the form + // [typename] [nested-name-specifier] [template] simple-template-id + if (const auto *TST = + UnderlyingType->getAs<TemplateSpecializationType>()) { + Template = dyn_cast_or_null<ClassTemplateDecl>( + TST->getTemplateName().getAsTemplateDecl()); + } else if (const auto *RT = UnderlyingType->getAs<RecordType>()) { + // Cases where template arguments in the RHS of the alias are not + // dependent. e.g. + // using AliasFoo = Foo<bool>; + if (const auto *CTSD = + llvm::dyn_cast<ClassTemplateSpecializationDecl>( + RT->getDecl())) + Template = CTSD->getSpecializedTemplate(); + } } } } diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp index 8d55fb087193f..2611de70aec63 100644 --- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp +++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp @@ -1604,3 +1604,59 @@ void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template, SavedContext.pop(); } + +TypeAliasTemplateDecl *Sema::BuildAliasForCTADFromTypeTemplateParameter( + TemplateTemplateParmDecl *D, TemplateName Replacement, SourceLocation Loc) { + + // [C++26] [over.match.class.deduct]p3 + // When resolving a placeholder for a deduced class type where the + // template-name designates a type template template parameter P, let A be an + // alias template whose template parameter list is that of P and whose + // defining-type-id designates the type template template argument with a + // simpletemplate-id in which the template-argument-list consists of a list of + // identifiers naming each template-parameter of P, with the argument being a + // pack expansion if the template-parameter is a pack. A is then used instead + // of the original template-name to resolve the placeholder + + LocalInstantiationScope Scope(SemaRef); + + auto &AST = SemaRef.getASTContext(); + auto *Func = cast<NamedDecl>(CurContext); + auto *Ctx = CurContext->getParent(); + + MultiLevelTemplateArgumentList MTAL = + SemaRef.getTemplateInstantiationArgs(Func); + llvm::SmallVector<NamedDecl *> Parameters; + llvm::SmallVector<TemplateArgument> Args; + for (NamedDecl *P : D->getTemplateParameters()->asArray()) { + auto [Depth, Index] = getDepthAndIndex(P); + NamedDecl *NewParam = + transformTemplateParameter(*this, Ctx, P, MTAL, Index, Depth - 1); + if (!NewParam) + return nullptr; + Parameters.push_back(NewParam); + Args.push_back(SemaRef.Context.getInjectedTemplateArg(NewParam)); + } + + auto *ParamList = + TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(), + Parameters, SourceLocation(), nullptr); + + QualType Type = AST.getCanonicalType(AST.getTemplateSpecializationType( + ElaboratedTypeKeyword::Class, Replacement, Args, {})); + + auto *Alias = TypeAliasDecl::Create(AST, Ctx, Loc, SourceLocation(), nullptr, + AST.getTrivialTypeSourceInfo(Type)); + Alias->setImplicit(true); + + auto *Template = TypeAliasTemplateDecl::Create( + AST, Ctx, Loc, Alias->getDeclName(), ParamList, Alias); + + Alias->setDescribedAliasTemplate(Template); + + Template->setImplicit(true); + Template->setLexicalDeclContext(Alias->getDeclContext()); + Ctx->addDecl(Template); + + return Template; +} diff --git a/clang/test/SemaCXX/cxx2c-ctad-type-template.cpp b/clang/test/SemaCXX/cxx2c-ctad-type-template.cpp new file mode 100644 index 0000000000000..6eeab72532cfe --- /dev/null +++ b/clang/test/SemaCXX/cxx2c-ctad-type-template.cpp @@ -0,0 +1,102 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++23 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++2c -verify %s + + +namespace Ex1 { + template<typename T> + struct C { + C(T); + }; + template<template<typename> class X> + void f() { + X x(1); + } + template void f<C>(); +} + +namespace Ex2 { +template<typename ... T> +struct C { + C(T ...); +}; +template<template<typename> class X> +void f() { + X x1{1}; + X x2{1, 2}; // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'Ex2::C'}} \ + // expected-note {{candidate function template not viable: requires 1 argument, but 2 were provided}} \ + // expected-note {{implicit deduction guide declared as 'template <typename> requires __is_deducible(Ex2::(anonymous), Ex2::C<type-parameter-0-0>) (type-parameter-0-0) -> Ex2::C<type-parameter-0-0>'}} \ + // expected-note {{candidate function template not viable: requires 1 argument, but 2 were provided}} \ + // expected-note {{implicit deduction guide declared as 'template <typename> requires __is_deducible(Ex2::(anonymous), Ex2::C<type-parameter-0-0>) (Ex2::C<type-parameter-0-0>) -> Ex2::C<type-parameter-0-0}} +} +template void f<C>(); // expected-note {{in instantiation}} +} + +namespace Ex3 { + template<typename T = int> + struct C { + C(int); + }; + template<template<typename = long> class X> + void f() { + X x(1); + } + template void f<C>(); +} + +namespace Ex4 { +template<int> +struct A { }; +template<int I> +struct C { + C(A<I>); +}; +template<template<short> class X> +void f() { + X x1{A<1>()}; // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'Ex4::C'}} \ + // expected-note {{candidate template ignored: substitution failure: deduced non-type template argument does not have the same type as the corresponding template parameter ('int' vs 'short')}} \ + // expected-note {{implicit deduction guide declared as 'template <short> requires __is_deducible(Ex4::(anonymous), Ex4::C<value-parameter-0-0>) (A<value-parameter-0-0>) -> Ex4::C<value-parameter-0-0>'}} \ + // expected-note {{candidate template ignored: could not match 'Ex4::C' against 'A'}} \ + // expected-note {{implicit deduction guide declared as 'template <short> requires __is_deducible(Ex4::(anonymous), Ex4::C<value-parameter-0-0>) (Ex4::C<value-parameter-0-0>) -> Ex4::C<value-parameter-0-0>'}} +} +template void f<C>(); // expected-note {{in instantiation}} +} + + +namespace CWG3003 { + +template <typename T> struct A { A(T); }; + +template <typename T, template <typename> class TT = A> +using Alias = TT<T>; // expected-note {{template is declared here}} + +template <typename T> +using Alias2 = Alias<T>; + +void h() { Alias2 a(42); } // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'Alias2'}} +void h2() { Alias a(42); } // expected-error {{alias template 'Alias' requires template arguments; argument deduction only allowed for class templates or alias templates}} + +} + + +namespace WordingExample { +template<typename ... Ts> +struct Y { + Y(); + Y(Ts ...); +}; +template<template<typename T = char> class X> +void f() { + X x0{}; + X x1{1}; + X x2{1, 2}; // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'WordingExample::Y'}} \ + // expected-note {{candidate function template not viable: requires 1 argument, but 2 were provided}} \ + // expected-note {{implicit deduction guide declared as 'template <typename T = char> requires __is_deducible(WordingExample::(anonymous), WordingExample::Y<T>) (T) -> WordingExample::Y<T>'}} \ + // expected-note {{candidate function template not viable: requires 1 argument, but 2 were provided}} \ + // expected-note {{implicit deduction guide declared as 'template <typename T = char> requires __is_deducible(WordingExample::(anonymous), WordingExample::Y<T>) (WordingExample::Y<T>) -> WordingExample::Y<T>'}} \ + // expected-note {{candidate function template not viable: requires 0 arguments, but 2 were provided}} \ + // expected-note {{implicit deduction guide declared as 'template <typename T = char> requires __is_deducible(WordingExample::(anonymous), WordingExample::Y<T>) () -> WordingExample::Y<T>'}} +}; +template void f<Y>(); // expected-note {{in instantiation}} + +} diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 2c834b07f9a8f..87c58d9ea3851 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -352,7 +352,7 @@ <h2 id="cxx26">C++2c implementation status</h2> <tr> <td>CTAD for type template template parameters</td> <td><a href="https://wg21.link/P3865">P3865R3</a>(<a href="#dr">DR</a>)</td> - <td class="none" align="center">No</td> + <td class="unreleased" align="center">Clang 23</td> </tr> <tr> <td>Adjustments to Union Lifetime Rules</td> `````````` </details> https://github.com/llvm/llvm-project/pull/191409 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
