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

Reply via email to