-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[Clang] Handle default template arguments for alias CTAD guides #134807
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
Conversation
It's possible that some deduced template arguments come from default arguments, not just from the return type. So we need to recursively visit the default arguments if the parameter is referenced, thereby the template parameter referenced by the defualt arguments could come along to the synthesized deduction guide.
d93117d
to
3f09d11
Compare
@llvm/pr-subscribers-clang Author: Younan Zhang (zyn0217) ChangesIt's possible that some deduced template arguments come from default arguments, not just from the return type. So we need to recursively visit the default arguments of the parameter if it's referenced, thereby the template parameter referenced by the defualt arguments could come along to the synthesized deduction guide. Fixes #134471 Full diff: https://github.com/llvm/llvm-project/pull/134807.diff 3 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e671183522565..aa57b995a6433 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -390,6 +390,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)
- The initialization kind of elements of structured bindings
direct-list-initialized from an array is corrected to direct-initialization.
- Clang no longer crashes when a coroutine is declared ``[[noreturn]]``. (#GH127327)
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index b4863cefc3fb4..30376ca774384 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -690,6 +690,23 @@ SmallVector<unsigned> TemplateParamsReferencedInTemplateArgumentList(
SemaRef.MarkUsedTemplateParameters(
DeducedArgs, TemplateParamsList->getDepth(), ReferencedTemplateParams);
+ auto MarkDefaultArgs = [&](auto *Param) {
+ if (!Param || !Param->hasDefaultArgument())
+ return;
+ SemaRef.MarkUsedTemplateParameters(
+ Param->getDefaultArgument().getArgument(),
+ TemplateParamsList->getDepth(), ReferencedTemplateParams);
+ };
+
+ for (unsigned Index = 0; Index < TemplateParamsList->size(); ++Index) {
+ if (!ReferencedTemplateParams[Index])
+ continue;
+ auto *Param = TemplateParamsList->getParam(Index);
+ MarkDefaultArgs(dyn_cast<TemplateTypeParmDecl>(Param));
+ MarkDefaultArgs(dyn_cast<NonTypeTemplateParmDecl>(Param));
+ MarkDefaultArgs(dyn_cast<TemplateTemplateParmDecl>(Param));
+ }
+
SmallVector<unsigned> Results;
for (unsigned Index = 0; Index < TemplateParamsList->size(); ++Index) {
if (ReferencedTemplateParams[Index])
diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp
index 6db132ca37c7e..76b6cb051b7dd 100644
--- a/clang/test/SemaTemplate/deduction-guide.cpp
+++ b/clang/test/SemaTemplate/deduction-guide.cpp
@@ -771,3 +771,42 @@ D d(24);
// CHECK-NEXT: `-ParmVarDecl {{.+}} 'U'
} // namespace GH132616_DeductionGuide
+
+namespace GH133132 {
+
+template <class _Ty>
+struct A {};
+
+template <class T = int, class U = T>
+using AA = A<U>;
+
+AA a{};
+
+// CHECK-LABEL: Dumping GH133132::<deduction guide for AA>:
+// CHECK-NEXT: FunctionTemplateDecl {{.+}} implicit <deduction guide for AA>
+// CHECK-NEXT: |-TemplateTypeParmDecl {{.+}} class depth 0 index 0 T
+// CHECK-NEXT: | `-TemplateArgument type 'int'
+// CHECK-NEXT: | `-BuiltinType {{.+}} 'int'
+// CHECK-NEXT: |-TemplateTypeParmDecl {{.+}} class depth 0 index 1 U
+// CHECK-NEXT: | `-TemplateArgument type 'T':'type-parameter-0-0'
+// CHECK-NEXT: | `-TemplateTypeParmType {{.+}} 'T' dependent depth 0 index 0
+// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'T'
+// CHECK-NEXT: |-TypeTraitExpr {{.+}} 'bool' __is_deducible
+// CHECK-NEXT: | |-DeducedTemplateSpecializationType {{.+}} 'GH133132::AA' dependent
+// CHECK-NEXT: | | `-name: 'GH133132::AA'
+// CHECK-NEXT: | | `-TypeAliasTemplateDecl {{.+}} AA
+// CHECK-NEXT: | `-TemplateSpecializationType {{.+}} 'A<U>' dependent
+// CHECK-NEXT: | |-name: 'A':'GH133132::A' qualified
+// CHECK-NEXT: | | `-ClassTemplateDecl {{.+}} A
+// CHECK-NEXT: | `-TemplateArgument type 'U':'type-parameter-0-1'
+// CHECK-NEXT: | `-SubstTemplateTypeParmType {{.+}} 'U' sugar dependent class depth 0 index 0 _Ty
+// CHECK-NEXT: | |-FunctionTemplate {{.+}} '<deduction guide for A>'
+// CHECK-NEXT: | `-TemplateTypeParmType {{.+}} 'U' dependent depth 0 index 1
+// CHECK-NEXT: | `-TemplateTypeParm {{.+}} 'U'
+// CHECK-NEXT: |-CXXDeductionGuideDecl {{.+}} implicit <deduction guide for AA> 'auto () -> A<U>'
+// CHECK-NEXT: `-CXXDeductionGuideDecl {{.+}} implicit used <deduction guide for AA> 'auto () -> A<int>' implicit_instantiation
+// CHECK-NEXT: |-TemplateArgument type 'int'
+// CHECK-NEXT: | `-BuiltinType {{.+}} 'int'
+// CHECK-NEXT: `-TemplateArgument type 'int'
+// CHECK-NEXT: `-BuiltinType {{.+}} 'int'
+}
|
auto MarkDefaultArgs = [&](auto *Param) { | ||
if (!Param || !Param->hasDefaultArgument()) | ||
return; | ||
SemaRef.MarkUsedTemplateParameters( | ||
Param->getDefaultArgument().getArgument(), | ||
TemplateParamsList->getDepth(), ReferencedTemplateParams); | ||
}; | ||
|
||
for (unsigned Index = 0; Index < TemplateParamsList->size(); ++Index) { | ||
if (!ReferencedTemplateParams[Index]) | ||
continue; | ||
auto *Param = TemplateParamsList->getParam(Index); | ||
MarkDefaultArgs(dyn_cast<TemplateTypeParmDecl>(Param)); | ||
MarkDefaultArgs(dyn_cast<NonTypeTemplateParmDecl>(Param)); | ||
MarkDefaultArgs(dyn_cast<TemplateTemplateParmDecl>(Param)); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we are missing a step to get the converted template argument list, and should be calling this with that instead of the deduced arguments directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, it seems this will mark default arguments as used even if the corresponding parameter has a deduced argument.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we are missing a step to get the converted template argument list, and should be calling this with that instead of the deduced arguments directly.
The fact that makes me think it sensible is, in the later template parameter transform, we do retain/tranaform the default arguments. That means, the transformed template parameters which has default arguments could end up referring to anything that won't otherwise appear in the synthesized template parameter list. And this is exactly where the issue raised.
Re the ConvertDeducedTemplateArguments, i think we probably want it, but I couldn't immediately recall what job it does to handle default arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, it seems this will mark default arguments as used even if the corresponding parameter has a deduced argument.
That might not be a big deal, because as long as the default parameter doesn't end up in the argument list of RHS, it shouldn't affect the template argument deduction running against on the synthesized deductions guides -- we don't need to CodeGen for deduction guides nor it has to be fully instantiated for all of its parameters, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re the ConvertDeducedTemplateArguments, i think we probably want it, but I couldn't immediately recall what job it does to handle default arguments.
I will produce a full list of template arguments, including default arguments where a parameter is not deduced.
It will also pack the arguments for template parameter packs, and do other things which might not be relevant here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mizvekov I looked into ConvertDeducedTemplateArguments
and I'm afraid it won't help
In deduction guide synthesizing, we have a unique template parameter transform that does handle default parameters (which also covers non-alias deduction guide synthesizing) so when we find a parameter (e.g. U
in example) used by the RHS (A<U>
), we form a new TTP on top of the TTP U = T
. And, as T
is not referenced by A<U>
, the declaration of which won't be created anyway, so we ended up with a problematic template <class U = <template-param-0-0>>
in the eventual CTAD guide.
template <class _Ty> struct A {};
template <class T = int, class U = T>
using AA = A<U>;
This is where the problem lies. And with ConvertDeducedTemplateArguments
, which would substitute the default arguments with the deduced ones in non-rewrite mode, we end up building Subst* nodes that we never want in CTAD.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But we could augment ConvertDeducedTemplateArguments with the option of producing rewrites.
Or eventually I think the rewrite flag could be removed entirely, everything being handled automatically.
I am more worried about the big picture here, I find it strange that this operates on the Deduced arguments directly,
which is kind of an arbitrary / clang specific point where the problem is divided,
and that there might be more stuff related to conversions which don't happen correctly in CTAD.
But since I have too much on my plate to dive on this problem now, I digress, lets go ahead :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am happy with that (modulo nits) when @mizvekov is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
…#134807) It's possible that some deduced template arguments come from default arguments, not just from the return type. So we need to recursively visit the default arguments of the parameter if it's referenced, thereby the template parameter referenced by the defualt arguments could come along to the synthesized deduction guide. Fixes llvm#134471
It's possible that some deduced template arguments come from default arguments, not just from the return type. So we need to recursively visit the default arguments of the parameter if it's referenced, thereby the template parameter referenced by the defualt arguments could come along to the synthesized deduction guide.
Fixes #134471