Rudimentary virtual function call interop support (#6050)

This is Itanium-specific for now (explicitly downcasting to the itanium
vtable handling code in Clang) - though it doesn't look like it'd be a
big stretch to either have conditional/two codepaths down Itanium and
MSVC in Carbon, or maybe add a virtual function in clang to avoid
needing to conditional+downcast in Carbon.

Here's a working example:
`dynamic_type.h`:
```
#ifndef TEST_H
#define TEST_H

struct A {
  virtual auto virt0() -> int;
  virtual auto virt1() -> int;
};

auto GetVal() -> A* _Nonnull;

#endif
```
`test.carbon`:
```
library "test";

import Cpp library "dynamic_type.h";
import Core library "io";

fn Run() {
  var a: Cpp.A* = Cpp.GetVal();
  Core.Print(a->virt0());
  Core.Print(a->virt1());
}
```
`dynamic_type.cpp`:
```
#include "dynamic_type.h"

auto A::virt0() -> int {
  return 0;
}

auto A::virt1() -> int {
  return 1;
}

struct B: A {
  auto virt0() -> int override {
    return 7;
  }
  auto virt1() -> int override {
    return 42;
  }
};

auto GetVal() -> A* _Nonnull {
  static B b;
  return &b;
}
```
```
$ ./bazel-bin/toolchain/carbon compile test.carbon
$ clang++-tot -g dynamic_type.cpp test.o --output=a.out
$ ./a.out
7
42
```
(linking with `carbon link` failed because we aren't linking to the C++
runtime yet, it seems, so: `ld.lld: error: undefined symbol: vtable for
__cxxabiv1::__class_type_info`)

---------

Co-authored-by: Dana Jansens <danakj@orodu.net>
This commit is contained in:
David Blaikie
2025-09-17 23:30:13 +00:00
committed by GitHub
co-authored by Dana Jansens
parent cac3578b86
commit bff0e5978b
4 changed files with 162 additions and 62 deletions
+18 -9
View File
@@ -13,6 +13,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/UnresolvedSet.h"
#include "clang/AST/VTableBuilder.h"
#include "clang/Basic/FileManager.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
@@ -1623,6 +1624,21 @@ static auto ImportFunction(Context& context, SemIR::LocId loc_id,
AddPlaceholderInstInNoBlock(context, Parse::NodeId::None, function_decl);
context.imports().push_back(decl_id);
auto virtual_modifier = SemIR::Function::VirtualModifier::None;
int32_t virtual_index = -1;
if (auto* method_decl = dyn_cast<clang::CXXMethodDecl>(clang_decl)) {
if (method_decl->size_overridden_methods()) {
virtual_modifier = SemIR::Function::VirtualModifier::Override;
} else if (method_decl->isVirtual()) {
virtual_modifier = SemIR::Function::VirtualModifier::Virtual;
}
if (virtual_modifier != SemIR::Function::VirtualModifier::None) {
// TODO: Add support for Microsoft/non-Itanium vtables.
virtual_index = dyn_cast<clang::ItaniumVTableContext>(
context.ast_context().getVTableContext())
->getMethodVTableIndex(method_decl);
}
}
auto function_info = SemIR::Function{
{.name_id = GetFunctionName(context, clang_decl),
.parent_scope_id = GetParentNameScopeId(context, clang_decl),
@@ -1640,7 +1656,8 @@ static auto ImportFunction(Context& context, SemIR::LocId loc_id,
.definition_id = SemIR::InstId::None},
{.call_params_id = function_params_insts->call_params_id,
.return_slot_pattern_id = function_params_insts->return_slot_pattern_id,
.virtual_modifier = SemIR::FunctionFields::VirtualModifier::None,
.virtual_modifier = virtual_modifier,
.virtual_index = virtual_index,
.self_param_id = FindSelfPattern(
context, function_params_insts->implicit_param_patterns_id),
.clang_decl_id = context.sem_ir().clang_decls().Add(
@@ -1675,14 +1692,6 @@ auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id,
return SemIR::ErrorInst::InstId;
}
if (auto* method_decl = dyn_cast<clang::CXXMethodDecl>(clang_decl)) {
if (method_decl->isVirtual()) {
context.TODO(loc_id, "Unsupported: Virtual function");
MarkFailedDecl(context, clang_decl);
return SemIR::ErrorInst::InstId;
}
}
CARBON_CHECK(clang_decl->getFunctionType()->isFunctionProtoType(),
"Not Prototype function (non-C++ code)");