Skip to content

[Clang] Consider default template arguments when synthesizing CTAD guides #147675

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
29 changes: 25 additions & 4 deletions clang/lib/Sema/SemaTemplateDeductionGuide.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1061,15 +1061,36 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
SmallVector<DeducedTemplateArgument> 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 <class T>
// using Foo = A<A<T>>;
//
// template <class U = int>
// using Bar = Foo<U>;
//
// In terms of Bar, we want U to appear in the synthesized deduction guide,
// but U would remain undeduced if we deduce against A<T> 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<TemplateArgument> Ps = FReturnType->template_arguments();
if (auto *DG = dyn_cast<CXXDeductionGuideDecl>(F->getTemplatedDecl());
DG && DG->getSourceDeductionGuideKind() ==
CXXDeductionGuideDecl::SourceDeductionGuideKind::Alias)
Ps = F->getInjectedTemplateArgs(Context);
Comment on lines +1079 to +1083
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I gather about the context of this function, F is always a deduction guide, so the dyn_cast is superfluous.

Also, wouldn't we always be wanting to deduce against the injected template parameters of F anyway, so you could instead just change FReturnType->template_arguments() below to F->getInjectedTemplateArgs(Context)? And you could then nuke the whole part above about obtaining FReturnType, making this fix a simplification overall?


// 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<TemplateArgument> DeducedArgs;
SmallVector<unsigned> NonDeducedTemplateParamsInFIndex;
Expand Down
33 changes: 27 additions & 6 deletions clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 *>

Expand Down Expand Up @@ -539,3 +540,23 @@ 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 = int>
using Bar = Foo<T>;

template <class T = int, class U = int>
using Baz = Bar<T>;

Bar a{};

Baz b{};

} // namespace GH133132