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@-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@-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


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to