[clang] 040e7ad - [Clang] Consider default template arguments when synthesizing CTAD guides (#147675)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Jul 13 23:45:26 PDT 2025
Author: Younan Zhang
Date: 2025-07-14T14:45:23+08:00
New Revision: 040e7ad8281dcb52507070fbeec59421af78c5ca
URL: https://github.com/llvm/llvm-project/commit/040e7ad8281dcb52507070fbeec59421af78c5ca
DIFF: https://github.com/llvm/llvm-project/commit/040e7ad8281dcb52507070fbeec59421af78c5ca.diff
LOG: [Clang] Consider default template arguments when synthesizing CTAD guides (#147675)
We copy arguments from different template parameter lists depending on
the deducibility of the template parameters. In particular, we may lose
the default template argument from the original alias declaration, and
this patch helps preserve that.
Fixes https://github.com/llvm/llvm-project/issues/133132
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/lib/Sema/SemaTemplateDeductionGuide.cpp
clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 7b6069624c26c..6996066826cbc 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -875,7 +875,7 @@ Bug Fixes to C++ Support
- Clang no longer crashes when trying to unify the types of arrays with
certain
diff erences in qualifiers (this could happen during template argument
deduction or when building a ternary operator). (#GH97005)
-- Fixed type alias CTAD issues involving default template arguments. (#GH134471)
+- Fixed type alias CTAD issues involving default template arguments. (#GH133132), (#GH134471)
- Fixed CTAD issues when initializing anonymous fields with designated initializers. (#GH67173)
- The initialization kind of elements of structured bindings
direct-list-initialized from an array is corrected to direct-initialization.
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index bdc46a0115a45..9be1c9c356cb2 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -949,7 +949,7 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef,
ReturnType = SemaRef.SubstType(
ReturnType, Args, AliasTemplate->getLocation(),
Context.DeclarationNames.getCXXDeductionGuideName(AliasTemplate));
- };
+ }
SmallVector<TypeSourceInfo *> IsDeducibleTypeTraitArgs = {
Context.getTrivialTypeSourceInfo(
@@ -981,7 +981,8 @@ getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate) {
// template<typename T>
// using AliasFoo1 = Foo<T>; // a class/type alias template specialization
Template = TST->getTemplateName().getAsTemplateDecl();
- AliasRhsTemplateArgs = TST->template_arguments();
+ AliasRhsTemplateArgs =
+ TST->getAsNonAliasTemplateSpecializationType()->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.
@@ -1025,6 +1026,24 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
auto [Template, AliasRhsTemplateArgs] =
getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate);
+ // We need both types desugared, before we continue to perform type deduction.
+ // The intent is to get the template argument list 'matched', e.g. in the
+ // following case:
+ //
+ //
+ // template <class T>
+ // struct A {};
+ // template <class T>
+ // using Foo = A<A<T>>;
+ // template <class U = int>
+ // using Bar = Foo<U>;
+ //
+ // In terms of Bar, we want U (which has the default argument) to appear in
+ // the synthesized deduction guide, but U would remain undeduced if we deduced
+ // A<A<T>> using Foo<U> directly.
+ //
+ // Instead, we need to canonicalize both against A, i.e. A<A<T>> and A<A<U>>,
+ // such that T can be deduced as U.
auto RType = F->getTemplatedDecl()->getReturnType();
// The (trailing) return type of the deduction guide.
const TemplateSpecializationType *FReturnType =
@@ -1034,7 +1053,7 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
FReturnType = InjectedCNT->getInjectedTST();
else if (const auto *ET = RType->getAs<ElaboratedType>())
// explicit deduction guide.
- FReturnType = ET->getNamedType()->getAs<TemplateSpecializationType>();
+ FReturnType = ET->getNamedType()->getAsNonAliasTemplateSpecializationType();
assert(FReturnType && "expected to see a return type");
// Deduce template arguments of the deduction guide f from the RHS of
// the alias.
diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
index aeb02c9e4898e..ae70cd9eeac43 100644
--- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
+++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
@@ -207,13 +207,14 @@ namespace test15 {
template <class T> struct Foo { Foo(T); };
template<class V> using AFoo = Foo<V *>;
-template<typename> concept False = false;
+template<typename> concept False = false; // #test15_False
template<False W>
-using BFoo = AFoo<W>; // expected-note {{candidate template ignored: constraints not satisfied [with V = int]}} \
- // expected-note {{cannot deduce template arguments for 'BFoo' from 'Foo<int *>'}} \
- // expected-note {{implicit deduction guide declared as 'template <class V> requires __is_deducible(AFoo, Foo<V *>) && __is_deducible(test15::BFoo, Foo<V *>) BFoo(V *) -> Foo<V *>}} \
- // expected-note {{candidate template ignored: could not match 'Foo<V *>' against 'int *'}} \
- // expected-note {{template <class V> requires __is_deducible(AFoo, Foo<V *>) && __is_deducible(test15::BFoo, Foo<V *>) BFoo(Foo<V *>) -> Foo<V *>}}
+using BFoo = AFoo<W>; // expected-note {{candidate template ignored: constraints not satisfied [with W = int]}} \
+ // expected-note at -1 {{because 'int' does not satisfy 'False'}} \
+ // expected-note@#test15_False {{because 'false' evaluated to false}} \
+ // expected-note {{implicit deduction guide declared as 'template <False<> W> requires __is_deducible(AFoo, Foo<W *>) && __is_deducible(test15::BFoo, Foo<W *>) BFoo(W *) -> Foo<W *>}} \
+ // expected-note {{candidate template ignored: could not match 'Foo<W *>' against 'int *'}} \
+ // expected-note {{template <False<> W> requires __is_deducible(AFoo, Foo<W *>) && __is_deducible(test15::BFoo, Foo<W *>) BFoo(Foo<W *>) -> Foo<W *>}}
int i = 0;
AFoo a1(&i); // OK, deduce Foo<int *>
@@ -441,6 +442,32 @@ ACase4 case4{0, 1};
} // namespace test24
+namespace test25 {
+
+template<typename T, typename...Us>
+struct A{
+ template<typename V> requires __is_same(V, int)
+ A(V);
+};
+
+template<typename...TS>
+using AA = A<int, TS...>;
+
+template<typename...US>
+using BB = AA<US...>; // #test25_BB
+
+BB a{0};
+static_assert(__is_same(decltype(a), A<int>));
+// FIXME: The template parameter list of generated deduction guide is not strictly conforming,
+// as the pack occurs prior to the non-packs.
+BB b{0, 1};
+// expected-error at -1 {{no viable}}
+// expected-note@#test25_BB 2{{not viable}}
+// expected-note@#test25_BB {{template <typename ...US, typename V> requires __is_same(V, int) && __is_deducible(AA, A<int, US...>) && __is_deducible(test25::BB, A<int, US...>) BB(V) -> A<int, US...>}}
+// expected-note@#test25_BB {{implicit deduction guide}}
+
+}
+
namespace GH92212 {
template<typename T, typename...Us>
struct A{
@@ -526,6 +553,7 @@ 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){} };
@@ -539,3 +567,22 @@ using C = Proxy< A<T> >;
C test{ 42 }; // expected-error {{no viable constructor or deduction guide for deduction of template arguments}}
} // namespace GH125821
+
+namespace GH133132 {
+
+template <class T>
+struct A {};
+
+template <class T>
+using Foo = A<A<T>>;
+
+template <class T>
+using Bar = Foo<T>;
+
+template <class T = int>
+using Baz = Bar<T>;
+
+Baz a{};
+static_assert(__is_same(decltype(a), A<A<int>>));
+
+} // namespace GH133132
More information about the cfe-commits
mailing list