Fix crash when lowering Carbon derived class with C++ virtual base class (#7745)

If a Carbon class overrides virtual functions from a C++ base class but
is never referenced from C++, it is never exported to Clang. During
lowering, `BuildVtable` then fails to find a `CXXRecordDecl` and crashes
when attempting to get the vtable from Clang's code generator.

Ensure dynamic classes with foreign vtables are exported to Clang when
completing the class definition in `CheckCompleteClassType`, and look up
`first_decl_id()` in `BuildVtable`.

Fixes #7721

---------

Co-authored-by: Dana Jansens <danakj@orodu.net>
This commit is contained in:
ATHARVA
2026-09-18 18:08:53 +00:00
committed by GitHub
co-authored by Dana Jansens
parent d84387f8b8
commit 804359dce0
9 changed files with 416 additions and 130 deletions
@@ -57,3 +57,24 @@ void Test() {
Carbon::Derived d;
}
''';
// --- fail_todo_generic.carbon
// TODO: Test in `toolchain/lower/testdata/` once generic classes deriving from
// C++ virtual classes pass check.
library "[[@TEST_NAME]]";
import Cpp;
inline Cpp '''
struct VBase {
virtual void f() {}
};
''';
// CHECK:STDERR: fail_todo_generic.carbon:[[@LINE+4]]:1: error: semantics TODO: `generic class deriving from C++ virtual class` [SemanticsTodo]
// CHECK:STDERR: class GenericZ(T: type) {
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
class GenericZ(T: type) {
extend base: Cpp.VBase;
override fn f(unused self) {}
}
@@ -94,10 +94,16 @@ import Cpp library "overload.h";
class OverloadX {
extend base: Cpp.OverloadedBase;
//
// CHECK:STDERR: fail_overload_type.carbon:[[@LINE+16]]:23: note: initializing function parameter [InCallToFunctionParam]
// CHECK:STDERR: override fn f(self, x: Cpp.X);
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_overload_type.carbon: error: cannot implicitly convert expression of type `Cpp.Y` to `Cpp.X` [ConversionFailure]
// CHECK:STDERR: fail_overload_type.carbon: note: type `Cpp.Y` does not implement interface `Core.ImplicitAs(Cpp.X)` [MissingImplInMemberAccessInContext]
// CHECK:STDERR: fail_overload_type.carbon:[[@LINE+10]]:23: note: initializing function parameter [InCallToFunctionParam]
// CHECK:STDERR: override fn f(self, x: Cpp.X);
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR: fail_overload_type.carbon:[[@LINE-8]]:10: in file included here [InCppInclude]
// CHECK:STDERR: fail_overload_type.carbon:[[@LINE-14]]:10: in file included here [InCppInclude]
// CHECK:STDERR: ./overload.h:7:16: note: while building thunk to match the signature of this function [ThunkSignature]
// CHECK:STDERR: virtual void f(Y y);
// CHECK:STDERR: ^
@@ -110,10 +116,16 @@ class OverloadX {
class OverloadY {
extend base: Cpp.OverloadedBase;
//
// CHECK:STDERR: fail_overload_type.carbon:[[@LINE+14]]:23: note: initializing function parameter [InCallToFunctionParam]
// CHECK:STDERR: override fn f(self, y: Cpp.Y);
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_overload_type.carbon: error: cannot implicitly convert expression of type `Cpp.X` to `Cpp.Y` [ConversionFailure]
// CHECK:STDERR: fail_overload_type.carbon: note: type `Cpp.X` does not implement interface `Core.ImplicitAs(Cpp.Y)` [MissingImplInMemberAccessInContext]
// CHECK:STDERR: fail_overload_type.carbon:[[@LINE+8]]:23: note: initializing function parameter [InCallToFunctionParam]
// CHECK:STDERR: override fn f(self, y: Cpp.Y);
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR: fail_overload_type.carbon:[[@LINE-24]]:10: in file included here [InCppInclude]
// CHECK:STDERR: fail_overload_type.carbon:[[@LINE-36]]:10: in file included here [InCppInclude]
// CHECK:STDERR: ./overload.h:6:16: note: while building thunk to match the signature of this function [ThunkSignature]
// CHECK:STDERR: virtual void f(X x);
// CHECK:STDERR: ^