[clang] 8ce2b9c - [Clang][ItaniumMangle] Fix recursive mangling for lambda init-captures (#182667)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 27 11:18:15 PST 2026
Author: Tommy Chiang
Date: 2026-02-28T03:18:11+08:00
New Revision: 8ce2b9cbc2bcde50189e74650cf994e8f549f58d
URL: https://github.com/llvm/llvm-project/commit/8ce2b9cbc2bcde50189e74650cf994e8f549f58d
DIFF: https://github.com/llvm/llvm-project/commit/8ce2b9cbc2bcde50189e74650cf994e8f549f58d.diff
LOG: [Clang][ItaniumMangle] Fix recursive mangling for lambda init-captures (#182667)
[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
Added:
Modified:
clang/include/clang/AST/DeclCXX.h
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/DeclCXX.cpp
clang/lib/AST/ItaniumMangle.cpp
clang/lib/Sema/SemaLambda.cpp
clang/test/CodeGenCXX/mangle-lambdas.cpp
Removed:
################################################################################
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 7c34377fc6e1a..04201fc11c4ac 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -634,6 +634,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 *
@@ -675,9 +688,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
@@ -1874,12 +1896,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.
@@ -2278,6 +2301,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());
@@ -2285,8 +2311,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 be7f73dbc267f..8572e3a742a6c 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -491,11 +491,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 =
@@ -512,10 +507,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
@@ -525,7 +533,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 {
More information about the cfe-commits
mailing list