From fb190be793c632bc03d401d80c67588f35dd9d6e Mon Sep 17 00:00:00 2001 From: Younan Zhang Date: Wed, 9 Jul 2025 16:29:45 +0800 Subject: [PATCH] [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/docs/ReleaseNotes.rst | 2 +- clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 29 +++++++++++++--- clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 33 +++++++++++++++---- 3 files changed, 53 insertions(+), 11 deletions(-) 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. diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp index bdc46a0115a45..9eea2cc1dad4a 100644 --- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp +++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp @@ -1061,15 +1061,36 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef, SmallVector DeduceResults( F->getTemplateParameters()->size()); + // We don't have to deduce against the alias template specialization, + // if the source template is a synthesized alias deduction guide. This allows + // us to utilize the default template arguments from alias declaration. + // + // template + // using Foo = A>; + // + // template + // using Bar = Foo; + // + // In terms of Bar, we want U to appear in the synthesized deduction guide, + // but U would remain undeduced if we deduce against A instead of T. + // Also note that since the deduced results are only used for synthesizing + // template parameters, they should not introduce unintended behavior in + // theory. + ArrayRef Ps = FReturnType->template_arguments(); + if (auto *DG = dyn_cast(F->getTemplatedDecl()); + DG && DG->getSourceDeductionGuideKind() == + CXXDeductionGuideDecl::SourceDeductionGuideKind::Alias) + Ps = F->getInjectedTemplateArgs(Context); + // FIXME: DeduceTemplateArguments stops immediately at the first // non-deducible template argument. However, this doesn't seem to cause // issues for practice cases, we probably need to extend it to continue // performing deduction for rest of arguments to align with the C++ // standard. - SemaRef.DeduceTemplateArguments( - F->getTemplateParameters(), FReturnType->template_arguments(), - AliasRhsTemplateArgs, TDeduceInfo, DeduceResults, - /*NumberOfArgumentsMustMatch=*/false); + SemaRef.DeduceTemplateArguments(F->getTemplateParameters(), Ps, + AliasRhsTemplateArgs, TDeduceInfo, + DeduceResults, + /*NumberOfArgumentsMustMatch=*/false); SmallVector DeducedArgs; SmallVector NonDeducedTemplateParamsInFIndex; diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index aeb02c9e4898e..6d027130ce741 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 struct Foo { Foo(T); }; template using AFoo = Foo; -template concept False = false; +template concept False = false; // #test15_False template -using BFoo = AFoo; // expected-note {{candidate template ignored: constraints not satisfied [with V = int]}} \ - // expected-note {{cannot deduce template arguments for 'BFoo' from 'Foo'}} \ - // expected-note {{implicit deduction guide declared as 'template requires __is_deducible(AFoo, Foo) && __is_deducible(test15::BFoo, Foo) BFoo(V *) -> Foo}} \ - // expected-note {{candidate template ignored: could not match 'Foo' against 'int *'}} \ - // expected-note {{template requires __is_deducible(AFoo, Foo) && __is_deducible(test15::BFoo, Foo) BFoo(Foo) -> Foo}} +using BFoo = AFoo; // 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 W> requires __is_deducible(AFoo, Foo) && __is_deducible(test15::BFoo, Foo) BFoo(W *) -> Foo}} \ + // expected-note {{candidate template ignored: could not match 'Foo' against 'int *'}} \ + // expected-note {{template W> requires __is_deducible(AFoo, Foo) && __is_deducible(test15::BFoo, Foo) BFoo(Foo) -> Foo}} int i = 0; AFoo a1(&i); // OK, deduce Foo @@ -539,3 +540,23 @@ using C = Proxy< A >; C test{ 42 }; // expected-error {{no viable constructor or deduction guide for deduction of template arguments}} } // namespace GH125821 + +namespace GH133132 { + +template +struct A {}; + +template +using Foo = A>; + +template +using Bar = Foo; + +template +using Baz = Bar; + +Bar a{}; + +Baz b{}; + +} // namespace GH133132