diff --git a/toolchain/check/cpp/export.cpp b/toolchain/check/cpp/export.cpp index 1d56a4fa41ff..96db62126188 100644 --- a/toolchain/check/cpp/export.cpp +++ b/toolchain/check/cpp/export.cpp @@ -535,6 +535,9 @@ static auto BuildCppToCarbonThunkDecl( constexpr_kind, clang_loc, trailing_requires_clause); // TODO: Map Carbon access to C++ access. thunk_function_decl->setAccess(clang::AS_public); + thunk_function_decl->setVirtualAsWritten( + target.function.virtual_modifier != + SemIR::Function::VirtualModifier::None); } else { thunk_function_decl = clang::FunctionDecl::Create( ast_context, target.decl_context, clang_loc, name_info, diff --git a/toolchain/lower/testdata/interop/cpp/class/import/dynamic.carbon b/toolchain/lower/testdata/interop/cpp/class/import/dynamic.carbon index 2b2bca3db762..9efdcb530260 100644 --- a/toolchain/lower/testdata/interop/cpp/class/import/dynamic.carbon +++ b/toolchain/lower/testdata/interop/cpp/class/import/dynamic.carbon @@ -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} diff --git a/toolchain/lower/testdata/interop/cpp/class/virtual_fn.carbon b/toolchain/lower/testdata/interop/cpp/class/virtual_fn.carbon index b9ed93579d91..589b535ccc03 100644 --- a/toolchain/lower/testdata/interop/cpp/class/virtual_fn.carbon +++ b/toolchain/lower/testdata/interop/cpp/class/virtual_fn.carbon @@ -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 }