[clang] [Clang][C++26] Implement P3865R1 - CTAD for type template template parameters (PR #191409)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 10 05:57:58 PDT 2026
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
More information about the cfe-commits
mailing list