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
+29
View File
@@ -8,6 +8,9 @@
#include <string>
#include <utility>
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Mangle.h"
#include "common/check.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
@@ -221,6 +224,32 @@ auto File::set_cpp_file(std::unique_ptr<SemIR::CppFile> cpp_file) -> void {
cpp_file_ = std::move(cpp_file);
}
auto File::AppendCppMangledTypeName(ClassId class_id,
llvm::raw_ostream& out) const -> bool {
// Only classes imported *from* C++ need this. A C++ scope records the Clang
// declaration it came from; note that a Carbon class exported *to* C++ also
// has an associated Clang declaration, but its scope is not a C++ scope and
// it is already uniquely identified by its name and parent scope.
auto scope_id = classes().Get(class_id).scope_id;
if (!scope_id.has_value()) {
return false;
}
const auto& scope = name_scopes().Get(scope_id);
if (!scope.is_cpp_scope()) {
return false;
}
auto clang_decl_id = scope.clang_decl_context_id();
if (!clang_decl_id.has_value()) {
return false;
}
// A C++ class's scope always maps to a Clang tag declaration.
auto* tag_decl =
clang::cast<clang::TagDecl>(clang_decls().Get(clang_decl_id).key.decl);
cpp_file_->mangle_context().mangleCanonicalTypeName(
cpp_file_->ast_context().getCanonicalTagType(tag_decl), out);
return true;
}
} // namespace Carbon::SemIR
namespace Carbon {