Skip to content
This repository has been archived by the owner on Jan 20, 2024. It is now read-only.

Commit

Permalink
Reland "[clang][Sema] Use original template pattern when declaring im…
Browse files Browse the repository at this point in the history
…plicit deduction guides for nested template classes" (#69676)

Reland of dd0fba1

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.

Changes from the last iteration:
1. The outer retained levels from the outer template are always added to
the `MultiLevelTemplateArgumentList` for rewriting
`FunctionTemplateDecl` arguments, even if there is no FTD and the
arguments are empty.
2. When building implicit deduction guides, the template pattern
underlying decl is pushed as the current context. This resolves the
issue where `FindInstantiatedDecl` is unable to find the inner template
class.
3. Tests are updated to cover the failing case, and to assert that the
type is correct after argument deduction in the implicit case.
  • Loading branch information
antangelo authored Oct 24, 2023
1 parent 4950467 commit f418319
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 1 deletion.
5 changes: 5 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,11 @@ Bug Fixes to C++ Support
declaration definition. Fixes:
(`#61763 <https://github.com/llvm/llvm-project/issues/61763>`_)

- Fix a bug where implicit deduction guides are not correctly generated for nested template
classes. Fixes:
(`#46200 <https://github.com/llvm/llvm-project/issues/46200>`_)
(`#57812 <https://github.com/llvm/llvm-project/issues/57812>`_)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
- Fixed an import failure of recursive friend class template.
Expand Down
26 changes: 25 additions & 1 deletion clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2253,6 +2253,7 @@ struct ConvertConstructorToDeductionGuideTransform {

Sema &SemaRef;
ClassTemplateDecl *Template;
ClassTemplateDecl *NestedPattern = nullptr;

DeclContext *DC = Template->getDeclContext();
CXXRecordDecl *Primary = Template->getTemplatedDecl();
Expand Down Expand Up @@ -2332,6 +2333,9 @@ struct ConvertConstructorToDeductionGuideTransform {
Args.addOuterRetainedLevel();
}

if (NestedPattern)
Args.addOuterRetainedLevels(NestedPattern->getTemplateDepth());

FunctionProtoTypeLoc FPTL = CD->getTypeSourceInfo()->getTypeLoc()
.getAsAdjusted<FunctionProtoTypeLoc>();
assert(FPTL && "no prototype for constructor declaration");
Expand Down Expand Up @@ -2441,10 +2445,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());
Expand Down Expand Up @@ -2650,13 +2661,24 @@ 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).
ContextRAII SavedContext(*this, Pattern->getTemplatedDecl());
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;
Expand Down Expand Up @@ -2702,6 +2724,8 @@ void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template,
Transform.buildSimpleDeductionGuide(Transform.DeducedType))
->getTemplatedDecl())
->setDeductionCandidateKind(DeductionCandidate::Copy);

SavedContext.pop();
}

/// Diagnose the presence of a default template argument on a
Expand Down
5 changes: 5 additions & 0 deletions clang/test/SemaTemplate/nested-deduction-guides.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
template<typename T> struct A {
template<typename U> struct B {
B(...);
B(const B &) = default;
};
template<typename U> B(U) -> B<U>;
};
A<void>::B b = 123;
A<void>::B copy = b;

using T = decltype(b);
using T = A<void>::B<int>;

using Copy = decltype(copy);
using Copy = A<void>::B<int>;
15 changes: 15 additions & 0 deletions clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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};

using T = decltype(x);
using T = S<int>::N<int>;

0 comments on commit f418319

Please sign in to comment.