[clang] bee78b8 - Reland "[clang][Sema] Use original template pattern when declaring implicit deduction guides for nested template classes" (#73087)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Nov 25 11:26:48 PST 2023
Author: antangelo
Date: 2023-11-25T14:26:44-05:00
New Revision: bee78b88f8effdb378a809e0416988ce9c37f5ac
URL: https://github.com/llvm/llvm-project/commit/bee78b88f8effdb378a809e0416988ce9c37f5ac
DIFF: https://github.com/llvm/llvm-project/commit/bee78b88f8effdb378a809e0416988ce9c37f5ac.diff
LOG: Reland "[clang][Sema] Use original template pattern when declaring implicit deduction guides for nested template classes" (#73087)
Reland of f418319730341e9d41ce8ead6fbfe5603c343985 with proper handling
of template constructors
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 last iteration:
1. In template constructors, arguments are first rewritten to depth - 1
relative to the constructor as compared to depth 0 originally. These
arguments are needed for substitution into constraint expressions.
2. Outer arguments are then applied with the template instantiator to
produce a template argument at depth zero for use in the deduction
guide. This substitution does not evaluate constraints, which preserves
constraint arguments at the correct depth for later evaluation.
3. Tests are added that cover template constructors within nested
deduction guides for all special substitution cases.
4. Computation of the template pattern and outer instantiation arguments
are pulled into the constructor of
`ConvertConstructorToDeductionGuideTransform`.
Added:
clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp
Modified:
clang/docs/ReleaseNotes.rst
clang/lib/Sema/SemaTemplate.cpp
clang/test/SemaTemplate/nested-deduction-guides.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0ce15169d49bcce..29a06d0f713f588 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -741,6 +741,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>`_)
+
- Diagnose use of a variable-length array in a coroutine. The design of
coroutines is such that it is not possible to support VLA use. Fixes:
(`#65858 <https://github.com/llvm/llvm-project/issues/65858>`_)
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index c188dd34014a4b3..34d7b8c731e9076 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2250,10 +2250,24 @@ class ExtractTypeForDeductionGuide
struct ConvertConstructorToDeductionGuideTransform {
ConvertConstructorToDeductionGuideTransform(Sema &S,
ClassTemplateDecl *Template)
- : SemaRef(S), Template(Template) {}
+ : SemaRef(S), Template(Template) {
+ // If the template is nested, then we need to use the original
+ // pattern to iterate over the constructors.
+ ClassTemplateDecl *Pattern = Template;
+ while (Pattern->getInstantiatedFromMemberTemplate()) {
+ if (Pattern->isMemberSpecialization())
+ break;
+ Pattern = Pattern->getInstantiatedFromMemberTemplate();
+ NestedPattern = Pattern;
+ }
+
+ if (NestedPattern)
+ OuterInstantiationArgs = SemaRef.getTemplateInstantiationArgs(Template);
+ }
Sema &SemaRef;
ClassTemplateDecl *Template;
+ ClassTemplateDecl *NestedPattern = nullptr;
DeclContext *DC = Template->getDeclContext();
CXXRecordDecl *Primary = Template->getTemplatedDecl();
@@ -2266,6 +2280,10 @@ struct ConvertConstructorToDeductionGuideTransform {
// depth-0 template parameters.
unsigned Depth1IndexAdjustment = Template->getTemplateParameters()->size();
+ // Instantiation arguments for the outermost depth-1 templates
+ // when the template is nested
+ MultiLevelTemplateArgumentList OuterInstantiationArgs;
+
/// Transform a constructor declaration into a deduction guide.
NamedDecl *transformConstructor(FunctionTemplateDecl *FTD,
CXXConstructorDecl *CD) {
@@ -2284,21 +2302,43 @@ struct ConvertConstructorToDeductionGuideTransform {
if (FTD) {
TemplateParameterList *InnerParams = FTD->getTemplateParameters();
SmallVector<NamedDecl *, 16> AllParams;
+ SmallVector<TemplateArgument, 16> Depth1Args;
AllParams.reserve(TemplateParams->size() + InnerParams->size());
AllParams.insert(AllParams.begin(),
TemplateParams->begin(), TemplateParams->end());
SubstArgs.reserve(InnerParams->size());
+ Depth1Args.reserve(InnerParams->size());
// Later template parameters could refer to earlier ones, so build up
// a list of substituted template arguments as we go.
for (NamedDecl *Param : *InnerParams) {
MultiLevelTemplateArgumentList Args;
Args.setKind(TemplateSubstitutionKind::Rewrite);
- Args.addOuterTemplateArguments(SubstArgs);
+ Args.addOuterTemplateArguments(Depth1Args);
Args.addOuterRetainedLevel();
+ if (NestedPattern)
+ Args.addOuterRetainedLevels(NestedPattern->getTemplateDepth());
NamedDecl *NewParam = transformTemplateParameter(Param, Args);
if (!NewParam)
return nullptr;
+
+ // Constraints require that we substitute depth-1 arguments
+ // to match depths when substituted for evaluation later
+ Depth1Args.push_back(SemaRef.Context.getCanonicalTemplateArgument(
+ SemaRef.Context.getInjectedTemplateArg(NewParam)));
+
+ if (NestedPattern) {
+ TemplateDeclInstantiator Instantiator(SemaRef, DC,
+ OuterInstantiationArgs);
+ Instantiator.setEvaluateConstraints(false);
+ SemaRef.runWithSufficientStackSpace(NewParam->getLocation(), [&] {
+ NewParam = cast<NamedDecl>(Instantiator.Visit(NewParam));
+ });
+ }
+
+ assert(NewParam->getTemplateDepth() == 0 &&
+ "Unexpected template parameter depth");
+
AllParams.push_back(NewParam);
SubstArgs.push_back(SemaRef.Context.getCanonicalTemplateArgument(
SemaRef.Context.getInjectedTemplateArg(NewParam)));
@@ -2309,8 +2349,10 @@ struct ConvertConstructorToDeductionGuideTransform {
if (Expr *InnerRC = InnerParams->getRequiresClause()) {
MultiLevelTemplateArgumentList Args;
Args.setKind(TemplateSubstitutionKind::Rewrite);
- Args.addOuterTemplateArguments(SubstArgs);
+ Args.addOuterTemplateArguments(Depth1Args);
Args.addOuterRetainedLevel();
+ if (NestedPattern)
+ Args.addOuterRetainedLevels(NestedPattern->getTemplateDepth());
ExprResult E = SemaRef.SubstExpr(InnerRC, Args);
if (E.isInvalid())
return nullptr;
@@ -2333,6 +2375,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");
@@ -2394,7 +2439,7 @@ struct ConvertConstructorToDeductionGuideTransform {
// substitute it directly.
auto *NewTTP = TemplateTypeParmDecl::Create(
SemaRef.Context, DC, TTP->getBeginLoc(), TTP->getLocation(),
- /*Depth*/ 0, Depth1IndexAdjustment + TTP->getIndex(),
+ TTP->getDepth() - 1, Depth1IndexAdjustment + TTP->getIndex(),
TTP->getIdentifier(), TTP->wasDeclaredWithTypename(),
TTP->isParameterPack(), TTP->hasTypeConstraint(),
TTP->isExpandedParameterPack()
@@ -2429,7 +2474,8 @@ struct ConvertConstructorToDeductionGuideTransform {
// the index of the parameter once it's done.
auto *NewParam =
cast<TemplateParmDecl>(SemaRef.SubstDecl(OldParam, DC, Args));
- assert(NewParam->getDepth() == 0 && "unexpected template param depth");
+ assert(NewParam->getDepth() == OldParam->getDepth() - 1 &&
+ "unexpected template param depth");
NewParam->setPosition(NewParam->getPosition() + Depth1IndexAdjustment);
return NewParam;
}
@@ -2446,6 +2492,9 @@ struct ConvertConstructorToDeductionGuideTransform {
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());
@@ -2655,9 +2704,12 @@ void Sema::DeclareImplicitDeductionGuides(TemplateDecl *Template,
// 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).
+ ClassTemplateDecl *Pattern =
+ Transform.NestedPattern ? Transform.NestedPattern : Transform.Template;
+ 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;
@@ -2703,6 +2755,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
diff --git a/clang/test/SemaTemplate/nested-deduction-guides.cpp b/clang/test/SemaTemplate/nested-deduction-guides.cpp
index 2c5dda456a138a8..38410b93ead3b97 100644
--- a/clang/test/SemaTemplate/nested-deduction-guides.cpp
+++ b/clang/test/SemaTemplate/nested-deduction-guides.cpp
@@ -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>;
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..c44ec6918c7afb1
--- /dev/null
+++ b/clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp
@@ -0,0 +1,60 @@
+// RUN: %clang_cc1 -std=c++20 -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>;
+
+template<class X> struct default_ftd_argument {
+ template<class Y> struct B {
+ template<class W = X, class Z = Y, class V = Z, int I = 0> B(Y);
+ };
+};
+
+default_ftd_argument<int>::B default_arg("a");
+using DefaultArg = decltype(default_arg);
+using DefaultArg = default_ftd_argument<int>::B<const char *>;
+
+template<bool> struct test;
+template<class X> struct non_type_param {
+ template<class Y> struct B {
+ B(Y);
+ template<class Z, test<Z::value> = 0> B(Z);
+ };
+};
+
+non_type_param<int>::B ntp = 5;
+using NonTypeParam = decltype(ntp);
+using NonTypeParam = non_type_param<int>::B<int>;
+
+template<typename A, typename T>
+concept C = (sizeof(T) == sizeof(A));
+
+template<class X> struct concepts {
+ template<class Y> struct B {
+ template<class K = X, C<K> Z> B(Y, Z);
+ };
+};
+
+concepts<int>::B cc(1, 3);
+using Concepts = decltype(cc);
+using Concepts = concepts<int>::B<int>;
+
+template<class X> struct requires_clause {
+ template<class Y> struct B {
+ template<class Z> requires (sizeof(Z) == sizeof(X))
+ B(Y, Z);
+ };
+};
+
+requires_clause<int>::B req(1, 2);
+using RC = decltype(req);
+using RC = requires_clause<int>::B<int>;
More information about the cfe-commits
mailing list