-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[clang][Sema] Use original template pattern when declaring implicit deduction guides for nested template classes #68379
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
@llvm/pr-subscribers-clang ChangesWhen a nested template is instantiated, the template pattern of the inner class is not copied into the outer class ClassTemplateSpecializationDecl. The specialization contains a ClassTemplateDecl with an empty record that points to the original template pattern instead. As a result, when looking up the constructors of the inner class, no results are returned. This patch finds the original template pattern and uses that for the lookup instead. Based on CWG2471 we must also substitute the known outer template arguments when creating deduction guides for the inner class. Fixes #46200 Full diff: https://github.com/llvm/llvm-project/pull/68379.diff 2 Files Affected:
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 080f005e04402cb..33907bd58b44fc4 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2250,6 +2250,7 @@ struct ConvertConstructorToDeductionGuideTransform {
Sema &SemaRef;
ClassTemplateDecl *Template;
+ ClassTemplateDecl *NestedPattern = nullptr;
DeclContext *DC = Template->getDeclContext();
CXXRecordDecl *Primary = Template->getTemplatedDecl();
@@ -2327,6 +2328,8 @@ struct ConvertConstructorToDeductionGuideTransform {
if (FTD) {
Args.addOuterTemplateArguments(SubstArgs);
Args.addOuterRetainedLevel();
+ if (NestedPattern)
+ Args.addOuterRetainedLevels(NestedPattern->getTemplateDepth());
}
FunctionProtoTypeLoc FPTL = CD->getTypeSourceInfo()->getTypeLoc()
@@ -2438,10 +2441,17 @@ struct ConvertConstructorToDeductionGuideTransform {
SmallVector<QualType, 4> ParamTypes;
const FunctionProtoType *T = TL.getTypePtr();
+ MultiLevelTemplateArgumentList OuterInstantiationArgs;
+ if (NestedPattern)
+ OuterInstantiationArgs = SemaRef.getTemplateInstantiationArgs(Template);
+
// -- The types of the function parameters are those of the constructor.
for (auto *OldParam : TL.getParams()) {
ParmVarDecl *NewParam =
transformFunctionTypeParam(OldParam, Args, MaterializedTypedefs);
+ if (NestedPattern && NewParam)
+ NewParam = transformFunctionTypeParam(NewParam, OuterInstantiationArgs,
+ MaterializedTypedefs);
if (!NewParam)
return QualType();
ParamTypes.push_back(NewParam->getType());
@@ -2647,13 +2657,23 @@ void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template,
if (BuildingDeductionGuides.isInvalid())
return;
+ // If the template is nested, then we need to use the original
+ // pattern to iterate over the constructors.
+ ClassTemplateDecl *Pattern = Transform.Template;
+ while (Pattern->getInstantiatedFromMemberTemplate()) {
+ if (Pattern->isMemberSpecialization())
+ break;
+ Pattern = Pattern->getInstantiatedFromMemberTemplate();
+ Transform.NestedPattern = Pattern;
+ }
+
// Convert declared constructors into deduction guide templates.
// FIXME: Skip constructors for which deduction must necessarily fail (those
// for which some class template parameter without a default argument never
// appears in a deduced context).
llvm::SmallPtrSet<NamedDecl *, 8> ProcessedCtors;
bool AddedAny = false;
- for (NamedDecl *D : LookupConstructors(Transform.Primary)) {
+ for (NamedDecl *D : LookupConstructors(Pattern->getTemplatedDecl())) {
D = D->getUnderlyingDecl();
if (D->isInvalidDecl() || D->isImplicit())
continue;
diff --git a/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp b/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp
new file mode 100644
index 000000000000000..4915c687cf4c4ef
--- /dev/null
+++ b/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -std=c++17 -verify %s
+// expected-no-diagnostics
+
+template<class T> struct S {
+ template<class U> struct N {
+ N(T) {}
+ N(T, U) {}
+ template<class V> N(V, U) {}
+ };
+};
+
+S<int>::N x{"a", 1};
|
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.
This seems reasonable, however it needs a release note.
Thanks for the review! I've added a release note to the C++ bugfix section |
Friendly ping |
deduction guides for nested template classes When a nested template is instantiated, the template pattern of the inner class is not copied into the outer class ClassTemplateSpecializationDecl. The specialization contains a ClassTemplateDecl with an empty record that points to the original template pattern instead. As a result, when looking up the constructors of the inner class, no results are returned. This patch finds the original template pattern and uses that for the lookup instead. Based on CWG2471 we must also substitute the known outer template arguments when creating deduction guides for the inner class. Fixes llvm#46200 Fixes llvm#57812
b48a0c4
to
67b8a02
Compare
I think we may be seeing some issues with this patch, when building Fuchsia. We're seeing the following error.
But the code in question is defining a constructor for the nested class. You can find the code here: https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/lib/ld/include/lib/ld/internal/filter-view.h;l=51?q=filter_iterator&ss=fuchsia%2Ffuchsia We're tracking this in https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=135353 Do you have any idea why it would be flagging this case. I don't see any obvious C++ rules being violated, so I think this is rather surprising. I've included a reproducer, but I haven't had a chance to reduce it yet, so it's likely to be quite large. |
I've come up with this minimal reproducer: template<class A>
struct outer {
template<class B>
struct inner {
inner(B b);
inner(const inner &x) = default;
};
template<class B>
inner(B b) -> inner<B>;
};
outer<int>::inner i(1); The error seems to be triggered by the inclusion of both the copy constructor and the explicit deduction guide. I'll revert the patch now until I can come up with a proper fix for it. |
When As a result I think that in this case, if the decl passed into |
I think your analysis and approach sounds correct, but probably worth checking with someone a bit more familiar with all of the C++ nuance here. |
I've posted a candidate reland of this PR at #69676 . |
When a nested template is instantiated, the template pattern of the inner class is not copied into the outer class ClassTemplateSpecializationDecl. The specialization contains a ClassTemplateDecl with an empty record that points to the original template pattern instead.
As a result, when looking up the constructors of the inner class, no results are returned. This patch finds the original template pattern and uses that for the lookup instead.
Based on CWG2471 we must also substitute the known outer template arguments when creating deduction guides for the inner class.
Fixes #46200
Fixes #57812