https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/191409
>From 94edc08efcb2e34d54f39b25964957d6dbeffade Mon Sep 17 00:00:00 2001 From: Corentin Jabot <[email protected]> Date: Fri, 10 Apr 2026 14:51:40 +0200 Subject: [PATCH 1/2] [Clang][C++26] Implement P3865R1 - CTAD for type template template parameters As a DR to C++20. Fixes #191380 --- clang/docs/ReleaseNotes.rst | 2 + clang/include/clang/Sema/Sema.h | 6 ++ clang/lib/Sema/SemaInit.cpp | 80 +++++++++----- clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 56 ++++++++++ .../test/SemaCXX/cxx2c-ctad-type-template.cpp | 102 ++++++++++++++++++ clang/www/cxx_status.html | 2 +- 6 files changed, 221 insertions(+), 27 deletions(-) create mode 100644 clang/test/SemaCXX/cxx2c-ctad-type-template.cpp 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> >From 0c8119406bda688dbb544f7e9c1645180a53acd0 Mon Sep 17 00:00:00 2001 From: Corentin Jabot <[email protected]> Date: Sat, 11 Apr 2026 13:08:44 +0200 Subject: [PATCH 2/2] fix libc++ crash --- clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 7 +++-- .../test/SemaCXX/cxx2c-ctad-type-template.cpp | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp index 2611de70aec63..e9322f573fcc2 100644 --- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp +++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp @@ -1621,11 +1621,12 @@ TypeAliasTemplateDecl *Sema::BuildAliasForCTADFromTypeTemplateParameter( LocalInstantiationScope Scope(SemaRef); auto &AST = SemaRef.getASTContext(); - auto *Func = cast<NamedDecl>(CurContext); auto *Ctx = CurContext->getParent(); - MultiLevelTemplateArgumentList MTAL = - SemaRef.getTemplateInstantiationArgs(Func); + MultiLevelTemplateArgumentList MTAL; + if (NamedDecl *Func = dyn_cast<NamedDecl>(CurContext)) + MTAL = SemaRef.getTemplateInstantiationArgs(Func); + llvm::SmallVector<NamedDecl *> Parameters; llvm::SmallVector<TemplateArgument> Args; for (NamedDecl *P : D->getTemplateParameters()->asArray()) { diff --git a/clang/test/SemaCXX/cxx2c-ctad-type-template.cpp b/clang/test/SemaCXX/cxx2c-ctad-type-template.cpp index 6eeab72532cfe..929c150d91dde 100644 --- a/clang/test/SemaCXX/cxx2c-ctad-type-template.cpp +++ b/clang/test/SemaCXX/cxx2c-ctad-type-template.cpp @@ -100,3 +100,33 @@ void f() { template void f<Y>(); // expected-note {{in instantiation}} } + +namespace Regression1 { + +template <typename> +struct __iter_concept_impl; +template <typename _Iter> + requires requires { typename _Iter; } +struct __iter_concept_impl<_Iter>; +template <typename _Iter> +concept input_iterator = true; + +template <typename _Tp> +concept input_range = true; + +template <template <typename> typename _Cont> +using _DeduceExpr1 = decltype(_Cont()); + +template <template <typename> typename _Cont, input_range _Rg> +auto to(_Rg) { + auto _ = requires { typename _DeduceExpr1<_Cont>; }; +} + +template <typename> +struct vector; + +void test() { + int range; + to<vector>(range); +} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
