Export virtual functions when exporting a class definition (#7210)

This allows Clang to correctly generate the vtable for the exported
class.

There's still something wrong with new virtual functions in the Carbon
type (left a TODO) - I thought it might be related to not flagging
the CXXMethodDecl as virtual, but my initial experiments don't seem to
back that up, so I'll look into it further separately.

There's also a test regression due to an virtual (well, abstract
specifically, but I think it'd happen with a virtual one too) function
in an abstract class taking `self` by value being rejected since
the abstract class can't be instantiated. Not sure if this is a correct
change - the test's behavior could be preserved by using `ref self`
instnead of `self` in this function. Is that reasonable/expected? Should
we not require a type to be complete when passing by value if we can
compute the value representation without such completeness?

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
David Blaikie
2026-05-15 23:53:12 +00:00
committed by GitHub
co-authored by Richard Smith
parent 6d5a883d6d
commit 843323b864
4 changed files with 226 additions and 174 deletions
+45 -9
View File
@@ -355,6 +355,9 @@ class CarbonExternalASTSource : public clang::ExternalASTSource {
auto MapInstIdToClangDeclOrType(LookupResult lookup)
-> std::variant<clang::NamedDecl*, clang::QualType>;
auto GetOrExportFunctionToCpp(SemIR::InstId target_inst_id,
SemIR::FunctionId function_id)
-> clang::FunctionDecl*;
// Get a current best-effort location for the current position within C++
// processing.
auto GetCurrentCppLocId() -> SemIR::LocId {
@@ -426,15 +429,8 @@ auto CarbonExternalASTSource::MapInstIdToClangDeclOrType(LookupResult lookup)
return nullptr;
}
const SemIR::Function& function =
context_->functions().Get(callee_function->function_id);
if (function.clang_decl_id.has_value()) {
return cast<clang::NamedDecl>(
context_->clang_decls().Get(function.clang_decl_id).key.decl);
}
return ExportFunctionToCpp(*context_, SemIR::LocId(target_inst_id),
callee_function->function_id);
return GetOrExportFunctionToCpp(target_inst_id,
callee_function->function_id);
}
case CARBON_KIND(SemIR::FieldDecl field_decl): {
return ExportFieldToCpp(*context_, target_inst_id, field_decl);
@@ -444,6 +440,19 @@ auto CarbonExternalASTSource::MapInstIdToClangDeclOrType(LookupResult lookup)
}
}
auto CarbonExternalASTSource::GetOrExportFunctionToCpp(
SemIR::InstId target_inst_id, SemIR::FunctionId function_id)
-> clang::FunctionDecl* {
const SemIR::Function& function = context_->functions().Get(function_id);
if (function.clang_decl_id.has_value()) {
return cast<clang::FunctionDecl>(
context_->clang_decls().Get(function.clang_decl_id).key.decl);
}
return ExportFunctionToCpp(*context_, SemIR::LocId(target_inst_id),
function_id);
}
auto CarbonExternalASTSource::BuildCarbonNamespace() -> void {
static const llvm::StringLiteral carbon_namespace_name = "Carbon";
auto& ast_context = context_->ast_context();
@@ -653,6 +662,33 @@ auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void {
class_decl->addDecl(ExportDestructorToCpp(*context_, class_info, class_decl));
// TODO: Import any special member functions that affect class properties.
if (class_info.vtable_decl_id.has_value()) {
auto vtable_inst_block = context_->inst_blocks().Get(
context_->vtables()
.Get(context_->insts()
.GetAs<SemIR::VtableDecl>(class_info.vtable_decl_id)
.vtable_id)
.virtual_functions_id);
for (auto vtable_entry_id : vtable_inst_block) {
if (!vtable_entry_id.has_value()) {
continue;
}
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));
context_->clang_sema().AddOverriddenMethods(class_decl, method_decl);
}
}
class_decl->completeDefinition();
}