diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 157afd9e86291..ad5213aa30b20 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -734,6 +734,11 @@ Bug Fixes to C++ Support declaration definition. Fixes: (`#61763 `_) +- Fix a bug where implicit deduction guides are not correctly generated for nested template + classes. Fixes: + (`#46200 `_) + (`#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 `_) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index c188dd34014a4..34d7b8c731e90 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 AllParams; + SmallVector 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(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(); 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(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 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 2c5dda456a138..38410b93ead3b 100644 --- a/clang/test/SemaTemplate/nested-deduction-guides.cpp +++ b/clang/test/SemaTemplate/nested-deduction-guides.cpp @@ -4,10 +4,15 @@ template struct A { template struct B { B(...); + B(const B &) = default; }; template B(U) -> B; }; A::B b = 123; +A::B copy = b; using T = decltype(b); using T = A::B; + +using Copy = decltype(copy); +using Copy = A::B; 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 0000000000000..c44ec6918c7af --- /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 struct S { + template struct N { + N(T) {} + N(T, U) {} + template N(V, U) {} + }; +}; + +S::N x{"a", 1}; +using T = decltype(x); +using T = S::N; + +template struct default_ftd_argument { + template struct B { + template B(Y); + }; +}; + +default_ftd_argument::B default_arg("a"); +using DefaultArg = decltype(default_arg); +using DefaultArg = default_ftd_argument::B; + +template struct test; +template struct non_type_param { + template struct B { + B(Y); + template = 0> B(Z); + }; +}; + +non_type_param::B ntp = 5; +using NonTypeParam = decltype(ntp); +using NonTypeParam = non_type_param::B; + +template +concept C = (sizeof(T) == sizeof(A)); + +template struct concepts { + template struct B { + template Z> B(Y, Z); + }; +}; + +concepts::B cc(1, 3); +using Concepts = decltype(cc); +using Concepts = concepts::B; + +template struct requires_clause { + template struct B { + template requires (sizeof(Z) == sizeof(X)) + B(Y, Z); + }; +}; + +requires_clause::B req(1, 2); +using RC = decltype(req); +using RC = requires_clause::B;