https://github.com/antangelo created https://github.com/llvm/llvm-project/pull/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`. >From 37166cd7c7b2a0bb0ad5fe917856eacee2f69ff4 Mon Sep 17 00:00:00 2001 From: Antonio Abbatangelo <cont...@antangelo.com> Date: Wed, 1 Nov 2023 18:12:58 -0400 Subject: [PATCH] Reland "[clang][Sema] Use original template pattern when declaring implicit deduction guides for nested template classes" Reland of f418319730341e9d41ce8ead6fbfe5603c343985 with proper handling of FTDs --- clang/docs/ReleaseNotes.rst | 5 ++ clang/lib/Sema/SemaTemplate.cpp | 66 +++++++++++++++++-- .../SemaTemplate/nested-deduction-guides.cpp | 5 ++ .../nested-implicit-deduction-guides.cpp | 60 +++++++++++++++++ 4 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 clang/test/SemaTemplate/nested-implicit-deduction-guides.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 157afd9e8629152..ad5213aa30b20e9 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -734,6 +734,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>; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits