Export virtual/abstract/override functions as virtual (#7211)

This provides enough to Clang so it can include these new virtual
functions in the vtable of Carbon types derived from C++ types.
This commit is contained in:
David Blaikie
2026-05-18 19:04:33 +00:00
committed by GitHub
parent 832d46f01b
commit 31db4a0931
3 changed files with 49 additions and 18 deletions
@@ -34,8 +34,6 @@ __attribute__((optnone)) auto Use(Base& b) {
base class Derived {
extend base: Cpp.Base;
// TODO: `other_func` should appear in the vtable (it currently doesn't
// maybe because the CXXMethodDecl isn't marked as virtual)
virtual fn other_func[unused self: Self]() {
}
override fn func[unused ref self: Self]() {
@@ -81,7 +79,7 @@ auto DoThing() -> void {
// CHECK:STDOUT:
// CHECK:STDOUT: $_ZTSN6Carbon7DerivedE = comdat any
// CHECK:STDOUT:
// CHECK:STDOUT: @_ZTVN6Carbon7DerivedE = linkonce_odr dso_local unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTIN6Carbon7DerivedE, ptr @_ZN11FurtherBase17further_base_funcEv, ptr @_ZNR6Carbon7Derived4funcEv] }, comdat, align 8
// CHECK:STDOUT: @_ZTVN6Carbon7DerivedE = linkonce_odr dso_local unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTIN6Carbon7DerivedE, ptr @_ZN11FurtherBase17further_base_funcEv, ptr @_ZNR6Carbon7Derived4funcEv, ptr @_ZN6Carbon7Derived10other_funcEv] }, comdat, align 8
// CHECK:STDOUT: @_ZTIN6Carbon7DerivedE = linkonce_odr dso_local constant { ptr, ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv120__si_class_type_infoE, i64 2), ptr @_ZTSN6Carbon7DerivedE, ptr @_ZTI4Base }, comdat, align 8
// CHECK:STDOUT: @_ZTVN10__cxxabiv120__si_class_type_infoE = external global [0 x ptr]
// CHECK:STDOUT: @_ZTSN6Carbon7DerivedE = linkonce_odr dso_local constant [18 x i8] c"N6Carbon7DerivedE\00", comdat, align 1
@@ -119,7 +117,7 @@ auto DoThing() -> void {
// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !14
// CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8
// CHECK:STDOUT: call void @_ZN4BaseC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this1) #5
// CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 16) ({ [4 x ptr] }, ptr @_ZTVN6Carbon7DerivedE, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !16
// CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 24) ({ [5 x ptr] }, ptr @_ZTVN6Carbon7DerivedE, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !16
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -129,7 +127,7 @@ auto DoThing() -> void {
// CHECK:STDOUT: %this.addr = alloca ptr, align 8
// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !14
// CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8
// CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 16) ({ [4 x ptr] }, ptr @_ZTVN6Carbon7DerivedE, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !16
// CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 24) ({ [5 x ptr] }, ptr @_ZTVN6Carbon7DerivedE, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !16
// CHECK:STDOUT: call void @"_C__destroy_thunk:thunk.Derived.Main"(ptr noundef nonnull align 8 dereferenceable(12) %this1)
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
@@ -226,6 +224,16 @@ auto DoThing() -> void {
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress nounwind uwtable
// CHECK:STDOUT: define internal void @_ZN6Carbon7Derived10other_funcEv(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #8 align 2 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %this.addr = alloca ptr, align 8
// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !14
// CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8
// CHECK:STDOUT: call void @_Cother_func__carbon_thunk.Derived.Main(ptr noundef nonnull align 8 dereferenceable(12) %this1)
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: inlinehint mustprogress nounwind uwtable
// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN11FurtherBaseC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this) unnamed_addr #3 comdat align 2 {
// CHECK:STDOUT: entry:
@@ -274,17 +282,17 @@ auto DoThing() -> void {
// CHECK:STDOUT: !15 = !{!"p1 _ZTSN6Carbon7DerivedE", !13, i64 0}
// CHECK:STDOUT: !16 = !{!17, !17, i64 0}
// CHECK:STDOUT: !17 = !{!"vtable pointer", !10, i64 0}
// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "other_func", linkageName: "_Cother_func.Derived.Main", scope: null, file: !6, line: 26, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !22)
// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "other_func", linkageName: "_Cother_func.Derived.Main", scope: null, file: !6, line: 24, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !22)
// CHECK:STDOUT: !19 = !DISubroutineType(types: !20)
// CHECK:STDOUT: !20 = !{null, !21}
// CHECK:STDOUT: !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
// CHECK:STDOUT: !22 = !{!23}
// CHECK:STDOUT: !23 = !DILocalVariable(arg: 1, scope: !18, type: !21)
// CHECK:STDOUT: !24 = !DILocation(line: 26, column: 3, scope: !18)
// CHECK:STDOUT: !25 = distinct !DISubprogram(name: "func", linkageName: "_Cfunc.Derived.Main", scope: null, file: !6, line: 28, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !26)
// CHECK:STDOUT: !24 = !DILocation(line: 24, column: 3, scope: !18)
// CHECK:STDOUT: !25 = distinct !DISubprogram(name: "func", linkageName: "_Cfunc.Derived.Main", scope: null, file: !6, line: 26, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !26)
// CHECK:STDOUT: !26 = !{!27}
// CHECK:STDOUT: !27 = !DILocalVariable(arg: 1, scope: !25, type: !21)
// CHECK:STDOUT: !28 = !DILocation(line: 28, column: 3, scope: !25)
// CHECK:STDOUT: !28 = !DILocation(line: 26, column: 3, scope: !25)
// CHECK:STDOUT: !29 = distinct !DISubprogram(name: "__destroy_thunk", linkageName: "_C__destroy_thunk:thunk.Derived.Main", scope: null, file: !6, line: 22, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !30)
// CHECK:STDOUT: !30 = !{!31}
// CHECK:STDOUT: !31 = !DILocalVariable(arg: 1, scope: !29, type: !21)
@@ -297,13 +305,13 @@ auto DoThing() -> void {
// CHECK:STDOUT: !38 = !{!39}
// CHECK:STDOUT: !39 = !DILocalVariable(arg: 1, scope: !37, type: !21)
// CHECK:STDOUT: !40 = !DILocation(line: 22, column: 1, scope: !37)
// CHECK:STDOUT: !41 = distinct !DISubprogram(name: "func__carbon_thunk", linkageName: "_Cfunc__carbon_thunk.Derived.Main", scope: null, file: !6, line: 28, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !42)
// CHECK:STDOUT: !41 = distinct !DISubprogram(name: "func__carbon_thunk", linkageName: "_Cfunc__carbon_thunk.Derived.Main", scope: null, file: !6, line: 26, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !42)
// CHECK:STDOUT: !42 = !{!43}
// CHECK:STDOUT: !43 = !DILocalVariable(arg: 1, scope: !41, type: !21)
// CHECK:STDOUT: !44 = !DILocation(line: 28, column: 3, scope: !41)
// CHECK:STDOUT: !45 = distinct !DISubprogram(name: "other_func__carbon_thunk", linkageName: "_Cother_func__carbon_thunk.Derived.Main", scope: null, file: !6, line: 26, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !46)
// CHECK:STDOUT: !44 = !DILocation(line: 26, column: 3, scope: !41)
// CHECK:STDOUT: !45 = distinct !DISubprogram(name: "other_func__carbon_thunk", linkageName: "_Cother_func__carbon_thunk.Derived.Main", scope: null, file: !6, line: 24, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !46)
// CHECK:STDOUT: !46 = !{!47}
// CHECK:STDOUT: !47 = !DILocalVariable(arg: 1, scope: !45, type: !21)
// CHECK:STDOUT: !48 = !DILocation(line: 26, column: 3, scope: !45)
// CHECK:STDOUT: !48 = !DILocation(line: 24, column: 3, scope: !45)
// CHECK:STDOUT: !49 = !{!50, !50, i64 0}
// CHECK:STDOUT: !50 = !{!"p1 _ZTS11FurtherBase", !13, i64 0}
@@ -80,13 +80,13 @@ void delete_new_Base() {
// CHECK:STDOUT:
// CHECK:STDOUT: $_ZTSN6Carbon4BaseE = comdat any
// CHECK:STDOUT:
// CHECK:STDOUT: @_ZTVN6Carbon5FinalE = linkonce_odr dso_local unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTIN6Carbon5FinalE, ptr @_ZN6Carbon5FinalD2Ev, ptr @_ZN6Carbon5FinalD0Ev, ptr @_ZNK1A1FEv] }, comdat, align 8
// CHECK:STDOUT: @_ZTVN6Carbon5FinalE = linkonce_odr dso_local unnamed_addr constant { [6 x ptr] } { [6 x ptr] [ptr null, ptr @_ZTIN6Carbon5FinalE, ptr @_ZN6Carbon5FinalD2Ev, ptr @_ZN6Carbon5FinalD0Ev, ptr @_ZNK1A1FEv, ptr @_ZN6Carbon5Final1FEv] }, comdat, align 8
// CHECK:STDOUT: @_ZTIN6Carbon5FinalE = linkonce_odr dso_local constant { ptr, ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv120__si_class_type_infoE, i64 2), ptr @_ZTSN6Carbon5FinalE, ptr @_ZTI1A }, comdat, align 8
// CHECK:STDOUT: @_ZTVN10__cxxabiv120__si_class_type_infoE = external global [0 x ptr]
// CHECK:STDOUT: @_ZTSN6Carbon5FinalE = linkonce_odr dso_local constant [16 x i8] c"N6Carbon5FinalE\00", comdat, align 1
// CHECK:STDOUT: @_ZTI1A = external constant ptr
// CHECK:STDOUT: @_ZTV1A = available_externally unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTI1A, ptr @__cxa_pure_virtual, ptr @__cxa_pure_virtual, ptr @_ZNK1A1FEv] }, align 8
// CHECK:STDOUT: @_ZTVN6Carbon4BaseE = linkonce_odr dso_local unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTIN6Carbon4BaseE, ptr @_ZN6Carbon4BaseD2Ev, ptr @_ZN6Carbon4BaseD0Ev, ptr @_ZNK1A1FEv] }, comdat, align 8
// CHECK:STDOUT: @_ZTVN6Carbon4BaseE = linkonce_odr dso_local unnamed_addr constant { [6 x ptr] } { [6 x ptr] [ptr null, ptr @_ZTIN6Carbon4BaseE, ptr @_ZN6Carbon4BaseD2Ev, ptr @_ZN6Carbon4BaseD0Ev, ptr @_ZNK1A1FEv, ptr @_ZN6Carbon4Base1FEv] }, comdat, align 8
// CHECK:STDOUT: @_ZTIN6Carbon4BaseE = linkonce_odr dso_local constant { ptr, ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv120__si_class_type_infoE, i64 2), ptr @_ZTSN6Carbon4BaseE, ptr @_ZTI1A }, comdat, align 8
// CHECK:STDOUT: @_ZTSN6Carbon4BaseE = linkonce_odr dso_local constant [15 x i8] c"N6Carbon4BaseE\00", comdat, align 1
// CHECK:STDOUT:
@@ -125,7 +125,7 @@ void delete_new_Base() {
// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !11
// CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8
// CHECK:STDOUT: call void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this1) #8
// CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 24) ({ [5 x ptr] }, ptr @_ZTVN6Carbon5FinalE, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !14
// CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 32) ({ [6 x ptr] }, ptr @_ZTVN6Carbon5FinalE, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !14
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -177,7 +177,7 @@ void delete_new_Base() {
// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !16
// CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8
// CHECK:STDOUT: call void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this1) #8
// CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 24) ({ [5 x ptr] }, ptr @_ZTVN6Carbon4BaseE, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !14
// CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 32) ({ [6 x ptr] }, ptr @_ZTVN6Carbon4BaseE, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !14
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -277,6 +277,16 @@ void delete_new_Base() {
// CHECK:STDOUT:
// CHECK:STDOUT: declare void @_ZNK1A1FEv(ptr noundef nonnull align 8 dereferenceable(8)) unnamed_addr #10
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress nounwind uwtable
// CHECK:STDOUT: define internal void @_ZN6Carbon5Final1FEv(ptr noundef nonnull align 8 dereferenceable(8) %this) unnamed_addr #5 align 2 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %this.addr = alloca ptr, align 8
// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !11
// CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8
// CHECK:STDOUT: call void @_CF__carbon_thunk.Final.Main(ptr noundef nonnull align 8 dereferenceable(8) %this1)
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: declare void @__cxa_pure_virtual() unnamed_addr
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
@@ -288,7 +298,7 @@ void delete_new_Base() {
// CHECK:STDOUT: %this.addr = alloca ptr, align 8
// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !16
// CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8
// CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 24) ({ [5 x ptr] }, ptr @_ZTVN6Carbon4BaseE, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !14
// CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 32) ({ [6 x ptr] }, ptr @_ZTVN6Carbon4BaseE, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !14
// CHECK:STDOUT: call void @"_C__destroy_thunk:thunk.Base.Main"(ptr noundef nonnull align 8 dereferenceable(8) %this1)
// CHECK:STDOUT: call void @_ZN1AD2Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %this1) #8
// CHECK:STDOUT: ret void
@@ -305,6 +315,16 @@ void delete_new_Base() {
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress nounwind uwtable
// CHECK:STDOUT: define internal void @_ZN6Carbon4Base1FEv(ptr noundef nonnull align 8 dereferenceable(8) %this) unnamed_addr #5 align 2 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %this.addr = alloca ptr, align 8
// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !16
// CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8
// CHECK:STDOUT: call void @_CF__carbon_thunk.Base.Main(ptr noundef nonnull align 8 dereferenceable(8) %this1)
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; uselistorder directives
// CHECK:STDOUT: uselistorder ptr @_ZN6Carbon5FinalD2Ev, { 2, 1, 0 }
// CHECK:STDOUT: uselistorder ptr @llvm.load.relative.i32, { 1, 0 }