[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