[clang] [Clang][C++26] Implement P3865R1 - CTAD for type template template parameters (PR #191409)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 10 06:59:17 PDT 2026
================
@@ -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 {
----------------
mizvekov wrote:
I am surprised we even had to implement anything, I thought this already worked in all compilers?
So for example, I don't get this change.
Why do we even care about the `SubstTemplateTemplateParm`?
It's basically like a typedef, it doesn't change anything about what it introduces.
The previous implementation, and now the 'else' branch here, are doing the right thing, which is to just step over template name sugar and get to the template declaration that matters, with `getAsTemplateDecl`.
The call to `getAsSubstTemplateTemplateParm` does the opposite, it expects there to be exactly a top level `SubstTemplateTemplateParm` and returns it, null otherwise.
So for example, this does nothing if there are two levels of substitution involved (ie we had to replace two template template parameters to get to the alias template). I'd expect in that case for it to fall back to the else branch, and then just work? But then it would also work for the one level case, so why this branch then though?
https://github.com/llvm/llvm-project/pull/191409
More information about the cfe-commits
mailing list