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