Skip to content

Commit 8c852ab

Browse files
authored
[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
1 parent 200f3bd commit 8c852ab

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
@@ -2492,9 +2492,6 @@ struct ConvertConstructorToDeductionGuideTransform {
24922492
Args.addOuterRetainedLevel();
24932493
}
24942494

2495-
if (NestedPattern)
2496-
Args.addOuterRetainedLevels(NestedPattern->getTemplateDepth());
2497-
24982495
FunctionProtoTypeLoc FPTL = CD->getTypeSourceInfo()->getTypeLoc()
24992496
.getAsAdjusted<FunctionProtoTypeLoc>();
25002497
assert(FPTL && "no prototype for constructor declaration");
@@ -2584,11 +2581,27 @@ struct ConvertConstructorToDeductionGuideTransform {
25842581

25852582
// -- The types of the function parameters are those of the constructor.
25862583
for (auto *OldParam : TL.getParams()) {
2587-
ParmVarDecl *NewParam =
2588-
transformFunctionTypeParam(OldParam, Args, MaterializedTypedefs);
2589-
if (NestedPattern && NewParam)
2584+
ParmVarDecl *NewParam = OldParam;
2585+
// Given
2586+
// template <class T> struct C {
2587+
// template <class U> struct D {
2588+
// template <class V> D(U, V);
2589+
// };
2590+
// };
2591+
// First, transform all the references to template parameters that are
2592+
// defined outside of the surrounding class template. That is T in the
2593+
// above example.
2594+
if (NestedPattern) {
25902595
NewParam = transformFunctionTypeParam(NewParam, OuterInstantiationArgs,
25912596
MaterializedTypedefs);
2597+
if (!NewParam)
2598+
return QualType();
2599+
}
2600+
// Then, transform all the references to template parameters that are
2601+
// defined at the class template and the constructor. In this example,
2602+
// they're U and V, respectively.
2603+
NewParam =
2604+
transformFunctionTypeParam(NewParam, Args, MaterializedTypedefs);
25922605
if (!NewParam)
25932606
return QualType();
25942607
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)