https://github.com/chbessonova updated https://github.com/llvm/llvm-project/pull/206740
>From 81fb8f0bdbb2dd7e68e7f8fe18babb3dd0af0b83 Mon Sep 17 00:00:00 2001 From: Kristina Bessonova <[email protected]> Date: Mon, 29 Jun 2026 21:56:40 +0200 Subject: [PATCH] [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 `bb` because the first `T::a` definition was overriden due to missing `<local-name>` encoding and a discriminator required to distinguish two local `T` definitions. --- clang/docs/ReleaseNotes.md | 3 + clang/include/clang/Basic/ABIVersions.def | 5 ++ 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 | 79 +++++++++++++++++++ 6 files changed, 109 insertions(+), 16 deletions(-) create mode 100644 clang/test/CodeGenCXX/mangle-lambdas-in-dmi-local-class.cpp diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 4d7d9f2909096..49914be9f42e8 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
