[clang] [Clang][CTAD] Implement deduction guides for alias templates with nested aliases (PR #222215)

via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 8 18:55:55 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Muhammad Bassiouni (bassiounix)

<details>
<summary>Changes</summary>

Fix edge case of CTAD alias template 
```cpp
template<typename T>
struct A { A(T){} };

template<typename T>
using Proxy = T;

template<typename T>
using C = Proxy<A<T>>;

C test{ 42 };
```

---
Full diff: https://github.com/llvm/llvm-project/pull/222215.diff


3 Files Affected:

- (modified) clang/docs/ReleaseNotes.md (+5) 
- (modified) clang/lib/Sema/SemaTemplateDeductionGuide.cpp (+31-4) 
- (modified) clang/test/SemaCXX/cxx20-ctad-type-alias.cpp (+2-2) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 0fb6dcf59d4c9..66a3456d78eb7 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -551,6 +551,11 @@ features cannot lower the translation-unit ABI level;
 
 - Fixed an issue where `__typeof__` incorrectly rejected cv-qualified function types.
 
+- Class template argument deduction through an alias template now works when
+  the right-hand side of the alias names another alias template that cannot
+  have deduction guides of its own. The deduction guides are now
+  derived from the first template in the chain that can have them. (#GH125821)
+
 - Fixed a bug where top-level CV qualifiers (such as ``const``) were dropped from pointers modified by Microsoft pointer attributes (like ``__ptr32`` and ``__ptr64``) and WebAssembly's ``__funcref``.
 
 - Fixed a bug where we accepted ``__super`` being qualified by a scope specifier, causing codegen to assertion fail elsewhere. (#GH212988)
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index 1a488ece55d25..7edf07b129cf9 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -1177,13 +1177,40 @@ getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate) {
   auto RhsType = AliasTemplate->getTemplatedDecl()->getUnderlyingType();
   TemplateDecl *Template = nullptr;
   llvm::ArrayRef<TemplateArgument> AliasRhsTemplateArgs;
-  if (const auto *TST = RhsType->getAs<TemplateSpecializationType>()) {
+  const auto *TST = RhsType->getAs<TemplateSpecializationType>();
+
+  // The RHS of the alias may name another alias template that can never have
+  // deduction guides of its own, because its defining-type-id is not of the
+  // form
+  //   [typename] [nested-name-specifier] [template] simple-template-id
+  // as required by [over.match.class.deduct]p3. e.g.
+  //   template <typename T>
+  //   using Identity = T;
+  //   template <typename T>
+  //   using C = Identity<Foo<T>>;
+  // Per [temp.alias]p2, Identity<Foo<T>> is equivalent to Foo<T>, so step
+  // through such aliases and derive the deduction guides from the first
+  // template that can actually have them (GH125821).
+  while (TST) {
+    auto *RhsAlias = dyn_cast_or_null<TypeAliasTemplateDecl>(
+        TST->getTemplateName().getAsTemplateDecl());
+    if (!RhsAlias || getRHSTemplateDeclAndArgs(SemaRef, RhsAlias).first)
+      break;
+    RhsType = TST->desugar();
+    TST = RhsType->getAs<TemplateSpecializationType>();
+  }
+
+  if (TST) {
     // Cases where the RHS of the alias is dependent. e.g.
     //   template<typename T>
     //   using AliasFoo1 = Foo<T>; // a class/type alias template specialization
-    Template = TST->getTemplateName().getAsTemplateDecl();
-    AliasRhsTemplateArgs =
-        TST->getAsNonAliasTemplateSpecializationType()->template_arguments();
+    // The RHS may not desugar to a template specialization at all (e.g. an
+    // alias of the form 'T*' whose specialization ends up being a pointer);
+    // in that case, there is no template to derive the guides from.
+    if (const auto *RhsTST = TST->getAsNonAliasTemplateSpecializationType()) {
+      Template = TST->getTemplateName().getAsTemplateDecl();
+      AliasRhsTemplateArgs = RhsTST->template_arguments();
+    }
   } else if (const auto *RT = RhsType->getAs<RecordType>()) {
     // Cases where template arguments in the RHS of the alias are not
     // dependent. e.g.
diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
index 78911cbaed67b..e0f59be663b2f 100644
--- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
+++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
@@ -553,7 +553,6 @@ void foo() { test<{1, 2, 3}>(); }
 
 } // namespace GH113518
 
-// FIXME: This is accepted by GCC: https://gcc.godbolt.org/z/f3rMfbacz
 namespace GH125821 {
 template<typename T>
 struct A { A(T){} };
@@ -564,7 +563,8 @@ using Proxy = T;
 template<typename T>
 using C = Proxy< A<T> >;
 
-C test{ 42 }; // expected-error {{no viable constructor or deduction guide for deduction of template arguments}}
+C test{ 42 };
+static_assert(__is_same(decltype(test), A<int>));
 
 } // namespace GH125821
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/222215


More information about the cfe-commits mailing list