[clang] 7205d7c - [clang][ItaniumMangle] Fix mangling of lambdas in default member initializers of local classes (#206740)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 8 04:36:48 PDT 2026
Author: Kristina Bessonova
Date: 2026-07-08T13:36:43+02:00
New Revision: 7205d7c676c8f90a0cf89484c1a9687fa601b867
URL: https://github.com/llvm/llvm-project/commit/7205d7c676c8f90a0cf89484c1a9687fa601b867
DIFF: https://github.com/llvm/llvm-project/commit/7205d7c676c8f90a0cf89484c1a9687fa601b867.diff
LOG: [clang][ItaniumMangle] Fix mangling of lambdas in default member initializers of local classes (#206740)
Lambdas appearing in default member initializers of members of local
classes were previously mangled as if they belonged only to the class
scope, ignoring the enclosing function-local context.
This caused different (but same-named) local classes to produce
identical closure type mangling and corresponding RTTI/IR collisions.
Consider the following example:
```
void foo() {
{
struct T {
std::function<void()> a = [](){ std::cout << "a"; };
} t;
t.a();
}
{
struct T {
std::function<void()> a = [](){ std::cout << "b"; };
} t;
t.a();
}
}
```
The expected output is `ab` (and GCC-compiled code behaves accordingly),
while clang previously printed `bb` because the first `T::a` definition
is overridden by the second one due to the missing `<local-name>`
encoding and discriminator required to distinguish the two local `T`
definitions.
Added:
clang/test/CodeGenCXX/mangle-lambdas-in-dmi-local-class.cpp
Modified:
clang/docs/ReleaseNotes.md
clang/include/clang/Basic/ABIVersions.def
clang/lib/AST/ItaniumMangle.cpp
clang/test/CodeGenCXX/dtor-local-lambda-mangle.cpp
clang/test/CodeGenCXX/mangle-lambdas-gh88906.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 772cd64dd93f4..5de8e546b7812 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -156,6 +156,9 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
nested template types (for example, ones containing local lambdas) could
produce very large writable `.data` sections. Emitted RTTI name strings
change only for types whose name exceeds the length limit. (#GH206313)
+- Fixed Itanium mangling for lambdas in default member initializers of local
+ classes to use `<local-name>` encoding, preventing mangling collisions between
+ distinct local classes.
### AST Dumping Potentially Breaking Changes
diff --git a/clang/include/clang/Basic/ABIVersions.def b/clang/include/clang/Basic/ABIVersions.def
index 92edcd830f031..a9f72481ee0ea 100644
--- a/clang/include/clang/Basic/ABIVersions.def
+++ b/clang/include/clang/Basic/ABIVersions.def
@@ -135,6 +135,11 @@ ABI_VER_MAJOR(20)
/// operator delete.
ABI_VER_MAJOR(21)
+/// Attempt to be ABI-compatible with code generated by Clang 22.0.x.
+/// This causes clang to mangle lambdas in default member initializers of
+/// local classes as nested-name entities instead of local-name entities.
+ABI_VER_MAJOR(22)
+
/// Conform to the underlying platform's C and C++ ABIs as closely as we can.
ABI_VER_LATEST(Latest)
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index e5cdd6f31c507..257379e568bcc 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -530,7 +530,8 @@ class CXXNameMangler {
ArrayRef<TemplateArgument> Args);
void mangleNestedNameWithClosurePrefix(GlobalDecl GD,
const NamedDecl *PrefixND,
- ArrayRef<StringRef> AdditionalAbiTags);
+ ArrayRef<StringRef> AdditionalAbiTags,
+ bool NoFunction = false);
void manglePrefix(NestedNameSpecifier Qualifier);
void manglePrefix(const DeclContext *DC, bool NoFunction=false);
void manglePrefix(QualType type);
@@ -1067,10 +1068,10 @@ void CXXNameMangler::mangleNameWithAbiTags(
// ::= <local-name>
//
const DeclContext *DC = Context.getEffectiveDeclContext(ND);
- bool IsLambda = isLambda(ND);
if (GetLocalClassDecl(ND) &&
- (!IsLambda || isCompatibleWith(LangOptions::ClangABI::Ver18))) {
+ (!isLambda(ND) || isCompatibleWith(LangOptions::ClangABI::Ver18) ||
+ !isCompatibleWith(LangOptions::ClangABI::Ver22))) {
mangleLocalName(GD, AdditionalAbiTags);
return;
}
@@ -1845,7 +1846,7 @@ void CXXNameMangler::mangleNestedName(const TemplateDecl *TD,
void CXXNameMangler::mangleNestedNameWithClosurePrefix(
GlobalDecl GD, const NamedDecl *PrefixND,
- ArrayRef<StringRef> AdditionalAbiTags) {
+ ArrayRef<StringRef> AdditionalAbiTags, bool NoFunction) {
// A <closure-prefix> represents a variable or field, not a regular
// DeclContext, so needs special handling. In this case we're mangling a
// limited form of <nested-name>:
@@ -1854,7 +1855,7 @@ void CXXNameMangler::mangleNestedNameWithClosurePrefix(
Out << 'N';
- mangleClosurePrefix(PrefixND);
+ mangleClosurePrefix(PrefixND, NoFunction);
mangleUnqualifiedName(GD, nullptr, AdditionalAbiTags);
Out << 'E';
@@ -1945,8 +1946,13 @@ void CXXNameMangler::mangleLocalName(GlobalDecl GD,
mangleUnqualifiedBlock(BD);
} else {
const NamedDecl *ND = cast<NamedDecl>(D);
- mangleNestedName(GD, Context.getEffectiveDeclContext(ND),
- AdditionalAbiTags, true /*NoFunction*/);
+ const NamedDecl *PrefixND = getClosurePrefix(ND);
+ if (PrefixND && !isCompatibleWith(LangOptions::ClangABI::Ver18))
+ mangleNestedNameWithClosurePrefix(GD, PrefixND, AdditionalAbiTags,
+ /*NoFunction=*/true);
+ else
+ mangleNestedName(GD, Context.getEffectiveDeclContext(ND),
+ AdditionalAbiTags, /*NoFunction=*/true);
}
} else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) {
// Mangle a block in a default parameter; see above explanation for
diff --git a/clang/test/CodeGenCXX/dtor-local-lambda-mangle.cpp b/clang/test/CodeGenCXX/dtor-local-lambda-mangle.cpp
index 95283dad4a933..63d08e07a40dd 100644
--- a/clang/test/CodeGenCXX/dtor-local-lambda-mangle.cpp
+++ b/clang/test/CodeGenCXX/dtor-local-lambda-mangle.cpp
@@ -10,7 +10,7 @@ struct E {
E::E() {
struct {
- // CHECK-DAG: _ZTSN1EC13$_012anotherValueMUlvE_E
+ // CHECK-DAG: _ZTSZN1EC1EvEN3$_012anotherValueMUlvE_E
int anotherValue = [x = 1] { return x; }();
} obj;
}
@@ -18,14 +18,14 @@ E::E() {
template<typename T>
E::E(T t) {
struct {
- // CHECK-DAG: _ZTSN1EC1IiEUt_12anotherValueMUlvE_E
+ // CHECK-DAG: _ZTSZN1EC1IiEET_ENUt_12anotherValueMUlvE_E
int anotherValue = [x = 1] { return x; }();
} obj;
}
E::~E() {
struct {
- // CHECK-DAG: _ZTSN1ED13$_012anotherValueMUlvE_E
+ // CHECK-DAG: _ZTSZN1ED1EvEN3$_012anotherValueMUlvE_E
int anotherValue = [x = 2] { return x; }();
} obj;
}
diff --git a/clang/test/CodeGenCXX/mangle-lambdas-gh88906.cpp b/clang/test/CodeGenCXX/mangle-lambdas-gh88906.cpp
index e7592cec5da77..ba99906d358be 100644
--- a/clang/test/CodeGenCXX/mangle-lambdas-gh88906.cpp
+++ b/clang/test/CodeGenCXX/mangle-lambdas-gh88906.cpp
@@ -21,13 +21,13 @@ void GH88906(){
}
// CHECK-LABEL: define internal void @_ZZ7GH88906vEN4TestC2Ev
-// CHECK: call void @_ZN4funcC2IN7GH889064Test1aMUlvE_ENS3_UlvE0_EEET_T0_
-// CHECK: call void @_ZN4funcC2IN7GH889064Test1bMUlvE_EEET_
-// CHECK: call void @_ZN4funcC2IN7GH889064Test1cMUlvE_EEET_
+// CHECK: call void @_ZN4funcC2IZ7GH88906vEN4Test1aMUlvE_EZ7GH88906vENS2_UlvE0_EEET_T0_
+// CHECK: call void @_ZN4funcC2IZ7GH88906vEN4Test1bMUlvE_EEET_
+// CHECK: call void @_ZN4funcC2IZ7GH88906vEN4Test1cMUlvE_EEET_
-// CHECK-LABEL: define internal void @_ZN4funcC2IN7GH889064Test1aMUlvE_ENS3_UlvE0_EEET_T0_
-// CHECK-LABEL: define internal void @_ZN4funcC2IN7GH889064Test1bMUlvE_EEET_
-// CHECK-LABEL: define internal void @_ZN4funcC2IN7GH889064Test1cMUlvE_EEET_
+// CHECK-LABEL: define internal void @_ZN4funcC2IZ7GH88906vEN4Test1aMUlvE_EZ7GH88906vENS2_UlvE0_EEET_T0_
+// CHECK-LABEL: define internal void @_ZN4funcC2IZ7GH88906vEN4Test1bMUlvE_EEET_
+// CHECK-LABEL: define internal void @_ZN4funcC2IZ7GH88906vEN4Test1cMUlvE_EEET_
// CLANG18-LABEL: define internal void @_ZZ7GH88906vEN4TestC2Ev
// CLANG18: call void @_ZN4funcC2IZ7GH88906vEN4TestUlvE_EZ7GH88906vENS1_UlvE0_EEET_T0_
diff --git a/clang/test/CodeGenCXX/mangle-lambdas-in-dmi-local-class.cpp b/clang/test/CodeGenCXX/mangle-lambdas-in-dmi-local-class.cpp
new file mode 100644
index 0000000000000..7f3602ceeccfa
--- /dev/null
+++ b/clang/test/CodeGenCXX/mangle-lambdas-in-dmi-local-class.cpp
@@ -0,0 +1,79 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -emit-llvm -I%S -std=c++20 -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fclang-abi-compat=22 %s -emit-llvm -I%S -std=c++20 -o - | FileCheck --check-prefix=ABICOMPAT22 %s
+
+// Ensure that local classes mangled with <local-name> while mangling a lamda
+// in default member initializer.
+
+// Itanium ABI 5.1.2
+// Names
+// <...> Entities declared within a function, including members of local classes, are mangled with <local-name>. Entities declared in a namespace or class scope are mangled with <nested-name>. <...>
+
+#include <typeinfo>
+
+void foo() {
+ {
+ struct Test {
+ const std::type_info& a = typeid([] {});
+ const std::type_info& b = typeid([] {});
+ } T;
+ }
+
+ {
+ struct Test {
+ const std::type_info& a = typeid([] {});
+ const std::type_info& b = typeid([] {});
+ } T;
+ }
+}
+
+// _Z
+// TI
+// Z3foovE ; local-name scope
+// N
+// 4Test
+// 1a ; member a
+// M ; member-initializer context
+// UlvE_ ; lambda closure type
+// E
+
+// CHECK: @_ZTIZ3foovEN4Test1aMUlvE_E
+// CHECK: @_ZTSZ3foovEN4Test1aMUlvE_E
+// CHECK: @_ZTIZ3foovEN4Test1bMUlvE_E
+// CHECK: @_ZTSZ3foovEN4Test1bMUlvE_E
+
+// _Z
+// TI
+// Z3foovE ; local-name scope
+// N
+// 4Test
+// 1a ; member a
+// M ; member-initializer context
+// UlvE_ ; lambda closure type
+// E
+// _0 ; second local 'Test' definition
+
+// CHECK: @_ZTIZ3foovEN4Test1aMUlvE_E_0
+// CHECK: @_ZTSZ3foovEN4Test1aMUlvE_E_0
+// CHECK: @_ZTIZ3foovEN4Test1bMUlvE_E_0
+// CHECK: @_ZTSZ3foovEN4Test1bMUlvE_E_0
+
+// CHECK-LABEL: define internal void @_ZZ3foovEN4TestC2Ev
+// CHECK: store ptr @_ZTIZ3foovEN4Test1aMUlvE_E
+// CHECK: store ptr @_ZTIZ3foovEN4Test1bMUlvE_E
+
+// CHECK-LABEL: define internal void @_ZZ3foovEN4TestC2E_0v
+// CHECK: store ptr @_ZTIZ3foovEN4Test1aMUlvE_E_0
+// CHECK: store ptr @_ZTIZ3foovEN4Test1bMUlvE_E_0
+
+// ABICOMPAT22: @_ZTIN3foo4Test1aMUlvE_E
+// ABICOMPAT22: @_ZTSN3foo4Test1aMUlvE_E
+// ABICOMPAT22: @_ZTIN3foo4Test1bMUlvE_E
+// ABICOMPAT22: @_ZTSN3foo4Test1bMUlvE_E
+
+// ABICOMPAT22-LABEL: define internal void @_ZZ3foovEN4TestC2Ev
+// ABICOMPAT22: store ptr @_ZTIN3foo4Test1aMUlvE_E
+// ABICOMPAT22: store ptr @_ZTIN3foo4Test1bMUlvE_E
+
+// ABICOMPAT22-LABEL: define internal void @_ZZ3foovEN4TestC2E_0v
+// ABICOMPAT22: store ptr @_ZTIN3foo4Test1aMUlvE_E
+// ABICOMPAT22: store ptr @_ZTIN3foo4Test1bMUlvE_E
More information about the cfe-commits
mailing list