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


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

Reply via email to