[clang] [clang] Don't use `VarDecl` of local variables as `ManglingContextDecl` for lambdas (PR #179035)
Jan Kokemüller via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 6 17:53:34 PST 2026
https://github.com/jiixyj updated https://github.com/llvm/llvm-project/pull/179035
>From 72d814fb6d05ae529538d5b673277e6c065bbbb5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 31 Jan 2026 14:26:59 +0100
Subject: [PATCH 01/10] Don't use VarDecl of local variables as
ManglingContextDecl for lambdas
---
clang/lib/Sema/SemaLambda.cpp | 8 ++++++++
clang/test/Modules/pr178893.cppm | 29 +++++++++++++++++++++++++++++
2 files changed, 37 insertions(+)
create mode 100644 clang/test/Modules/pr178893.cppm
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index e74fe02bd0cf5..24426259bf01c 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -294,6 +294,14 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
bool IsInNonspecializedTemplate =
inTemplateInstantiation() || CurContext->isDependentContext();
+ // If we must allocate mangling numbers but the `ManglingContextDecl`
+ // is a local variable, use the `DeclContext` containing the lambda expression
+ // instead.
+ if (ManglingContextDecl)
+ if (VarDecl *Var = dyn_cast<VarDecl>(ManglingContextDecl);
+ Var && Var->isLocalVarDecl())
+ ManglingContextDecl = const_cast<Decl *>(cast<Decl>(DC));
+
// Default arguments of member function parameters that appear in a class
// definition, as well as the initializers of data members, receive special
// treatment. Identify them.
diff --git a/clang/test/Modules/pr178893.cppm b/clang/test/Modules/pr178893.cppm
new file mode 100644
index 0000000000000..6d2a599588667
--- /dev/null
+++ b/clang/test/Modules/pr178893.cppm
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-apple-macosx10.7.0 -fmodules -xc++ -emit-llvm -o - %s -w | FileCheck %s
+
+// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZN8PR178893W3mod6format5parseEPiENKUlvE_clEv
+// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZN8PR178893W3mod6format5parseEPiENKUlvE0_clEv
+
+export module mod;
+
+namespace PR178893 {
+ struct format {
+ static inline int parse(int* i)
+ {
+ int number;
+ number = [&]() -> int { return i[0]; }();
+
+ volatile bool b = true;
+ if (b) {
+ auto identifier = [&]() -> int { return i[1]; }();
+ return identifier;
+ }
+
+ return number;
+ }
+ };
+
+ int test_format() {
+ int n[2] = {1, 0};
+ return format::parse(n);
+ }
+}
>From d5dbb1738f1d2c90850d27ba5e3e7245e6f325ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sun, 1 Feb 2026 10:45:25 +0100
Subject: [PATCH 02/10] Use `dyn_cast_or_null` instead of null check plus
`dyn_cast`
Co-authored-by: Corentin Jabot <corentinjabot at gmail.com>
---
clang/lib/Sema/SemaLambda.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 24426259bf01c..cdefd60508164 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -297,8 +297,7 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
// If we must allocate mangling numbers but the `ManglingContextDecl`
// is a local variable, use the `DeclContext` containing the lambda expression
// instead.
- if (ManglingContextDecl)
- if (VarDecl *Var = dyn_cast<VarDecl>(ManglingContextDecl);
+ if (VarDecl *Var = dyn_cast_or_null<VarDecl>(ManglingContextDecl);
Var && Var->isLocalVarDecl())
ManglingContextDecl = const_cast<Decl *>(cast<Decl>(DC));
>From 42bb683d0d44e008408f542b01e192a4d192e273 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sun, 1 Feb 2026 10:56:51 +0100
Subject: [PATCH 03/10] Autoformat
---
clang/lib/Sema/SemaLambda.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index cdefd60508164..1066333722bd8 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -298,8 +298,8 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
// is a local variable, use the `DeclContext` containing the lambda expression
// instead.
if (VarDecl *Var = dyn_cast_or_null<VarDecl>(ManglingContextDecl);
- Var && Var->isLocalVarDecl())
- ManglingContextDecl = const_cast<Decl *>(cast<Decl>(DC));
+ Var && Var->isLocalVarDecl())
+ ManglingContextDecl = const_cast<Decl *>(cast<Decl>(DC));
// Default arguments of member function parameters that appear in a class
// definition, as well as the initializers of data members, receive special
>From 4be67cb8356bf53bfe074663dc47792cb66fecf5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Mon, 2 Feb 2026 13:10:49 +0100
Subject: [PATCH 04/10] Refactor so that fallback only applies to module case
---
clang/lib/Sema/SemaLambda.cpp | 27 ++++++++++++++++-----------
1 file changed, 16 insertions(+), 11 deletions(-)
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 1066333722bd8..f7fd1d1831500 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -294,13 +294,6 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
bool IsInNonspecializedTemplate =
inTemplateInstantiation() || CurContext->isDependentContext();
- // If we must allocate mangling numbers but the `ManglingContextDecl`
- // is a local variable, use the `DeclContext` containing the lambda expression
- // instead.
- if (VarDecl *Var = dyn_cast_or_null<VarDecl>(ManglingContextDecl);
- Var && Var->isLocalVarDecl())
- ManglingContextDecl = const_cast<Decl *>(cast<Decl>(DC));
-
// Default arguments of member function parameters that appear in a class
// definition, as well as the initializers of data members, receive special
// treatment. Identify them.
@@ -311,12 +304,24 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
// Yeah, I think the only cases left where lambdas don't need a
// mangling are when they have (effectively) internal linkage or appear
// in a non-inline function in a non-module translation unit.
- if (auto *ND = dyn_cast<NamedDecl>(ManglingContextDecl ? ManglingContextDecl
- : cast<Decl>(DC));
+
+ Decl *ManglingContextDeclForModule = [&]() {
+ if (!ManglingContextDecl || [&]() {
+ // If we must allocate mangling numbers but the
+ // `ManglingContextDecl` is a local variable, use the `DeclContext`
+ // containing the lambda expression instead.
+ VarDecl *Var = dyn_cast<VarDecl>(ManglingContextDecl);
+ return Var && Var->isLocalVarDecl();
+ }())
+ return const_cast<Decl *>(cast<Decl>(DC));
+
+ return ManglingContextDecl;
+ }();
+
+ if (auto *ND = dyn_cast<NamedDecl>(ManglingContextDeclForModule);
ND && (ND->isInNamedModule() || ND->isFromGlobalModule()) &&
ND->isExternallyVisible()) {
- if (!ManglingContextDecl)
- ManglingContextDecl = const_cast<Decl *>(cast<Decl>(DC));
+ ManglingContextDecl = ND;
return NonInlineInModulePurview;
}
>From 8b7701621926ec2e1acbf50fed35973227523ada Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 3 Feb 2026 16:49:40 +0100
Subject: [PATCH 05/10] Remove unneeded '-fmodules' flag
---
clang/test/Modules/pr178893.cppm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/Modules/pr178893.cppm b/clang/test/Modules/pr178893.cppm
index 6d2a599588667..eb4041c330cb3 100644
--- a/clang/test/Modules/pr178893.cppm
+++ b/clang/test/Modules/pr178893.cppm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++20 -triple x86_64-apple-macosx10.7.0 -fmodules -xc++ -emit-llvm -o - %s -w | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-apple-macosx10.7.0 -xc++ -emit-llvm -o - %s -w | FileCheck %s
// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZN8PR178893W3mod6format5parseEPiENKUlvE_clEv
// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZN8PR178893W3mod6format5parseEPiENKUlvE0_clEv
>From 49338edb86222be0354114c4d4f19ff596c81750 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 3 Feb 2026 16:50:32 +0100
Subject: [PATCH 06/10] Make test a bit more robust
Co-authored-by: Chuanqi Xu <yedeng.yd at linux.alibaba.com>
---
clang/test/Modules/pr178893.cppm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/Modules/pr178893.cppm b/clang/test/Modules/pr178893.cppm
index eb4041c330cb3..81f29ac087712 100644
--- a/clang/test/Modules/pr178893.cppm
+++ b/clang/test/Modules/pr178893.cppm
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-apple-macosx10.7.0 -xc++ -emit-llvm -o - %s -w | FileCheck %s
-// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZN8PR178893W3mod6format5parseEPiENKUlvE_clEv
+// CHECK-LABEL: define {{.*}}@_ZZN8PR178893W3mod6format5parseEPiENKUlvE_clEv
// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZN8PR178893W3mod6format5parseEPiENKUlvE0_clEv
export module mod;
>From 691f4ed6ee9fa27c09d843206fc74b107277932a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 3 Feb 2026 16:51:20 +0100
Subject: [PATCH 07/10] Make test a bit more robust, part 2
---
clang/test/Modules/pr178893.cppm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/Modules/pr178893.cppm b/clang/test/Modules/pr178893.cppm
index 81f29ac087712..70bb681f4235d 100644
--- a/clang/test/Modules/pr178893.cppm
+++ b/clang/test/Modules/pr178893.cppm
@@ -1,7 +1,7 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-apple-macosx10.7.0 -xc++ -emit-llvm -o - %s -w | FileCheck %s
// CHECK-LABEL: define {{.*}}@_ZZN8PR178893W3mod6format5parseEPiENKUlvE_clEv
-// CHECK-LABEL: define linkonce_odr noundef i32 @_ZZN8PR178893W3mod6format5parseEPiENKUlvE0_clEv
+// CHECK-LABEL: define {{.*}}@_ZZN8PR178893W3mod6format5parseEPiENKUlvE0_clEv
export module mod;
>From 4de014b1a13557e4296b1e0c7fe78733de241ce6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Tue, 3 Feb 2026 16:52:43 +0100
Subject: [PATCH 08/10] Use %itanium_abi_triple as triple for the test
---
clang/test/Modules/pr178893.cppm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/Modules/pr178893.cppm b/clang/test/Modules/pr178893.cppm
index 70bb681f4235d..e58e183d82aba 100644
--- a/clang/test/Modules/pr178893.cppm
+++ b/clang/test/Modules/pr178893.cppm
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++20 -triple x86_64-apple-macosx10.7.0 -xc++ -emit-llvm -o - %s -w | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple -xc++ -emit-llvm -o - %s -w | FileCheck %s
// CHECK-LABEL: define {{.*}}@_ZZN8PR178893W3mod6format5parseEPiENKUlvE_clEv
// CHECK-LABEL: define {{.*}}@_ZZN8PR178893W3mod6format5parseEPiENKUlvE0_clEv
>From c09989925f901fbc9d7a073ad13e222e5b52f68b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Fri, 6 Feb 2026 18:22:24 +0100
Subject: [PATCH 09/10] Check for modules in 'Normal' Kind using helper
function, removing 'NonInlineInModulePurview'
---
clang/lib/Sema/SemaLambda.cpp | 46 ++++++++++++-----------------------
1 file changed, 16 insertions(+), 30 deletions(-)
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index f7fd1d1831500..772dc9f9d1287 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -275,6 +275,20 @@ static bool isInInlineFunction(const DeclContext *DC) {
return false;
}
+// See discussion in https://github.com/itanium-cxx-abi/cxx-abi/issues/186
+//
+// zygoloid:
+// Yeah, I think the only cases left where lambdas don't need a
+// mangling are when they have (effectively) internal linkage or
+// appear in a non-inline function in a non-module translation unit.
+static bool isNonInlineInModulePurview(const Decl *ManglingContextDecl,
+ const DeclContext *DC) {
+ auto *ND = dyn_cast<NamedDecl>(ManglingContextDecl ? ManglingContextDecl
+ : cast<Decl>(DC));
+ return ND && ((ND->isInNamedModule() || ND->isFromGlobalModule()) &&
+ ND->isExternallyVisible());
+}
+
std::tuple<MangleNumberingContext *, Decl *>
Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
// Compute the context for allocating mangling numbers in the current
@@ -288,7 +302,6 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
InlineVariable,
TemplatedVariable,
Concept,
- NonInlineInModulePurview
} Kind = Normal;
bool IsInNonspecializedTemplate =
@@ -298,33 +311,6 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
// definition, as well as the initializers of data members, receive special
// treatment. Identify them.
Kind = [&]() {
- // See discussion in https://github.com/itanium-cxx-abi/cxx-abi/issues/186
- //
- // zygoloid:
- // Yeah, I think the only cases left where lambdas don't need a
- // mangling are when they have (effectively) internal linkage or appear
- // in a non-inline function in a non-module translation unit.
-
- Decl *ManglingContextDeclForModule = [&]() {
- if (!ManglingContextDecl || [&]() {
- // If we must allocate mangling numbers but the
- // `ManglingContextDecl` is a local variable, use the `DeclContext`
- // containing the lambda expression instead.
- VarDecl *Var = dyn_cast<VarDecl>(ManglingContextDecl);
- return Var && Var->isLocalVarDecl();
- }())
- return const_cast<Decl *>(cast<Decl>(DC));
-
- return ManglingContextDecl;
- }();
-
- if (auto *ND = dyn_cast<NamedDecl>(ManglingContextDeclForModule);
- ND && (ND->isInNamedModule() || ND->isFromGlobalModule()) &&
- ND->isExternallyVisible()) {
- ManglingContextDecl = ND;
- return NonInlineInModulePurview;
- }
-
if (!ManglingContextDecl)
return Normal;
@@ -364,7 +350,8 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
// -- the bodies of inline or templated functions
if ((IsInNonspecializedTemplate &&
!(ManglingContextDecl && isa<ParmVarDecl>(ManglingContextDecl))) ||
- isInInlineFunction(CurContext)) {
+ isInInlineFunction(CurContext) ||
+ isNonInlineInModulePurview(ManglingContextDecl, DC)) {
while (auto *CD = dyn_cast<CapturedDecl>(DC))
DC = CD->getParent();
return std::make_tuple(&Context.getManglingNumberContext(DC), nullptr);
@@ -373,7 +360,6 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
return std::make_tuple(nullptr, nullptr);
}
- case NonInlineInModulePurview:
case Concept:
// Concept definitions aren't code generated and thus aren't mangled,
// however the ManglingContextDecl is important for the purposes of
>From d73c3f874c4176af5ed8d7b7947146f27c98119c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kokem=C3=BCller?= <jan.kokemueller at gmail.com>
Date: Sat, 7 Feb 2026 02:16:45 +0100
Subject: [PATCH 10/10] Add 'isNonInlineInModulePurview' check in 'VarDecl'
case
---
clang/lib/Sema/SemaLambda.cpp | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 772dc9f9d1287..670642045f6ae 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -281,12 +281,15 @@ static bool isInInlineFunction(const DeclContext *DC) {
// Yeah, I think the only cases left where lambdas don't need a
// mangling are when they have (effectively) internal linkage or
// appear in a non-inline function in a non-module translation unit.
-static bool isNonInlineInModulePurview(const Decl *ManglingContextDecl,
- const DeclContext *DC) {
- auto *ND = dyn_cast<NamedDecl>(ManglingContextDecl ? ManglingContextDecl
- : cast<Decl>(DC));
- return ND && ((ND->isInNamedModule() || ND->isFromGlobalModule()) &&
- ND->isExternallyVisible());
+static bool isNonInlineInModulePurview(const NamedDecl *ND) {
+ return (ND->isInNamedModule() || ND->isFromGlobalModule()) &&
+ ND->isExternallyVisible();
+}
+
+static bool isNonInlineInModulePurview(const DeclContext *DC) {
+ if (auto *ND = dyn_cast<NamedDecl>(DC))
+ return isNonInlineInModulePurview(ND);
+ return false;
}
std::tuple<MangleNumberingContext *, Decl *>
@@ -323,6 +326,9 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
if (Var->getMostRecentDecl()->isInline())
return InlineVariable;
+ if (isNonInlineInModulePurview(Var))
+ return InlineVariable;
+
if (Var->getDeclContext()->isRecord() && IsInNonspecializedTemplate)
return TemplatedVariable;
@@ -351,7 +357,7 @@ Sema::getCurrentMangleNumberContext(const DeclContext *DC) {
if ((IsInNonspecializedTemplate &&
!(ManglingContextDecl && isa<ParmVarDecl>(ManglingContextDecl))) ||
isInInlineFunction(CurContext) ||
- isNonInlineInModulePurview(ManglingContextDecl, DC)) {
+ isNonInlineInModulePurview(CurContext)) {
while (auto *CD = dyn_cast<CapturedDecl>(DC))
DC = CD->getParent();
return std::make_tuple(&Context.getManglingNumberContext(DC), nullptr);
More information about the cfe-commits
mailing list