https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/147675
>From f8b1894ad39560edb5bb0c14194a3a2d54a911ff Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Wed, 9 Jul 2025 16:29:45 +0800 Subject: [PATCH 1/3] [Clang] Consider default template arguments when synthesizing CTAD guides 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. --- clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 34 +++++++++--- clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 52 ++++++++++++++++--- 2 files changed, 74 insertions(+), 12 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp index bdc46a0115a45..3951813dc66dd 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( @@ -969,11 +969,14 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef, } std::pair<TemplateDecl *, llvm::ArrayRef<TemplateArgument>> -getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate) { +getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, + bool Desugar) { // Unwrap the sugared ElaboratedType. auto RhsType = AliasTemplate->getTemplatedDecl() ->getUnderlyingType() .getSingleStepDesugaredType(SemaRef.Context); + if (Desugar) + RhsType = RhsType.getDesugaredType(SemaRef.Context); TemplateDecl *Template = nullptr; llvm::ArrayRef<TemplateArgument> AliasRhsTemplateArgs; if (const auto *TST = RhsType->getAs<TemplateSpecializationType>()) { @@ -1023,8 +1026,26 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef, auto &Context = SemaRef.Context; auto [Template, AliasRhsTemplateArgs] = - getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate); + getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate, /*Desugar=*/true); + // 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 +1055,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. @@ -1223,7 +1244,7 @@ void DeclareImplicitDeductionGuidesForTypeAlias( return; auto &Context = SemaRef.Context; auto [Template, AliasRhsTemplateArgs] = - getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate); + getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate, /*Desugar=*/false); if (!Template) return; auto SourceDeductionGuides = getSourceDeductionGuides( @@ -1303,7 +1324,8 @@ FunctionTemplateDecl *DeclareAggregateDeductionGuideForTypeAlias( Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, MutableArrayRef<QualType> ParamTypes, SourceLocation Loc) { TemplateDecl *RHSTemplate = - getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate).first; + getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate, /*Desugar=*/false) + .first; if (!RHSTemplate) return nullptr; diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index aeb02c9e4898e..34bfa020f5baa 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,25 @@ 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...>; + +BB a{0}; +static_assert(__is_same(decltype(a), A<int>)); + +} + namespace GH92212 { template<typename T, typename...Us> struct A{ @@ -526,6 +546,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 +560,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 >From 7450653f60791c5de9c59f0c7dcc92a6dd40999c Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Fri, 11 Jul 2025 16:26:28 +0800 Subject: [PATCH 2/3] Simplify more --- clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp index 3951813dc66dd..9be1c9c356cb2 100644 --- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp +++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp @@ -969,14 +969,11 @@ Expr *buildIsDeducibleConstraint(Sema &SemaRef, } std::pair<TemplateDecl *, llvm::ArrayRef<TemplateArgument>> -getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, - bool Desugar) { +getRHSTemplateDeclAndArgs(Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate) { // Unwrap the sugared ElaboratedType. auto RhsType = AliasTemplate->getTemplatedDecl() ->getUnderlyingType() .getSingleStepDesugaredType(SemaRef.Context); - if (Desugar) - RhsType = RhsType.getDesugaredType(SemaRef.Context); TemplateDecl *Template = nullptr; llvm::ArrayRef<TemplateArgument> AliasRhsTemplateArgs; if (const auto *TST = RhsType->getAs<TemplateSpecializationType>()) { @@ -984,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. @@ -1026,7 +1024,7 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef, auto &Context = SemaRef.Context; auto [Template, AliasRhsTemplateArgs] = - getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate, /*Desugar=*/true); + 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 @@ -1244,7 +1242,7 @@ void DeclareImplicitDeductionGuidesForTypeAlias( return; auto &Context = SemaRef.Context; auto [Template, AliasRhsTemplateArgs] = - getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate, /*Desugar=*/false); + getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate); if (!Template) return; auto SourceDeductionGuides = getSourceDeductionGuides( @@ -1324,8 +1322,7 @@ FunctionTemplateDecl *DeclareAggregateDeductionGuideForTypeAlias( Sema &SemaRef, TypeAliasTemplateDecl *AliasTemplate, MutableArrayRef<QualType> ParamTypes, SourceLocation Loc) { TemplateDecl *RHSTemplate = - getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate, /*Desugar=*/false) - .first; + getRHSTemplateDeclAndArgs(SemaRef, AliasTemplate).first; if (!RHSTemplate) return nullptr; >From 7426050f4b623a8f8a77c818e47875c52b6dafa6 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Fri, 11 Jul 2025 16:30:08 +0800 Subject: [PATCH 3/3] Release note --- clang/docs/ReleaseNotes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 96477ef6ddc9a..2562650fcc622 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -803,7 +803,7 @@ Bug Fixes to C++ Support - Clang no longer crashes when trying to unify the types of arrays with certain differences 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. _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits