Fix mangling collision for C++ class template specializations (#7269)

Carbon-side thunks (for example the `Copy`/`Destroy` witness thunks
generated for imported C++ types) are mangled by Carbon, and their names
incorporate a fingerprint of the involved types. The instruction
fingerprinter identifies a class only by its name and parent scope,
which is sufficient for Carbon classes but not for imported C++ classes:
different specializations of one class template (and other cases such as
types in anonymous namespaces) share a Carbon name and parent scope. As
a result, the thunks for two distinct specializations could mangle to
the same name, producing a single LLVM function with two definitions and
failing `verifyModule` during lowering.

When fingerprinting a class imported from C++, also include the Clang
mangled name of its type.

Test: toolchain/lower/testdata/interop/cpp/thunks.carbon gains a split
with two specializations of one class template, each requiring a thunk;
their thunks now get distinct mangled names instead of colliding.

Assisted-by: Claude Code

---------

Co-authored-by: Christopher Di Bella <cjdb.ns@gmail.com>
This commit is contained in:
Chandler Carruth
2026-05-30 08:13:00 +00:00
committed by GitHub
co-authored by Christopher Di Bella
parent 3ef128ac91
commit 8a59f2a76b
64 changed files with 4762 additions and 4330 deletions
+12 -12
View File
@@ -144,7 +144,7 @@ fn GetRefs() {
// CHECK:STDOUT: %_.var.2 = alloca i32, align 4, !dbg !17
// CHECK:STDOUT: %.loc25_23.2.temp = alloca i32, align 4, !dbg !18
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %c.var), !dbg !14
// CHECK:STDOUT: call void @"_COp.cbe5dd53b28eb3d1:DefaultOrUnformed.Core.2603c91dd66a1dca"(ptr %c.var), !dbg !14
// CHECK:STDOUT: call void @"_COp.cbe5dd53b28eb3d1:DefaultOrUnformed.Core.4fdc745d4b6d0e3c"(ptr %c.var), !dbg !14
// CHECK:STDOUT: call void @_Z8TakeCRefR1C(ptr %c.var), !dbg !19
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var.1), !dbg !15
// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %_.var.1, ptr align 1 @C.val.loc19_20, i64 0, i1 false), !dbg !15
@@ -176,7 +176,7 @@ fn GetRefs() {
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind
// CHECK:STDOUT: define void @"_COp:thunk:Default.5648bcc2d6c775b7.Core:C.Cpp"(ptr sret({}) %return) #2 !dbg !29 {
// CHECK:STDOUT: define void @"_COp:thunk:Default.08e786f435a99766.Core:C.Cpp"(ptr sret({}) %return) #2 !dbg !29 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: call void @_ZN1CC1Ev.carbon_thunk.(ptr %return), !dbg !33
// CHECK:STDOUT: ret void, !dbg !33
@@ -236,8 +236,8 @@ fn GetRefs() {
// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #5
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define linkonce_odr void @"_COp.cbe5dd53b28eb3d1:DefaultOrUnformed.Core.2603c91dd66a1dca"(ptr sret({}) %return) #0 !dbg !43 {
// CHECK:STDOUT: call void @"_COp:thunk:Default.5648bcc2d6c775b7.Core:C.Cpp"(ptr %return), !dbg !45
// CHECK:STDOUT: define linkonce_odr void @"_COp.cbe5dd53b28eb3d1:DefaultOrUnformed.Core.4fdc745d4b6d0e3c"(ptr sret({}) %return) #0 !dbg !43 {
// CHECK:STDOUT: call void @"_COp:thunk:Default.08e786f435a99766.Core:C.Cpp"(ptr %return), !dbg !45
// CHECK:STDOUT: ret void, !dbg !46
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -296,7 +296,7 @@ fn GetRefs() {
// CHECK:STDOUT: !26 = !{!27, !27, i64 0}
// CHECK:STDOUT: !27 = !{!"p1 _ZTS1C", !28, i64 0}
// CHECK:STDOUT: !28 = !{!"any pointer", !9, i64 0}
// CHECK:STDOUT: !29 = distinct !DISubprogram(name: "Op", linkageName: "_COp:thunk:Default.5648bcc2d6c775b7.Core:C.Cpp", scope: null, file: !6, line: 5, type: !30, spFlags: DISPFlagDefinition, unit: !5)
// CHECK:STDOUT: !29 = distinct !DISubprogram(name: "Op", linkageName: "_COp:thunk:Default.08e786f435a99766.Core:C.Cpp", scope: null, file: !6, line: 5, type: !30, spFlags: DISPFlagDefinition, unit: !5)
// CHECK:STDOUT: !30 = !DISubroutineType(types: !31)
// CHECK:STDOUT: !31 = !{!32}
// CHECK:STDOUT: !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
@@ -310,7 +310,7 @@ fn GetRefs() {
// CHECK:STDOUT: !40 = !{!41}
// CHECK:STDOUT: !41 = !DILocalVariable(arg: 1, scope: !36, type: !39)
// CHECK:STDOUT: !42 = !DILocation(line: 25, column: 23, scope: !36)
// CHECK:STDOUT: !43 = distinct !DISubprogram(name: "Op", linkageName: "_COp.cbe5dd53b28eb3d1:DefaultOrUnformed.Core.2603c91dd66a1dca", scope: null, file: !44, line: 9, type: !30, spFlags: DISPFlagDefinition, unit: !5)
// CHECK:STDOUT: !43 = distinct !DISubprogram(name: "Op", linkageName: "_COp.cbe5dd53b28eb3d1:DefaultOrUnformed.Core.4fdc745d4b6d0e3c", scope: null, file: !44, line: 9, type: !30, spFlags: DISPFlagDefinition, unit: !5)
// CHECK:STDOUT: !44 = !DIFile(filename: "min_prelude/parts/default.carbon", directory: "")
// CHECK:STDOUT: !45 = !DILocation(line: 9, column: 28, scope: !43)
// CHECK:STDOUT: !46 = !DILocation(line: 9, column: 21, scope: !43)
@@ -333,7 +333,7 @@ fn GetRefs() {
// CHECK:STDOUT: %_.var = alloca i32, align 4, !dbg !17
// CHECK:STDOUT: %.loc26_23.2.temp = alloca i32, align 4, !dbg !18
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %c.var), !dbg !14
// CHECK:STDOUT: call void @"_COp.cbe5dd53b28eb3d1:DefaultOrUnformed.Core.2603c91dd66a1dca"(ptr %c.var), !dbg !14
// CHECK:STDOUT: call void @"_COp.cbe5dd53b28eb3d1:DefaultOrUnformed.Core.4fdc745d4b6d0e3c"(ptr %c.var), !dbg !14
// CHECK:STDOUT: call void @_Z8TakeCRefR1C10ForceThunk.carbon_thunk.r(ptr %c.var), !dbg !19
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc20_18.2.temp), !dbg !15
// CHECK:STDOUT: call void @_Z9TakeCRRefRK1C10ForceThunk.carbon_thunk._(ptr @C.val.loc20_20.3), !dbg !20
@@ -364,7 +364,7 @@ fn GetRefs() {
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind
// CHECK:STDOUT: define void @"_COp:thunk:Default.5648bcc2d6c775b7.Core:C.Cpp"(ptr sret({}) %return) #2 !dbg !29 {
// CHECK:STDOUT: define void @"_COp:thunk:Default.08e786f435a99766.Core:C.Cpp"(ptr sret({}) %return) #2 !dbg !29 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: call void @_ZN1CC1Ev.carbon_thunk.(ptr %return), !dbg !33
// CHECK:STDOUT: ret void, !dbg !33
@@ -446,8 +446,8 @@ fn GetRefs() {
// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #4
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define linkonce_odr void @"_COp.cbe5dd53b28eb3d1:DefaultOrUnformed.Core.2603c91dd66a1dca"(ptr sret({}) %return) #0 !dbg !45 {
// CHECK:STDOUT: call void @"_COp:thunk:Default.5648bcc2d6c775b7.Core:C.Cpp"(ptr %return), !dbg !47
// CHECK:STDOUT: define linkonce_odr void @"_COp.cbe5dd53b28eb3d1:DefaultOrUnformed.Core.4fdc745d4b6d0e3c"(ptr sret({}) %return) #0 !dbg !45 {
// CHECK:STDOUT: call void @"_COp:thunk:Default.08e786f435a99766.Core:C.Cpp"(ptr %return), !dbg !47
// CHECK:STDOUT: ret void, !dbg !48
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -507,7 +507,7 @@ fn GetRefs() {
// CHECK:STDOUT: !26 = !{!27, !27, i64 0}
// CHECK:STDOUT: !27 = !{!"p1 _ZTS1C", !28, i64 0}
// CHECK:STDOUT: !28 = !{!"any pointer", !9, i64 0}
// CHECK:STDOUT: !29 = distinct !DISubprogram(name: "Op", linkageName: "_COp:thunk:Default.5648bcc2d6c775b7.Core:C.Cpp", scope: null, file: !6, line: 5, type: !30, spFlags: DISPFlagDefinition, unit: !5)
// CHECK:STDOUT: !29 = distinct !DISubprogram(name: "Op", linkageName: "_COp:thunk:Default.08e786f435a99766.Core:C.Cpp", scope: null, file: !6, line: 5, type: !30, spFlags: DISPFlagDefinition, unit: !5)
// CHECK:STDOUT: !30 = !DISubroutineType(types: !31)
// CHECK:STDOUT: !31 = !{!32}
// CHECK:STDOUT: !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
@@ -523,7 +523,7 @@ fn GetRefs() {
// CHECK:STDOUT: !42 = !{!43}
// CHECK:STDOUT: !43 = !DILocalVariable(arg: 1, scope: !38, type: !41)
// CHECK:STDOUT: !44 = !DILocation(line: 26, column: 23, scope: !38)
// CHECK:STDOUT: !45 = distinct !DISubprogram(name: "Op", linkageName: "_COp.cbe5dd53b28eb3d1:DefaultOrUnformed.Core.2603c91dd66a1dca", scope: null, file: !46, line: 9, type: !30, spFlags: DISPFlagDefinition, unit: !5)
// CHECK:STDOUT: !45 = distinct !DISubprogram(name: "Op", linkageName: "_COp.cbe5dd53b28eb3d1:DefaultOrUnformed.Core.4fdc745d4b6d0e3c", scope: null, file: !46, line: 9, type: !30, spFlags: DISPFlagDefinition, unit: !5)
// CHECK:STDOUT: !46 = !DIFile(filename: "min_prelude/parts/default.carbon", directory: "")
// CHECK:STDOUT: !47 = !DILocation(line: 9, column: 28, scope: !45)
// CHECK:STDOUT: !48 = !DILocation(line: 9, column: 21, scope: !45)