https://github.com/oToToT updated https://github.com/llvm/llvm-project/pull/182667
>From 731efa8da5d21f3aafa2091c05dcae78e926f1f9 Mon Sep 17 00:00:00 2001 From: Tommy Chiang <[email protected]> Date: Fri, 20 Feb 2026 02:40:57 +0800 Subject: [PATCH] [Clang][ItaniumMangle] Fix recursive mangling for lambda init-captures Mangle computation for lambda signatures can recurse when a call operator type references an init-capture (for example via decltype(init-capture)). In these cases, mangling can re-enter the init-capture declaration and cycle back through operator() mangling. Make lambda context publication explicit and independent from numbering state, then use that context uniformly during mangling: * Publish lambda `ContextDecl` in `Sema::handleLambdaNumbering()` before numbering, so dependent type mangling can resolve the lambda context without recursing through the call operator. * Introduce `CXXRecordDecl::setLambdaContextDecl()` and remove `ContextDecl` from `CXXRecordDecl::LambdaNumbering`. * Update `ASTImporter` to import/set lambda context separately from numbering. * In Itanium mangling, derive init-capture handling from context computation: - map local-lambda init-captures to the enclosing local context in `getEffectiveDeclContext()` - support init-capture variables in `getClosurePrefix()` - keep `mangleLocalName()` generic and rely on the computed context Add mangling regression coverage in mangle-lambdas.cpp, including: * local init-captures used through decltype * non-local variable-template init-captures in decltype * non-local static inline member init-captures in decltype * Fixes https://github.com/llvm/llvm-project/issues/63271 * Fixes https://github.com/llvm/llvm-project/issues/86240 * Fixes https://github.com/llvm/llvm-project/issues/139089 --- clang/include/clang/AST/DeclCXX.h | 13 ++--- clang/lib/AST/ASTImporter.cpp | 10 ++-- clang/lib/AST/DeclCXX.cpp | 6 ++- clang/lib/AST/ItaniumMangle.cpp | 38 +++++++++++--- clang/lib/Sema/SemaLambda.cpp | 24 ++++++--- clang/test/CodeGenCXX/mangle-lambdas.cpp | 66 ++++++++++++++++++++++++ 6 files changed, 132 insertions(+), 25 deletions(-) diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 15dda098bad47..48fd12efdcafe 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -1791,6 +1791,9 @@ class CXXRecordDecl : public RecordDecl { /// the declaration context suffices. Decl *getLambdaContextDecl() const; + /// Set the context declaration for a lambda class. + void setLambdaContextDecl(Decl *ContextDecl); + /// Retrieve the index of this lambda within the context declaration returned /// by getLambdaContextDecl(). unsigned getLambdaIndexInContext() const { @@ -1800,21 +1803,19 @@ class CXXRecordDecl : public RecordDecl { /// Information about how a lambda is numbered within its context. struct LambdaNumbering { - Decl *ContextDecl = nullptr; unsigned IndexInContext = 0; unsigned ManglingNumber = 0; unsigned DeviceManglingNumber = 0; bool HasKnownInternalLinkage = false; }; - /// Set the mangling numbers and context declaration for a lambda class. + /// Set the mangling numbers for a lambda class. void setLambdaNumbering(LambdaNumbering Numbering); - // Get the mangling numbers and context declaration for a lambda class. + // Get the mangling numbers for a lambda class. LambdaNumbering getLambdaNumbering() const { - return {getLambdaContextDecl(), getLambdaIndexInContext(), - getLambdaManglingNumber(), getDeviceLambdaManglingNumber(), - hasKnownLambdaInternalLinkage()}; + return {getLambdaIndexInContext(), getLambdaManglingNumber(), + getDeviceLambdaManglingNumber(), hasKnownLambdaInternalLinkage()}; } /// Retrieve the device side mangling number. diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 8d8cc5426146d..c41d0c744e664 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3444,12 +3444,14 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { DC, *TInfoOrErr, Loc, DCXX->getLambdaDependencyKind(), DCXX->isGenericLambda(), DCXX->getLambdaCaptureDefault())) return D2CXX; - CXXRecordDecl::LambdaNumbering Numbering = DCXX->getLambdaNumbering(); - ExpectedDecl CDeclOrErr = import(Numbering.ContextDecl); + Decl *ContextDecl = DCXX->getLambdaContextDecl(); + ExpectedDecl CDeclOrErr = import(ContextDecl); if (!CDeclOrErr) return CDeclOrErr.takeError(); - Numbering.ContextDecl = *CDeclOrErr; - D2CXX->setLambdaNumbering(Numbering); + if (ContextDecl != nullptr) { + D2CXX->setLambdaContextDecl(*CDeclOrErr); + } + D2CXX->setLambdaNumbering(DCXX->getLambdaNumbering()); } else { if (GetImportedOrCreateDecl(D2CXX, D, Importer.getToContext(), D->getTagKind(), DC, *BeginLocOrErr, Loc, diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 37bc61ca35c4b..083c53e28cb91 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -1837,6 +1837,11 @@ Decl *CXXRecordDecl::getLambdaContextDecl() const { return getLambdaData().ContextDecl.get(Source); } +void CXXRecordDecl::setLambdaContextDecl(Decl *ContextDecl) { + assert(isLambda() && "Not a lambda closure type!"); + getLambdaData().ContextDecl = ContextDecl; +} + void CXXRecordDecl::setLambdaNumbering(LambdaNumbering Numbering) { assert(isLambda() && "Not a lambda closure type!"); getLambdaData().ManglingNumber = Numbering.ManglingNumber; @@ -1844,7 +1849,6 @@ void CXXRecordDecl::setLambdaNumbering(LambdaNumbering Numbering) { getASTContext().DeviceLambdaManglingNumbers[this] = Numbering.DeviceManglingNumber; getLambdaData().IndexInContext = Numbering.IndexInContext; - getLambdaData().ContextDecl = Numbering.ContextDecl; getLambdaData().HasKnownInternalLinkage = Numbering.HasKnownInternalLinkage; } diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index df00760fa911b..94358b6f9c1dc 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -633,6 +633,19 @@ NamespaceDecl *ItaniumMangleContextImpl::getStdNamespace() { return StdNamespace; } +/// Retrieve the lambda associated with an init-capture variable. +static const CXXRecordDecl *getLambdaForInitCapture(const VarDecl *VD) { + if (!VD || !VD->isInitCapture()) + return nullptr; + + const auto *Method = cast<CXXMethodDecl>(VD->getDeclContext()); + const auto *Lambda = dyn_cast<CXXRecordDecl>(Method->getParent()); + if (!Lambda || !Lambda->isLambda()) + return nullptr; + + return Lambda; +} + /// Retrieve the declaration context that should be used when mangling the given /// declaration. const DeclContext * @@ -674,9 +687,18 @@ ItaniumMangleContextImpl::getEffectiveDeclContext(const Decl *D) { return getEffectiveDeclContext(cast<Decl>(DC)); } - if (const auto *VD = dyn_cast<VarDecl>(D)) + if (const auto *VD = dyn_cast<VarDecl>(D)) { + if (const CXXRecordDecl *Lambda = getLambdaForInitCapture(VD)) { + const DeclContext *ParentDC = getEffectiveParentContext(Lambda); + // Init-captures in local lambdas are mangled relative to the enclosing + // local context rather than operator() to avoid recursive local-name + // encoding through the call operator type. + if (isLocalContainerContext(ParentDC)) + return ParentDC; + } if (VD->isExternC()) return getASTContext().getTranslationUnitDecl(); + } if (const auto *FD = getASTContext().getLangOpts().getClangABICompat() > LangOptions::ClangABI::Ver19 @@ -1873,12 +1895,13 @@ void CXXNameMangler::mangleLocalName(GlobalDecl GD, { AbiTagState LocalAbiTags(AbiTags); - if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(DC)) + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(DC)) { mangleObjCMethodName(MD); - else if (const BlockDecl *BD = dyn_cast<BlockDecl>(DC)) + } else if (const BlockDecl *BD = dyn_cast<BlockDecl>(DC)) { mangleBlockForPrefix(BD); - else + } else { mangleFunctionEncoding(getParentOfLocalEntity(DC)); + } // Implicit ABI tags (from namespace) are not available in the following // entity; reset to actually emitted tags, which are available. @@ -2277,6 +2300,9 @@ const NamedDecl *CXXNameMangler::getClosurePrefix(const Decl *ND) { const NamedDecl *Context = nullptr; if (auto *Block = dyn_cast<BlockDecl>(ND)) { Context = dyn_cast_or_null<NamedDecl>(Block->getBlockManglingContextDecl()); + } else if (auto *VD = dyn_cast<VarDecl>(ND)) { + if (const CXXRecordDecl *Lambda = getLambdaForInitCapture(VD)) + Context = dyn_cast_or_null<NamedDecl>(Lambda->getLambdaContextDecl()); } else if (auto *RD = dyn_cast<CXXRecordDecl>(ND)) { if (RD->isLambda()) Context = dyn_cast_or_null<NamedDecl>(RD->getLambdaContextDecl()); @@ -2284,8 +2310,8 @@ const NamedDecl *CXXNameMangler::getClosurePrefix(const Decl *ND) { if (!Context) return nullptr; - // Only lambdas within the initializer of a non-local variable or non-static - // data member get a <closure-prefix>. + // Only entities associated with lambdas within the initializer of a + // non-local variable or non-static data member get a <closure-prefix>. if ((isa<VarDecl>(Context) && cast<VarDecl>(Context)->hasGlobalStorage()) || isa<FieldDecl>(Context)) return Context; diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index e74fe02bd0cf5..013d2de62b977 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -478,11 +478,6 @@ bool Sema::DiagnoseInvalidExplicitObjectParameterInLambda( void Sema::handleLambdaNumbering( CXXRecordDecl *Class, CXXMethodDecl *Method, std::optional<CXXRecordDecl::LambdaNumbering> NumberingOverride) { - if (NumberingOverride) { - Class->setLambdaNumbering(*NumberingOverride); - return; - } - ContextRAII ManglingContext(*this, Class->getDeclContext()); auto getMangleNumberingContext = @@ -499,10 +494,23 @@ void Sema::handleLambdaNumbering( return &Context.getManglingNumberContext(DC); }; - CXXRecordDecl::LambdaNumbering Numbering; MangleNumberingContext *MCtx; - std::tie(MCtx, Numbering.ContextDecl) = + Decl *ContextDecl; + std::tie(MCtx, ContextDecl) = getCurrentMangleNumberContext(Class->getDeclContext()); + // getManglingNumber(Method) below may trigger mangling of dependent types + // that reference init-captures. Publish the lambda context declaration early + // so such mangling can resolve the surrounding context without recursing + // through the lambda call operator. This avoids publishing provisional + // numbering state before final numbering is assigned below. + if (ContextDecl) + Class->setLambdaContextDecl(ContextDecl); + if (NumberingOverride) { + Class->setLambdaNumbering(*NumberingOverride); + return; + } + + CXXRecordDecl::LambdaNumbering Numbering; if (!MCtx && (getLangOpts().CUDA || getLangOpts().SYCLIsDevice || getLangOpts().SYCLIsHost)) { // Force lambda numbering in CUDA/HIP as we need to name lambdas following @@ -512,7 +520,7 @@ void Sema::handleLambdaNumbering( // Also force for SYCL, since we need this for the // __builtin_sycl_unique_stable_name implementation, which depends on lambda // mangling. - MCtx = getMangleNumberingContext(Class, Numbering.ContextDecl); + MCtx = getMangleNumberingContext(Class, ContextDecl); assert(MCtx && "Retrieving mangle numbering context failed!"); Numbering.HasKnownInternalLinkage = true; } diff --git a/clang/test/CodeGenCXX/mangle-lambdas.cpp b/clang/test/CodeGenCXX/mangle-lambdas.cpp index 5a7de97c91858..d9ac0d39956e9 100644 --- a/clang/test/CodeGenCXX/mangle-lambdas.cpp +++ b/clang/test/CodeGenCXX/mangle-lambdas.cpp @@ -301,6 +301,70 @@ void test_StaticInlineMember() { StaticInlineMember::x(); } +template <typename L> +auto pr63271foo(L l_) { + return [l = l_](decltype(l)) -> void {}; +} + +// CHECK-LABEL: define{{.*}} @_Z10pr63271usev +// CHECK: call void @_ZZ10pr63271fooIiEDaT_ENKUliE_clEi +void pr63271use() { + pr63271foo(0)(0); +} + +template <typename T> +struct pr139089_vlambda { + pr139089_vlambda() { + [&m = m_args](decltype(m) args) { (void)args; }(m_args); + } + int m_args = 0; +}; + +// CHECK-LABEL: define{{.*}} @_Z11pr139089usev +// CHECK: call void @_ZN16pr139089_vlambdaIiEC1Ev +void pr139089use() { + (void)pr139089_vlambda<int>{}; +} + + +template <class RET> struct pr86240_context { + using Ptr = pr86240_context<RET> *; +}; + +template <typename Callable> +void pr86240_schedule_coro(Callable &&coro_function) { + [coro_function{coro_function}]( + typename pr86240_context<decltype(coro_function())>::Ptr ctx) -> int { + return ctx != nullptr; + }(nullptr); +} + +// CHECK-LABEL: define{{.*}} @_Z10pr86240usev +// CHECK: call noundef i32 @"_ZZ21pr86240_schedule_coroIZ10pr86240usevE3$_0EvOT_ENKUlP15pr86240_contextIiEE_clES5_" +void pr86240use() { + pr86240_schedule_coro([] { return 0; }); +} + +template <typename T> +auto nonLocalVarTemplate = [x = T{}](decltype(x) y) { return y; }; + +// CHECK-LABEL: define{{.*}} @_Z22nonLocalVarTemplateUsev +// CHECK: call noundef i32 @_ZNK19nonLocalVarTemplateIiEMUliE_clEi +int nonLocalVarTemplateUse() { + return nonLocalVarTemplate<int>(2); +} + +template <typename T> +struct nonLocalStaticInlineMember { + static inline auto l = [x = T{}](decltype(x) y) { return y; }; +}; + +// CHECK-LABEL: define{{.*}} @_Z29nonLocalStaticInlineMemberUsev +// CHECK: call noundef i32 @_ZNK26nonLocalStaticInlineMemberIiE1lMUliE_clEi +int nonLocalStaticInlineMemberUse() { + return nonLocalStaticInlineMember<int>::l(2); +} + // Check linkage of the various lambdas. // CHECK-LABEL: define linkonce_odr noundef i32 @_ZZ11inline_funciENKUlvE_clEv // CHECK: ret i32 1 @@ -331,6 +395,8 @@ void test_StaticInlineMember() { // CHECK-LABEL: define linkonce_odr void @_Z1fIZZNK23TestNestedInstantiationclEvENKUlvE_clEvEUlvE_EvT_ +// CHECK-LABEL: define linkonce_odr void @_ZZN16pr139089_vlambdaIiEC1EvENKUlRiE_clES1_ + namespace PR12808 { template <typename> struct B { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
