https://github.com/chbessonova updated 
https://github.com/llvm/llvm-project/pull/206740

>From 2004b67caee32114459d79831a3a1e151d54e2f4 Mon Sep 17 00:00:00 2001
From: Kristina Bessonova <[email protected]>
Date: Mon, 29 Jun 2026 21:56:40 +0200
Subject: [PATCH 1/4] [clang][ItaniumMangle] Fix mangling of lambdas in default
 member initializers of local classes

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 manglings 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 `aa` because the second `T::a` definition
never reached IR generation due to the missing `<local-name>` encoding and
discriminator required to distinguish the two local `T` definitions.
---
 clang/lib/AST/ItaniumMangle.cpp               | 20 +++---
 .../CodeGenCXX/dtor-local-lambda-mangle.cpp   |  6 +-
 .../CodeGenCXX/mangle-lambdas-gh88906.cpp     | 12 ++--
 .../mangle-lambdas-in-dmi-local-class.cpp     | 65 +++++++++++++++++++
 4 files changed, 86 insertions(+), 17 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/mangle-lambdas-in-dmi-local-class.cpp

diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index e5cdd6f31c507..e014b45a5da93 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,8 @@ void CXXNameMangler::mangleNameWithAbiTags(
   //         ::= <local-name>
   //
   const DeclContext *DC = Context.getEffectiveDeclContext(ND);
-  bool IsLambda = isLambda(ND);
 
-  if (GetLocalClassDecl(ND) &&
-      (!IsLambda || isCompatibleWith(LangOptions::ClangABI::Ver18))) {
+  if (GetLocalClassDecl(ND)) {
     mangleLocalName(GD, AdditionalAbiTags);
     return;
   }
@@ -1845,7 +1844,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 +1853,7 @@ void CXXNameMangler::mangleNestedNameWithClosurePrefix(
 
   Out << 'N';
 
-  mangleClosurePrefix(PrefixND);
+  mangleClosurePrefix(PrefixND, NoFunction);
   mangleUnqualifiedName(GD, nullptr, AdditionalAbiTags);
 
   Out << 'E';
@@ -1945,8 +1944,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,
+                                          true /*NoFunction*/);
+      else
+        mangleNestedName(GD, Context.getEffectiveDeclContext(ND),
+                         AdditionalAbiTags, true /*NoFunction*/);
     }
   } 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..bd7b60b816fa7 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: _ZTSZN1ED1EvEN3$_012anotherValueMUlvE_E
     int anotherValue = [x = 1] { return x; }();
   } obj;
 }
 
 E::~E() {
   struct {
-    // CHECK-DAG: _ZTSN1ED13$_012anotherValueMUlvE_E
+    // CHECK-DAG: _ZTSZN1EC1IiEET_ENUt_12anotherValueMUlvE_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..479ce23dc53ab
--- /dev/null
+++ b/clang/test/CodeGenCXX/mangle-lambdas-in-dmi-local-class.cpp
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu %s -emit-llvm -I%S -std=c++20 -o - 
| FileCheck %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: @_ZZ3foovEN4TestC2Ev
+// CHECK: store ptr @_ZTIZ3foovEN4Test1aMUlvE_E
+// CHECK: store ptr @_ZTIZ3foovEN4Test1bMUlvE_E
+
+// CHECK-LABEL: @_ZZ3foovEN4TestC2E_0v
+// CHECK: store ptr @_ZTIZ3foovEN4Test1aMUlvE_E_0
+// CHECK: store ptr @_ZTIZ3foovEN4Test1bMUlvE_E_0

>From 92ae1ca7808069b8b9de3b4396d73f630d14e098 Mon Sep 17 00:00:00 2001
From: Kristina Bessonova <[email protected]>
Date: Tue, 30 Jun 2026 18:56:01 +0200
Subject: [PATCH 2/4] Address the review comment and improve the test

---
 clang/lib/AST/ItaniumMangle.cpp                             | 4 ++--
 clang/test/CodeGenCXX/mangle-lambdas-in-dmi-local-class.cpp | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index e014b45a5da93..f3999728c3a9a 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -1947,10 +1947,10 @@ void CXXNameMangler::mangleLocalName(GlobalDecl GD,
       const NamedDecl *PrefixND = getClosurePrefix(ND);
       if (PrefixND && !isCompatibleWith(LangOptions::ClangABI::Ver18))
         mangleNestedNameWithClosurePrefix(GD, PrefixND, AdditionalAbiTags,
-                                          true /*NoFunction*/);
+                                          /*NoFunction=*/true);
       else
         mangleNestedName(GD, Context.getEffectiveDeclContext(ND),
-                         AdditionalAbiTags, true /*NoFunction*/);
+                         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/mangle-lambdas-in-dmi-local-class.cpp 
b/clang/test/CodeGenCXX/mangle-lambdas-in-dmi-local-class.cpp
index 479ce23dc53ab..d6d6ced64668c 100644
--- a/clang/test/CodeGenCXX/mangle-lambdas-in-dmi-local-class.cpp
+++ b/clang/test/CodeGenCXX/mangle-lambdas-in-dmi-local-class.cpp
@@ -56,10 +56,10 @@ void foo() {
 // CHECK: @_ZTIZ3foovEN4Test1bMUlvE_E_0
 // CHECK: @_ZTSZ3foovEN4Test1bMUlvE_E_0
 
-// CHECK-LABEL: @_ZZ3foovEN4TestC2Ev
+// CHECK-LABEL: define internal void @_ZZ3foovEN4TestC2Ev
 // CHECK: store ptr @_ZTIZ3foovEN4Test1aMUlvE_E
 // CHECK: store ptr @_ZTIZ3foovEN4Test1bMUlvE_E
 
-// CHECK-LABEL: @_ZZ3foovEN4TestC2E_0v
+// CHECK-LABEL: define internal void @_ZZ3foovEN4TestC2E_0v
 // CHECK: store ptr @_ZTIZ3foovEN4Test1aMUlvE_E_0
 // CHECK: store ptr @_ZTIZ3foovEN4Test1bMUlvE_E_0

>From 541d0842a8e33a02ccc7251c0112daa4c3b22afc Mon Sep 17 00:00:00 2001
From: Kristina Bessonova <[email protected]>
Date: Mon, 6 Jul 2026 13:01:20 +0200
Subject: [PATCH 3/4] Add release note

---
 clang/docs/ReleaseNotes.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index b372e5b58068b..e0635e2e2d644 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -127,6 +127,9 @@ latest release, please see the [Clang Web 
Site](https://clang.llvm.org) or the
   break on i686, MIPS O32, PowerPC64 ELFv1, and Lanai.
 - Fixed incorrect struct return when single large vector (256/512-bit) used on
   x86-64 targets. (#GH203760) The bug was introduced since Clang 21. 
(#GH120670)
+- Fixed an Itanium ABI mangling bug for lambdas in default member initializers
+  of local classes that could cause collisions between distinct local class
+  definitions.
 
 ### AST Dumping Potentially Breaking Changes
 

>From 5d00adea1a2578e0cc873f4487ac9029a8325694 Mon Sep 17 00:00:00 2001
From: Kristina Bessonova <[email protected]>
Date: Tue, 7 Jul 2026 12:50:54 +0200
Subject: [PATCH 4/4] Add new clang ABI entry

---
 clang/include/clang/Basic/ABIVersions.def          |  5 +++++
 clang/lib/AST/ItaniumMangle.cpp                    |  4 +++-
 .../mangle-lambdas-in-dmi-local-class.cpp          | 14 ++++++++++++++
 3 files changed, 22 insertions(+), 1 deletion(-)

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 f3999728c3a9a..257379e568bcc 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -1069,7 +1069,9 @@ void CXXNameMangler::mangleNameWithAbiTags(
   //
   const DeclContext *DC = Context.getEffectiveDeclContext(ND);
 
-  if (GetLocalClassDecl(ND)) {
+  if (GetLocalClassDecl(ND) &&
+      (!isLambda(ND) || isCompatibleWith(LangOptions::ClangABI::Ver18) ||
+       !isCompatibleWith(LangOptions::ClangABI::Ver22))) {
     mangleLocalName(GD, AdditionalAbiTags);
     return;
   }
diff --git a/clang/test/CodeGenCXX/mangle-lambdas-in-dmi-local-class.cpp 
b/clang/test/CodeGenCXX/mangle-lambdas-in-dmi-local-class.cpp
index d6d6ced64668c..7f3602ceeccfa 100644
--- a/clang/test/CodeGenCXX/mangle-lambdas-in-dmi-local-class.cpp
+++ b/clang/test/CodeGenCXX/mangle-lambdas-in-dmi-local-class.cpp
@@ -1,4 +1,5 @@
 // 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.
@@ -63,3 +64,16 @@ void foo() {
 // 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

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to