https://github.com/mihaizegheru created https://github.com/llvm/llvm-project/pull/235458
Fix lambda mangling collisions in function templates in named modules. The `D == RD` branch of `mangleLocalName()` omitted closure prefixes, allowing lambdas with the same mangling number in different contexts to produce identical names. Use `mangleNestedNameWithClosurePrefix()` to preserve the prefix without repeating the enclosing function’s encoding. Add regression coverage for ordinary and generic lambdas, including mixed static/non-static locals and two static locals. Fixes #234701. >From ce6d4803efeb44ec2b08702688809c1b3885fa25 Mon Sep 17 00:00:00 2001 From: Mihai Zegheru <[email protected]> Date: Sun, 25 Oct 2026 15:53:54 +0200 Subject: [PATCH 1/2] [clang] Preserve closure prefixes when mangling local lambda types --- clang/lib/AST/ItaniumMangle.cpp | 6 ++- .../mangle-lambda-static-local-module.cpp | 41 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 clang/test/CodeGenCXX/mangle-lambda-static-local-module.cpp diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 93ba1519518d2c..95ab08ddb6931a 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -1949,7 +1949,11 @@ void CXXNameMangler::mangleLocalName(GlobalDecl GD, // Mangle the name relative to the closest enclosing function. // equality ok because RD derived from ND above if (D == RD) { - mangleUnqualifiedName(RD, DC, AdditionalAbiTags); + const NamedDecl *PrefixND = getClosurePrefix(RD); + if (PrefixND && !isCompatibleWith(LangOptions::ClangABI::Ver18)) + mangleNestedNameWithClosurePrefix(GD, PrefixND, AdditionalAbiTags, /*NoFunction=*/true); + else + mangleUnqualifiedName(RD, DC, AdditionalAbiTags); } else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) { if (const NamedDecl *PrefixND = getClosurePrefix(BD)) mangleClosurePrefix(PrefixND, true /*NoFunction*/); diff --git a/clang/test/CodeGenCXX/mangle-lambda-static-local-module.cpp b/clang/test/CodeGenCXX/mangle-lambda-static-local-module.cpp new file mode 100644 index 00000000000000..4f67ea4a2ae069 --- /dev/null +++ b/clang/test/CodeGenCXX/mangle-lambda-static-local-module.cpp @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++23 \ +// RUN: -emit-llvm -o - %s | FileCheck %s + +export module mod; + +template <class F> struct Iterator { + F fn; + + decltype(auto) operator*() { + return fn({}); + } +}; + +// The static local's initializer has a separate lambda numbering context. +// Preserve its closure prefix when mangling the lambda as a template argument. +template <typename> void bad_function() { + auto lam1 = [](char n) { return 0; }; + static auto lam2 = [](char n) { return n; }; + + *Iterator{lam1}; + *Iterator{lam2}; +} + +// Also distinguish two static-local lambdas with identical signatures and +// return types, where a collision can silently reuse the wrong specialization. +template <typename> int both_static() { + static auto lam1 = [](char) { return 1; }; + static auto lam2 = [](char) { return 2; }; + + return *Iterator{lam1} + *Iterator{lam2}; +} + +int repro() { + bad_function<int>(); + return both_static<int>(); +} + +// CHECK-DAG: define{{.*}} i32 @_ZNW3mod8IteratorIZS_12bad_functionIiEvvEUlcE_EdeEv( +// CHECK-DAG: define{{.*}} i8 @_ZNW3mod8IteratorIZS_12bad_functionIiEvvEN4lam2MUlcE_EEdeEv( +// CHECK-DAG: define{{.*}} i32 @_ZNW3mod8IteratorIZS_11both_staticIiEivEN4lam1MUlcE_EEdeEv( +// CHECK-DAG: define{{.*}} i32 @_ZNW3mod8IteratorIZS_11both_staticIiEivEN4lam2MUlcE_EEdeEv( \ No newline at end of file >From 3971d98aa0e88bff3598efc2bc46abb4a93db1dc Mon Sep 17 00:00:00 2001 From: Mihai Zegheru <[email protected]> Date: Sun, 25 Oct 2026 16:24:44 +0200 Subject: [PATCH 2/2] [clang] Preserve closure prefixes when mangling local lambda types. --- .../mangle-lambda-static-local-module.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/clang/test/CodeGenCXX/mangle-lambda-static-local-module.cpp b/clang/test/CodeGenCXX/mangle-lambda-static-local-module.cpp index 4f67ea4a2ae069..5885a62f0593e2 100644 --- a/clang/test/CodeGenCXX/mangle-lambda-static-local-module.cpp +++ b/clang/test/CodeGenCXX/mangle-lambda-static-local-module.cpp @@ -7,12 +7,10 @@ template <class F> struct Iterator { F fn; decltype(auto) operator*() { - return fn({}); + return fn(char{}); } }; -// The static local's initializer has a separate lambda numbering context. -// Preserve its closure prefix when mangling the lambda as a template argument. template <typename> void bad_function() { auto lam1 = [](char n) { return 0; }; static auto lam2 = [](char n) { return n; }; @@ -21,8 +19,6 @@ template <typename> void bad_function() { *Iterator{lam2}; } -// Also distinguish two static-local lambdas with identical signatures and -// return types, where a collision can silently reuse the wrong specialization. template <typename> int both_static() { static auto lam1 = [](char) { return 1; }; static auto lam2 = [](char) { return 2; }; @@ -30,12 +26,21 @@ template <typename> int both_static() { return *Iterator{lam1} + *Iterator{lam2}; } +template <typename> int both_generic() { + static constexpr auto lam1 = [](auto n) { return 0; }; + static constexpr auto lam2 = [](auto n) { return n; }; + + return *Iterator{lam1} + *Iterator{lam2}; +} + int repro() { bad_function<int>(); - return both_static<int>(); + return both_static<int>() + both_generic<int>(); } // CHECK-DAG: define{{.*}} i32 @_ZNW3mod8IteratorIZS_12bad_functionIiEvvEUlcE_EdeEv( // CHECK-DAG: define{{.*}} i8 @_ZNW3mod8IteratorIZS_12bad_functionIiEvvEN4lam2MUlcE_EEdeEv( // CHECK-DAG: define{{.*}} i32 @_ZNW3mod8IteratorIZS_11both_staticIiEivEN4lam1MUlcE_EEdeEv( -// CHECK-DAG: define{{.*}} i32 @_ZNW3mod8IteratorIZS_11both_staticIiEivEN4lam2MUlcE_EEdeEv( \ No newline at end of file +// CHECK-DAG: define{{.*}} i32 @_ZNW3mod8IteratorIZS_11both_staticIiEivEN4lam2MUlcE_EEdeEv( +// CHECK-DAG: define{{.*}} i32 @_ZNW3mod8IteratorIZS_12both_genericIiEivEN4lam1MUlT_E_EEdeEv( +// CHECK-DAG: define{{.*}} i8 @_ZNW3mod8IteratorIZS_12both_genericIiEivEN4lam2MUlT_E_EEdeEv( \ No newline at end of file _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
