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
