Skip to content

Commit 8d65f14

Browse files
zyn0217tstellar
authored andcommitted
[Clang][Sema] Revise the transformation of CTAD parameters of nested class templates (llvm#91628)
This fixes a regression introduced by bee78b8. When we form a deduction guide for a constructor, basically, we do the following work: - Collect template parameters from the constructor's surrounding class template, if present. - Collect template parameters from the constructor. - Splice these template parameters together into a new template parameter list. - Turn all the references (e.g. the function parameter list) to the invented parameter list by applying a `TreeTransform` to the function type. In the previous fix, we handled cases of nested class templates by substituting the "outer" template parameters (i.e. those not declared at the surrounding class template or the constructor) with the instantiating template arguments. The approach per se makes sense, but there was a flaw in the following case: ```cpp template <typename U, typename... Us> struct X { template <typename V> struct Y { template <typename T> Y(T) {} }; template <typename T> Y(T) -> Y<T>; }; X<int>::Y y(42); ``` While we're transforming the parameters for `Y(T)`, we first attempt to transform all references to `V` and `T`; then, we handle the references to outer parameters `U` and `Us` using the template arguments from `X<int>` by transforming the same `ParamDecl`. However, the first step results in the reference `T` being `<template-param-0-1>` because the invented `T` is the last of the parameter list of the deduction guide, and what we're substituting with is a corresponding parameter pack (which is `Us`, though empty). Hence we're messing up the substitution. I think we can resolve it by reversing the substitution order, which means handling outer template parameters first and then the inner parameters. There's no release note because this is a regression in 18, and I hope we can catch up with the last release. Fixes llvm#88142 (cherry picked from commit 8c852ab)
1 parent be6c817 commit 8d65f14

File tree

2 files changed

+33
-6
lines changed

2 files changed

+33
-6
lines changed

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2404,9 +2404,6 @@ struct ConvertConstructorToDeductionGuideTransform {
24042404
Args.addOuterRetainedLevel();
24052405
}
24062406

2407-
if (NestedPattern)
2408-
Args.addOuterRetainedLevels(NestedPattern->getTemplateDepth());
2409-
24102407
FunctionProtoTypeLoc FPTL = CD->getTypeSourceInfo()->getTypeLoc()
24112408
.getAsAdjusted<FunctionProtoTypeLoc>();
24122409
assert(FPTL && "no prototype for constructor declaration");
@@ -2526,11 +2523,27 @@ struct ConvertConstructorToDeductionGuideTransform {
25262523

25272524
// -- The types of the function parameters are those of the constructor.
25282525
for (auto *OldParam : TL.getParams()) {
2529-
ParmVarDecl *NewParam =
2530-
transformFunctionTypeParam(OldParam, Args, MaterializedTypedefs);
2531-
if (NestedPattern && NewParam)
2526+
ParmVarDecl *NewParam = OldParam;
2527+
// Given
2528+
// template <class T> struct C {
2529+
// template <class U> struct D {
2530+
// template <class V> D(U, V);
2531+
// };
2532+
// };
2533+
// First, transform all the references to template parameters that are
2534+
// defined outside of the surrounding class template. That is T in the
2535+
// above example.
2536+
if (NestedPattern) {
25322537
NewParam = transformFunctionTypeParam(NewParam, OuterInstantiationArgs,
25332538
MaterializedTypedefs);
2539+
if (!NewParam)
2540+
return QualType();
2541+
}
2542+
// Then, transform all the references to template parameters that are
2543+
// defined at the class template and the constructor. In this example,
2544+
// they're U and V, respectively.
2545+
NewParam =
2546+
transformFunctionTypeParam(NewParam, Args, MaterializedTypedefs);
25342547
if (!NewParam)
25352548
return QualType();
25362549
ParamTypes.push_back(NewParam->getType());

clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,17 @@ nested_init_list<int>::concept_fail nil_invalid{1, ""};
8484
// expected-note@#INIT_LIST_INNER_INVALID {{candidate template ignored: substitution failure [with F = const char *]: constraints not satisfied for class template 'concept_fail' [with F = const char *]}}
8585
// expected-note@#INIT_LIST_INNER_INVALID {{candidate function template not viable: requires 1 argument, but 2 were provided}}
8686
// expected-note@#INIT_LIST_INNER_INVALID {{candidate function template not viable: requires 0 arguments, but 2 were provided}}
87+
88+
namespace GH88142 {
89+
90+
template <typename, typename...> struct X {
91+
template <typename> struct Y {
92+
template <typename T> Y(T) {}
93+
};
94+
95+
template <typename T> Y(T) -> Y<T>;
96+
};
97+
98+
X<int>::Y y(42);
99+
100+
} // namespace PR88142

0 commit comments

Comments
 (0)