mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Handle signature mismatch when a Carbon function overrides a C++ virtual function. (#7499)
When a Carbon virtual function overrides a C++ virtual function, we need to export it with the C++ signature in order for it to work as an override. Instead of mapping the C++ signature into Carbon and then back again, use the original C++ signature from the base class as the signature exported to C++. Also add documentation explaining how we use thunks in C++ interop, including in this new virtual function handling logic.
This commit is contained in:
@@ -510,7 +510,6 @@ auto CarbonExternalASTSource::GetOrExportFunctionToCpp(
|
||||
clang_function_decl,
|
||||
context_->clang_decl_signatures().Add(std::move(thunk_signature))),
|
||||
.inst_id = function.first_decl_id()});
|
||||
|
||||
return clang_function_decl;
|
||||
}
|
||||
|
||||
@@ -687,6 +686,15 @@ auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void {
|
||||
|
||||
// TODO: Import any special member functions that affect class properties.
|
||||
|
||||
// Virtual functions whose definitions we have deferred generating until the
|
||||
// class is complete.
|
||||
struct PendingVirtualFunction {
|
||||
SemIR::LocId loc_id;
|
||||
SemIR::FunctionId function_id;
|
||||
clang::CXXMethodDecl* method_decl;
|
||||
};
|
||||
llvm::SmallVector<PendingVirtualFunction> pending_virtual_functions;
|
||||
|
||||
if (class_info.vtable_decl_id.has_value()) {
|
||||
auto vtable_inst_block = context_->inst_blocks().Get(
|
||||
context_->vtables()
|
||||
@@ -699,37 +707,42 @@ auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The Carbon vtable entry for a function override is a thunk, which wraps
|
||||
// the function declaration to expose the signature of the overridden
|
||||
// virtual function. Here we want to generate a C++ method declaration for
|
||||
// the override, so we need to look through the thunk wrapping.
|
||||
const auto [callee_function, function] =
|
||||
[&]() -> std::pair<SemIR::CalleeFunction, const SemIR::Function&> {
|
||||
auto vtable_callee_function =
|
||||
GetCalleeAsFunction(context_->sem_ir(), vtable_entry_id);
|
||||
const SemIR::Function& vtable_function =
|
||||
context_->functions().Get(vtable_callee_function.function_id);
|
||||
if (!vtable_function.thunk_id().has_value()) {
|
||||
return {vtable_callee_function, vtable_function};
|
||||
}
|
||||
auto vtable_thunk =
|
||||
context_->sem_ir().thunks().Get(vtable_function.thunk_id());
|
||||
auto callee_function =
|
||||
GetCalleeAsFunction(context_->sem_ir(), vtable_thunk.callee_id);
|
||||
return {callee_function,
|
||||
context_->functions().Get(callee_function.function_id)};
|
||||
}();
|
||||
const auto callee_function =
|
||||
GetCalleeAsFunction(context_->sem_ir(), vtable_entry_id);
|
||||
const SemIR::Function& function =
|
||||
context_->functions().Get(callee_function.function_id);
|
||||
|
||||
// If this is a member of a base class, nothing to do here.
|
||||
if (function.parent_scope_id != class_info.scope_id) {
|
||||
continue;
|
||||
}
|
||||
auto* method_decl = cast<clang::CXXMethodDecl>(GetOrExportFunctionToCpp(
|
||||
vtable_entry_id, callee_function.function_id));
|
||||
auto* method_decl =
|
||||
cast_or_null<clang::CXXMethodDecl>(ExportVirtualFunctionDeclToCpp(
|
||||
*context_, SemIR::LocId(vtable_entry_id), class_decl,
|
||||
callee_function.function_id));
|
||||
if (!method_decl) {
|
||||
continue;
|
||||
}
|
||||
context_->clang_sema().AddOverriddenMethods(class_decl, method_decl);
|
||||
context_->clang_decls().Add(
|
||||
{.key = SemIR::ClangDeclKey::ForFunctionDecl(
|
||||
method_decl,
|
||||
MakeVirtualFunctionSignature(*context_, method_decl)),
|
||||
.inst_id = function.first_decl_id()});
|
||||
pending_virtual_functions.push_back(
|
||||
{.loc_id = SemIR::LocId(vtable_entry_id),
|
||||
.function_id = callee_function.function_id,
|
||||
.method_decl = method_decl});
|
||||
}
|
||||
}
|
||||
class_decl->completeDefinition();
|
||||
|
||||
// Now the class is complete, we can define the virtual function thunks.
|
||||
for (auto virtual_fn : pending_virtual_functions) {
|
||||
DefineExportedVirtualFunction(*context_, virtual_fn.loc_id,
|
||||
virtual_fn.function_id,
|
||||
virtual_fn.method_decl);
|
||||
}
|
||||
}
|
||||
|
||||
auto CarbonExternalASTSource::layoutRecordType(
|
||||
|
||||
Reference in New Issue
Block a user