From 26ec78ec00cc00f7d6c4c6edf94b8f558100d4b8 Mon Sep 17 00:00:00 2001 From: David Blaikie Date: Mon, 28 Jul 2025 14:06:57 -0700 Subject: [PATCH] Ensure vtable entries for generics are attached constants (#5853) Otherwise these end up as unattached constants (see the baseline test changes) and can't be resolved by `GetConstantValueInSpecific` in lowering or in further derived vtables. If the class is non-generic, then it's fine for the vtable entry for some function inherited from a generic base is represented as an unattached constant, since the specific in that specific_function is already fully resolved. --------- Co-authored-by: Dana Jansens --- toolchain/check/class.cpp | 19 +- .../testdata/class/virtual_modifiers.carbon | 439 ++++++++++++++++++ toolchain/lower/file_context.cpp | 2 +- toolchain/sem_ir/function.cpp | 6 +- toolchain/sem_ir/function.h | 4 +- 5 files changed, 465 insertions(+), 5 deletions(-) diff --git a/toolchain/check/class.cpp b/toolchain/check/class.cpp index 007028d43965..508e8d470d8e 100644 --- a/toolchain/check/class.cpp +++ b/toolchain/check/class.cpp @@ -176,7 +176,8 @@ static auto BuildVtable(Context& context, Parse::ClassDefinitionId node_id, // TODO: Avoid quadratic search. Perhaps build a map from `NameId` to the // elements of the top of `vtable_stack`. for (auto base_vtable_entry_id : base_vtable_inst_block) { - auto [derived_vtable_entry_id, fn_id, specific_id] = + auto [derived_vtable_entry_id, derived_vtable_entry_const_id, fn_id, + specific_id] = DecomposeVirtualFunction(context.sem_ir(), base_vtable_entry_id, base_class_specific_id); const auto& fn = context.sem_ir().functions().Get(fn_id); @@ -201,6 +202,22 @@ static auto BuildVtable(Context& context, Parse::ClassDefinitionId node_id, derived_vtable_entry_id = build_specific_function(*i); override_fn.virtual_index = vtable.size(); CARBON_CHECK(override_fn.virtual_index == fn.virtual_index); + } else if (auto base_vtable_specific_function = + context.sem_ir().insts().TryGetAs( + derived_vtable_entry_id)) { + if (derived_vtable_entry_const_id.is_symbolic()) { + // Create a new instruction here that is otherwise identical to + // `derived_vtable_entry_id` but is dependent within the derived + // class. This ensures we can `GetConstantValueInSpecific` for it + // with the derived class's specific (when forming further derived + // classes, lowering the vtable, etc). + derived_vtable_entry_id = GetOrAddInst( + context, node_id, + {.type_id = GetSingletonType( + context, SemIR::SpecificFunctionType::TypeInstId), + .callee_id = base_vtable_specific_function->callee_id, + .specific_id = base_vtable_specific_function->specific_id}); + } } vtable.push_back(derived_vtable_entry_id); } diff --git a/toolchain/check/testdata/class/virtual_modifiers.carbon b/toolchain/check/testdata/class/virtual_modifiers.carbon index a0524cf6266d..08f3783ff12f 100644 --- a/toolchain/check/testdata/class/virtual_modifiers.carbon +++ b/toolchain/check/testdata/class/virtual_modifiers.carbon @@ -489,6 +489,33 @@ import library "generic_lib"; class T1; var v: Base(T1) = {}; +// --- generic_derived_generic.carbon + +library "[[@TEST_NAME]]"; + +base class T1(G1:! type) { + virtual fn F[self: Self]() { } +} + +class T2(G2:! type) { + extend base: T1(G2); +} + +// --- generic_derived_generic_context.carbon + +library "[[@TEST_NAME]]"; + +base class T1(G1:! type) { + virtual fn F[self: Self]() { } +} + +class T2(G2:! type) { + class T3 { + extend base : T1(G2); + } +} + + // CHECK:STDOUT: --- modifiers.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -4957,3 +4984,415 @@ var v: Base(T1) = {}; // CHECK:STDOUT: %require_complete => constants.%complete_type // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- generic_derived_generic.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %G1: type = bind_symbolic_name G1, 0 [symbolic] +// CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] +// CHECK:STDOUT: %T1.type: type = generic_class_type @T1 [concrete] +// CHECK:STDOUT: %T1.generic: %T1.type = struct_value () [concrete] +// CHECK:STDOUT: %T1.18aea2.1: type = class_type @T1, @T1(%G1) [symbolic] +// CHECK:STDOUT: %pattern_type.48ecf1.1: type = pattern_type %T1.18aea2.1 [symbolic] +// CHECK:STDOUT: %T1.F.type.ebcc3f.1: type = fn_type @T1.F, @T1(%G1) [symbolic] +// CHECK:STDOUT: %T1.F.0df085.1: %T1.F.type.ebcc3f.1 = struct_value () [symbolic] +// CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] +// CHECK:STDOUT: %T1.F.specific_fn.1e2a8c.1: = specific_function %T1.F.0df085.1, @T1.F(%G1) [symbolic] +// CHECK:STDOUT: %T1.vtable_ptr.b5fc91.1: ref %ptr.454 = vtable_ptr @T1.vtable, @T1(%G1) [symbolic] +// CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] +// CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] +// CHECK:STDOUT: %require_complete.86d019.1: = require_complete_type %T1.18aea2.1 [symbolic] +// CHECK:STDOUT: %G2: type = bind_symbolic_name G2, 0 [symbolic] +// CHECK:STDOUT: %T2.type: type = generic_class_type @T2 [concrete] +// CHECK:STDOUT: %T2.generic: %T2.type = struct_value () [concrete] +// CHECK:STDOUT: %T2: type = class_type @T2, @T2(%G2) [symbolic] +// CHECK:STDOUT: %T1.18aea2.2: type = class_type @T1, @T1(%G2) [symbolic] +// CHECK:STDOUT: %T1.F.type.ebcc3f.2: type = fn_type @T1.F, @T1(%G2) [symbolic] +// CHECK:STDOUT: %T1.F.0df085.2: %T1.F.type.ebcc3f.2 = struct_value () [symbolic] +// CHECK:STDOUT: %pattern_type.48ecf1.2: type = pattern_type %T1.18aea2.2 [symbolic] +// CHECK:STDOUT: %T1.F.specific_fn.1e2a8c.2: = specific_function %T1.F.0df085.2, @T1.F(%G2) [symbolic] +// CHECK:STDOUT: %T1.vtable_ptr.b5fc91.2: ref %ptr.454 = vtable_ptr @T1.vtable, @T1(%G2) [symbolic] +// CHECK:STDOUT: %require_complete.86d019.2: = require_complete_type %T1.18aea2.2 [symbolic] +// CHECK:STDOUT: %T2.elem: type = unbound_element_type %T2, %T1.18aea2.2 [symbolic] +// CHECK:STDOUT: %T2.vtable_ptr: ref %ptr.454 = vtable_ptr @T2.vtable, @T2(%G2) [symbolic] +// CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %T1.18aea2.2} [symbolic] +// CHECK:STDOUT: %complete_type.987: = complete_type_witness %struct_type.base [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .T1 = %T1.decl +// CHECK:STDOUT: .T2 = %T2.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %T1.decl: %T1.type = class_decl @T1 [concrete = constants.%T1.generic] { +// CHECK:STDOUT: %G1.patt: %pattern_type.98f = symbolic_binding_pattern G1, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %G1.loc4_15.2: type = bind_symbolic_name G1, 0 [symbolic = %G1.loc4_15.1 (constants.%G1)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %T2.decl: %T2.type = class_decl @T2 [concrete = constants.%T2.generic] { +// CHECK:STDOUT: %G2.patt: %pattern_type.98f = symbolic_binding_pattern G2, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %G2.loc8_10.2: type = bind_symbolic_name G2, 0 [symbolic = %G2.loc8_10.1 (constants.%G2)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @T1(%G1.loc4_15.2: type) { +// CHECK:STDOUT: %G1.loc4_15.1: type = bind_symbolic_name G1, 0 [symbolic = %G1.loc4_15.1 (constants.%G1)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F, @T1(%G1.loc4_15.1) [symbolic = %T1.F.type (constants.%T1.F.type.ebcc3f.1)] +// CHECK:STDOUT: %T1.F: @T1.%T1.F.type (%T1.F.type.ebcc3f.1) = struct_value () [symbolic = %T1.F (constants.%T1.F.0df085.1)] +// CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2: = specific_function %T1.F, @T1.F(%G1.loc4_15.1) [symbolic = %T1.F.specific_fn.loc6_1.2 (constants.%T1.F.specific_fn.1e2a8c.1)] +// CHECK:STDOUT: %vtable_ptr.loc6_1.2: ref %ptr.454 = vtable_ptr @T1.vtable, @T1(%G1.loc4_15.1) [symbolic = %vtable_ptr.loc6_1.2 (constants.%T1.vtable_ptr.b5fc91.1)] +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %T1.F.decl: @T1.%T1.F.type (%T1.F.type.ebcc3f.1) = fn_decl @T1.F [symbolic = @T1.%T1.F (constants.%T1.F.0df085.1)] { +// CHECK:STDOUT: %self.patt: @T1.F.%pattern_type (%pattern_type.48ecf1.1) = binding_pattern self [concrete] +// CHECK:STDOUT: %self.param_patt: @T1.F.%pattern_type (%pattern_type.48ecf1.1) = value_param_pattern %self.patt, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: @T1.F.%T1 (%T1.18aea2.1) = value_param call_param0 +// CHECK:STDOUT: %.loc5_22.1: type = splice_block %Self.ref [symbolic = %T1 (constants.%T1.18aea2.1)] { +// CHECK:STDOUT: %.loc5_22.2: type = specific_constant constants.%T1.18aea2.1, @T1(constants.%G1) [symbolic = %T1 (constants.%T1.18aea2.1)] +// CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc5_22.2 [symbolic = %T1 (constants.%T1.18aea2.1)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %self: @T1.F.%T1 (%T1.18aea2.1) = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %T1.F.specific_fn.loc6_1.1: = specific_function %T1.F.decl, @T1.F(constants.%G1) [symbolic = %T1.F.specific_fn.loc6_1.2 (constants.%T1.F.specific_fn.1e2a8c.1)] +// CHECK:STDOUT: %vtable_ptr.loc6_1.1: ref %ptr.454 = vtable_ptr @T1.vtable, @T1(constants.%G1) [symbolic = %vtable_ptr.loc6_1.2 (constants.%T1.vtable_ptr.b5fc91.1)] +// CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete = constants.%struct_type.vptr] +// CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type.513] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: vtable_ptr = %vtable_ptr.loc6_1.1 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%T1.18aea2.1 +// CHECK:STDOUT: .F = %T1.F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @T2(%G2.loc8_10.2: type) { +// CHECK:STDOUT: %G2.loc8_10.1: type = bind_symbolic_name G2, 0 [symbolic = %G2.loc8_10.1 (constants.%G2)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %T1.loc9_21.2: type = class_type @T1, @T1(%G2.loc8_10.1) [symbolic = %T1.loc9_21.2 (constants.%T1.18aea2.2)] +// CHECK:STDOUT: %require_complete: = require_complete_type %T1.loc9_21.2 [symbolic = %require_complete (constants.%require_complete.86d019.2)] +// CHECK:STDOUT: %T2: type = class_type @T2, @T2(%G2.loc8_10.1) [symbolic = %T2 (constants.%T2)] +// CHECK:STDOUT: %T2.elem: type = unbound_element_type %T2, %T1.loc9_21.2 [symbolic = %T2.elem (constants.%T2.elem)] +// CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F, @T1(%G2.loc8_10.1) [symbolic = %T1.F.type (constants.%T1.F.type.ebcc3f.2)] +// CHECK:STDOUT: %T1.F: @T2.%T1.F.type (%T1.F.type.ebcc3f.2) = struct_value () [symbolic = %T1.F (constants.%T1.F.0df085.2)] +// CHECK:STDOUT: %T1.F.specific_fn.loc10_1.2: = specific_function %T1.F, @T1.F(%G2.loc8_10.1) [symbolic = %T1.F.specific_fn.loc10_1.2 (constants.%T1.F.specific_fn.1e2a8c.2)] +// CHECK:STDOUT: %vtable_ptr.loc10_1.2: ref %ptr.454 = vtable_ptr @T2.vtable, @T2(%G2.loc8_10.1) [symbolic = %vtable_ptr.loc10_1.2 (constants.%T2.vtable_ptr)] +// CHECK:STDOUT: %struct_type.base.loc10_1.2: type = struct_type {.base: @T2.%T1.loc9_21.2 (%T1.18aea2.2)} [symbolic = %struct_type.base.loc10_1.2 (constants.%struct_type.base)] +// CHECK:STDOUT: %complete_type.loc10_1.2: = complete_type_witness %struct_type.base.loc10_1.2 [symbolic = %complete_type.loc10_1.2 (constants.%complete_type.987)] +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %T1.ref: %T1.type = name_ref T1, file.%T1.decl [concrete = constants.%T1.generic] +// CHECK:STDOUT: %G2.ref: type = name_ref G2, %G2.loc8_10.2 [symbolic = %G2.loc8_10.1 (constants.%G2)] +// CHECK:STDOUT: %T1.loc9_21.1: type = class_type @T1, @T1(constants.%G2) [symbolic = %T1.loc9_21.2 (constants.%T1.18aea2.2)] +// CHECK:STDOUT: %.loc9: @T2.%T2.elem (%T2.elem) = base_decl %T1.loc9_21.1, element0 [concrete] +// CHECK:STDOUT: %T1.F.specific_fn.loc10_1.1: = specific_function constants.%T1.F.0df085.2, @T1.F(constants.%G2) [symbolic = %T1.F.specific_fn.loc10_1.2 (constants.%T1.F.specific_fn.1e2a8c.2)] +// CHECK:STDOUT: %vtable_ptr.loc10_1.1: ref %ptr.454 = vtable_ptr @T2.vtable, @T2(constants.%G2) [symbolic = %vtable_ptr.loc10_1.2 (constants.%T2.vtable_ptr)] +// CHECK:STDOUT: %struct_type.base.loc10_1.1: type = struct_type {.base: %T1.18aea2.2} [symbolic = %struct_type.base.loc10_1.2 (constants.%struct_type.base)] +// CHECK:STDOUT: %complete_type.loc10_1.1: = complete_type_witness %struct_type.base.loc10_1.1 [symbolic = %complete_type.loc10_1.2 (constants.%complete_type.987)] +// CHECK:STDOUT: complete_type_witness = %complete_type.loc10_1.1 +// CHECK:STDOUT: vtable_ptr = %vtable_ptr.loc10_1.1 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%T2 +// CHECK:STDOUT: .T1 = +// CHECK:STDOUT: .G2 = +// CHECK:STDOUT: .base = %.loc9 +// CHECK:STDOUT: extend %T1.loc9_21.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: vtable @T1.vtable { +// CHECK:STDOUT: @T1.%T1.F.specific_fn.loc6_1.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: vtable @T2.vtable { +// CHECK:STDOUT: @T2.%T1.F.specific_fn.loc10_1.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic virtual fn @T1.F(@T1.%G1.loc4_15.2: type) { +// CHECK:STDOUT: %G1: type = bind_symbolic_name G1, 0 [symbolic = %G1 (constants.%G1)] +// CHECK:STDOUT: %T1: type = class_type @T1, @T1(%G1) [symbolic = %T1 (constants.%T1.18aea2.1)] +// CHECK:STDOUT: %pattern_type: type = pattern_type %T1 [symbolic = %pattern_type (constants.%pattern_type.48ecf1.1)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %require_complete: = require_complete_type %T1 [symbolic = %require_complete (constants.%require_complete.86d019.1)] +// CHECK:STDOUT: +// CHECK:STDOUT: virtual fn(%self.param: @T1.F.%T1 (%T1.18aea2.1)) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1(constants.%G1) { +// CHECK:STDOUT: %G1.loc4_15.1 => constants.%G1 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %T1.F.type => constants.%T1.F.type.ebcc3f.1 +// CHECK:STDOUT: %T1.F => constants.%T1.F.0df085.1 +// CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2 => constants.%T1.F.specific_fn.1e2a8c.1 +// CHECK:STDOUT: %vtable_ptr.loc6_1.2 => constants.%T1.vtable_ptr.b5fc91.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1.F(constants.%G1) { +// CHECK:STDOUT: %G1 => constants.%G1 +// CHECK:STDOUT: %T1 => constants.%T1.18aea2.1 +// CHECK:STDOUT: %pattern_type => constants.%pattern_type.48ecf1.1 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %require_complete => constants.%require_complete.86d019.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T2(constants.%G2) { +// CHECK:STDOUT: %G2.loc8_10.1 => constants.%G2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1(constants.%G2) { +// CHECK:STDOUT: %G1.loc4_15.1 => constants.%G2 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %T1.F.type => constants.%T1.F.type.ebcc3f.2 +// CHECK:STDOUT: %T1.F => constants.%T1.F.0df085.2 +// CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2 => constants.%T1.F.specific_fn.1e2a8c.2 +// CHECK:STDOUT: %vtable_ptr.loc6_1.2 => constants.%T1.vtable_ptr.b5fc91.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1.F(constants.%G2) { +// CHECK:STDOUT: %G1 => constants.%G2 +// CHECK:STDOUT: %T1 => constants.%T1.18aea2.2 +// CHECK:STDOUT: %pattern_type => constants.%pattern_type.48ecf1.2 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %require_complete => constants.%require_complete.86d019.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- generic_derived_generic_context.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %G1: type = bind_symbolic_name G1, 0 [symbolic] +// CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete] +// CHECK:STDOUT: %T1.type: type = generic_class_type @T1 [concrete] +// CHECK:STDOUT: %T1.generic: %T1.type = struct_value () [concrete] +// CHECK:STDOUT: %T1.18aea2.1: type = class_type @T1, @T1(%G1) [symbolic] +// CHECK:STDOUT: %pattern_type.48ecf1.1: type = pattern_type %T1.18aea2.1 [symbolic] +// CHECK:STDOUT: %T1.F.type.ebcc3f.1: type = fn_type @T1.F, @T1(%G1) [symbolic] +// CHECK:STDOUT: %T1.F.0df085.1: %T1.F.type.ebcc3f.1 = struct_value () [symbolic] +// CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] +// CHECK:STDOUT: %T1.F.specific_fn.1e2a8c.1: = specific_function %T1.F.0df085.1, @T1.F(%G1) [symbolic] +// CHECK:STDOUT: %T1.vtable_ptr.b5fc91.1: ref %ptr.454 = vtable_ptr @T1.vtable, @T1(%G1) [symbolic] +// CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] +// CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] +// CHECK:STDOUT: %require_complete.86d019.1: = require_complete_type %T1.18aea2.1 [symbolic] +// CHECK:STDOUT: %G2: type = bind_symbolic_name G2, 0 [symbolic] +// CHECK:STDOUT: %T2.type: type = generic_class_type @T2 [concrete] +// CHECK:STDOUT: %T2.generic: %T2.type = struct_value () [concrete] +// CHECK:STDOUT: %T2: type = class_type @T2, @T2(%G2) [symbolic] +// CHECK:STDOUT: %T3: type = class_type @T3, @T3(%G2) [symbolic] +// CHECK:STDOUT: %T1.18aea2.2: type = class_type @T1, @T1(%G2) [symbolic] +// CHECK:STDOUT: %T1.F.type.ebcc3f.2: type = fn_type @T1.F, @T1(%G2) [symbolic] +// CHECK:STDOUT: %T1.F.0df085.2: %T1.F.type.ebcc3f.2 = struct_value () [symbolic] +// CHECK:STDOUT: %pattern_type.48ecf1.2: type = pattern_type %T1.18aea2.2 [symbolic] +// CHECK:STDOUT: %T1.F.specific_fn.1e2a8c.2: = specific_function %T1.F.0df085.2, @T1.F(%G2) [symbolic] +// CHECK:STDOUT: %T1.vtable_ptr.b5fc91.2: ref %ptr.454 = vtable_ptr @T1.vtable, @T1(%G2) [symbolic] +// CHECK:STDOUT: %require_complete.86d019.2: = require_complete_type %T1.18aea2.2 [symbolic] +// CHECK:STDOUT: %T3.elem: type = unbound_element_type %T3, %T1.18aea2.2 [symbolic] +// CHECK:STDOUT: %T3.vtable_ptr: ref %ptr.454 = vtable_ptr @T3.vtable, @T3(%G2) [symbolic] +// CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %T1.18aea2.2} [symbolic] +// CHECK:STDOUT: %complete_type.987: = complete_type_witness %struct_type.base [symbolic] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .T1 = %T1.decl +// CHECK:STDOUT: .T2 = %T2.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %T1.decl: %T1.type = class_decl @T1 [concrete = constants.%T1.generic] { +// CHECK:STDOUT: %G1.patt: %pattern_type.98f = symbolic_binding_pattern G1, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %G1.loc4_15.2: type = bind_symbolic_name G1, 0 [symbolic = %G1.loc4_15.1 (constants.%G1)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %T2.decl: %T2.type = class_decl @T2 [concrete = constants.%T2.generic] { +// CHECK:STDOUT: %G2.patt: %pattern_type.98f = symbolic_binding_pattern G2, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %G2.loc8_10.2: type = bind_symbolic_name G2, 0 [symbolic = %G2.loc8_10.1 (constants.%G2)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @T1(%G1.loc4_15.2: type) { +// CHECK:STDOUT: %G1.loc4_15.1: type = bind_symbolic_name G1, 0 [symbolic = %G1.loc4_15.1 (constants.%G1)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F, @T1(%G1.loc4_15.1) [symbolic = %T1.F.type (constants.%T1.F.type.ebcc3f.1)] +// CHECK:STDOUT: %T1.F: @T1.%T1.F.type (%T1.F.type.ebcc3f.1) = struct_value () [symbolic = %T1.F (constants.%T1.F.0df085.1)] +// CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2: = specific_function %T1.F, @T1.F(%G1.loc4_15.1) [symbolic = %T1.F.specific_fn.loc6_1.2 (constants.%T1.F.specific_fn.1e2a8c.1)] +// CHECK:STDOUT: %vtable_ptr.loc6_1.2: ref %ptr.454 = vtable_ptr @T1.vtable, @T1(%G1.loc4_15.1) [symbolic = %vtable_ptr.loc6_1.2 (constants.%T1.vtable_ptr.b5fc91.1)] +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %T1.F.decl: @T1.%T1.F.type (%T1.F.type.ebcc3f.1) = fn_decl @T1.F [symbolic = @T1.%T1.F (constants.%T1.F.0df085.1)] { +// CHECK:STDOUT: %self.patt: @T1.F.%pattern_type (%pattern_type.48ecf1.1) = binding_pattern self [concrete] +// CHECK:STDOUT: %self.param_patt: @T1.F.%pattern_type (%pattern_type.48ecf1.1) = value_param_pattern %self.patt, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: @T1.F.%T1 (%T1.18aea2.1) = value_param call_param0 +// CHECK:STDOUT: %.loc5_22.1: type = splice_block %Self.ref [symbolic = %T1 (constants.%T1.18aea2.1)] { +// CHECK:STDOUT: %.loc5_22.2: type = specific_constant constants.%T1.18aea2.1, @T1(constants.%G1) [symbolic = %T1 (constants.%T1.18aea2.1)] +// CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc5_22.2 [symbolic = %T1 (constants.%T1.18aea2.1)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %self: @T1.F.%T1 (%T1.18aea2.1) = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %T1.F.specific_fn.loc6_1.1: = specific_function %T1.F.decl, @T1.F(constants.%G1) [symbolic = %T1.F.specific_fn.loc6_1.2 (constants.%T1.F.specific_fn.1e2a8c.1)] +// CHECK:STDOUT: %vtable_ptr.loc6_1.1: ref %ptr.454 = vtable_ptr @T1.vtable, @T1(constants.%G1) [symbolic = %vtable_ptr.loc6_1.2 (constants.%T1.vtable_ptr.b5fc91.1)] +// CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete = constants.%struct_type.vptr] +// CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type.513] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: vtable_ptr = %vtable_ptr.loc6_1.1 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%T1.18aea2.1 +// CHECK:STDOUT: .F = %T1.F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @T2(%G2.loc8_10.2: type) { +// CHECK:STDOUT: %G2.loc8_10.1: type = bind_symbolic_name G2, 0 [symbolic = %G2.loc8_10.1 (constants.%G2)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %T3: type = class_type @T3, @T3(%G2.loc8_10.1) [symbolic = %T3 (constants.%T3)] +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %T3.decl: type = class_decl @T3 [symbolic = @T2.%T3 (constants.%T3)] {} {} +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%T2 +// CHECK:STDOUT: .T3 = %T3.decl +// CHECK:STDOUT: .T1 = +// CHECK:STDOUT: .G2 = +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @T3(@T2.%G2.loc8_10.2: type) { +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %G2: type = bind_symbolic_name G2, 0 [symbolic = %G2 (constants.%G2)] +// CHECK:STDOUT: %T1.loc10_24.2: type = class_type @T1, @T1(%G2) [symbolic = %T1.loc10_24.2 (constants.%T1.18aea2.2)] +// CHECK:STDOUT: %require_complete: = require_complete_type %T1.loc10_24.2 [symbolic = %require_complete (constants.%require_complete.86d019.2)] +// CHECK:STDOUT: %T3: type = class_type @T3, @T3(%G2) [symbolic = %T3 (constants.%T3)] +// CHECK:STDOUT: %T3.elem: type = unbound_element_type %T3, %T1.loc10_24.2 [symbolic = %T3.elem (constants.%T3.elem)] +// CHECK:STDOUT: %T1.F.type: type = fn_type @T1.F, @T1(%G2) [symbolic = %T1.F.type (constants.%T1.F.type.ebcc3f.2)] +// CHECK:STDOUT: %T1.F: @T3.%T1.F.type (%T1.F.type.ebcc3f.2) = struct_value () [symbolic = %T1.F (constants.%T1.F.0df085.2)] +// CHECK:STDOUT: %T1.F.specific_fn.loc11_3.2: = specific_function %T1.F, @T1.F(%G2) [symbolic = %T1.F.specific_fn.loc11_3.2 (constants.%T1.F.specific_fn.1e2a8c.2)] +// CHECK:STDOUT: %vtable_ptr.loc11_3.2: ref %ptr.454 = vtable_ptr @T3.vtable, @T3(%G2) [symbolic = %vtable_ptr.loc11_3.2 (constants.%T3.vtable_ptr)] +// CHECK:STDOUT: %struct_type.base.loc11_3.2: type = struct_type {.base: @T3.%T1.loc10_24.2 (%T1.18aea2.2)} [symbolic = %struct_type.base.loc11_3.2 (constants.%struct_type.base)] +// CHECK:STDOUT: %complete_type.loc11_3.2: = complete_type_witness %struct_type.base.loc11_3.2 [symbolic = %complete_type.loc11_3.2 (constants.%complete_type.987)] +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %T1.ref: %T1.type = name_ref T1, file.%T1.decl [concrete = constants.%T1.generic] +// CHECK:STDOUT: %G2.ref: type = name_ref G2, @T2.%G2.loc8_10.2 [symbolic = %G2 (constants.%G2)] +// CHECK:STDOUT: %T1.loc10_24.1: type = class_type @T1, @T1(constants.%G2) [symbolic = %T1.loc10_24.2 (constants.%T1.18aea2.2)] +// CHECK:STDOUT: %.loc10: @T3.%T3.elem (%T3.elem) = base_decl %T1.loc10_24.1, element0 [concrete] +// CHECK:STDOUT: %T1.F.specific_fn.loc11_3.1: = specific_function constants.%T1.F.0df085.2, @T1.F(constants.%G2) [symbolic = %T1.F.specific_fn.loc11_3.2 (constants.%T1.F.specific_fn.1e2a8c.2)] +// CHECK:STDOUT: %vtable_ptr.loc11_3.1: ref %ptr.454 = vtable_ptr @T3.vtable, @T3(constants.%G2) [symbolic = %vtable_ptr.loc11_3.2 (constants.%T3.vtable_ptr)] +// CHECK:STDOUT: %struct_type.base.loc11_3.1: type = struct_type {.base: %T1.18aea2.2} [symbolic = %struct_type.base.loc11_3.2 (constants.%struct_type.base)] +// CHECK:STDOUT: %complete_type.loc11_3.1: = complete_type_witness %struct_type.base.loc11_3.1 [symbolic = %complete_type.loc11_3.2 (constants.%complete_type.987)] +// CHECK:STDOUT: complete_type_witness = %complete_type.loc11_3.1 +// CHECK:STDOUT: vtable_ptr = %vtable_ptr.loc11_3.1 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%T3 +// CHECK:STDOUT: .T1 = +// CHECK:STDOUT: .G2 = +// CHECK:STDOUT: .base = %.loc10 +// CHECK:STDOUT: extend %T1.loc10_24.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: vtable @T1.vtable { +// CHECK:STDOUT: @T1.%T1.F.specific_fn.loc6_1.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: vtable @T3.vtable { +// CHECK:STDOUT: @T3.%T1.F.specific_fn.loc11_3.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic virtual fn @T1.F(@T1.%G1.loc4_15.2: type) { +// CHECK:STDOUT: %G1: type = bind_symbolic_name G1, 0 [symbolic = %G1 (constants.%G1)] +// CHECK:STDOUT: %T1: type = class_type @T1, @T1(%G1) [symbolic = %T1 (constants.%T1.18aea2.1)] +// CHECK:STDOUT: %pattern_type: type = pattern_type %T1 [symbolic = %pattern_type (constants.%pattern_type.48ecf1.1)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %require_complete: = require_complete_type %T1 [symbolic = %require_complete (constants.%require_complete.86d019.1)] +// CHECK:STDOUT: +// CHECK:STDOUT: virtual fn(%self.param: @T1.F.%T1 (%T1.18aea2.1)) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1(constants.%G1) { +// CHECK:STDOUT: %G1.loc4_15.1 => constants.%G1 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %T1.F.type => constants.%T1.F.type.ebcc3f.1 +// CHECK:STDOUT: %T1.F => constants.%T1.F.0df085.1 +// CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2 => constants.%T1.F.specific_fn.1e2a8c.1 +// CHECK:STDOUT: %vtable_ptr.loc6_1.2 => constants.%T1.vtable_ptr.b5fc91.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1.F(constants.%G1) { +// CHECK:STDOUT: %G1 => constants.%G1 +// CHECK:STDOUT: %T1 => constants.%T1.18aea2.1 +// CHECK:STDOUT: %pattern_type => constants.%pattern_type.48ecf1.1 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %require_complete => constants.%require_complete.86d019.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T2(constants.%G2) { +// CHECK:STDOUT: %G2.loc8_10.1 => constants.%G2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T3(constants.%G2) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1(constants.%G2) { +// CHECK:STDOUT: %G1.loc4_15.1 => constants.%G2 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %T1.F.type => constants.%T1.F.type.ebcc3f.2 +// CHECK:STDOUT: %T1.F => constants.%T1.F.0df085.2 +// CHECK:STDOUT: %T1.F.specific_fn.loc6_1.2 => constants.%T1.F.specific_fn.1e2a8c.2 +// CHECK:STDOUT: %vtable_ptr.loc6_1.2 => constants.%T1.vtable_ptr.b5fc91.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1.F(constants.%G2) { +// CHECK:STDOUT: %G1 => constants.%G2 +// CHECK:STDOUT: %T1 => constants.%T1.18aea2.2 +// CHECK:STDOUT: %pattern_type => constants.%pattern_type.48ecf1.2 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %require_complete => constants.%require_complete.86d019.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 89854c9721da..be1171d95b4a 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -978,7 +978,7 @@ auto FileContext::BuildVtable(const SemIR::Vtable& vtable, vfuncs.reserve(vtable_inst_block.size()); for (auto fn_decl_id : vtable_inst_block) { - auto [fn_decl, fn_id, fn_specific_id] = + auto [_1, _2, fn_id, fn_specific_id] = DecomposeVirtualFunction(sem_ir(), fn_decl_id, specific_id); vfuncs.push_back(llvm::ConstantExpr::getTrunc( diff --git a/toolchain/sem_ir/function.cpp b/toolchain/sem_ir/function.cpp index c76581b7628d..c073eb422f9b 100644 --- a/toolchain/sem_ir/function.cpp +++ b/toolchain/sem_ir/function.cpp @@ -70,8 +70,9 @@ auto DecomposeVirtualFunction(const File& sem_ir, InstId fn_decl_id, // Remap the base's vtable entry to the appropriate constant usable in // the context of the derived class (for the specific for the base // class, for instance). - fn_decl_id = sem_ir.constant_values().GetInstId( - GetConstantValueInSpecific(sem_ir, base_class_specific_id, fn_decl_id)); + auto fn_decl_const_id = + GetConstantValueInSpecific(sem_ir, base_class_specific_id, fn_decl_id); + fn_decl_id = sem_ir.constant_values().GetInstId(fn_decl_const_id); auto specific_id = SemIR::SpecificId::None; auto callee_id = fn_decl_id; if (auto specific_function = @@ -85,6 +86,7 @@ auto DecomposeVirtualFunction(const File& sem_ir, InstId fn_decl_id, sem_ir.types().GetAsInst(sem_ir.insts().Get(callee_id).type_id()); return {.fn_decl_id = fn_decl_id, + .fn_decl_const_id = fn_decl_const_id, .function_id = fn_type_inst.As().function_id, .specific_id = specific_id}; } diff --git a/toolchain/sem_ir/function.h b/toolchain/sem_ir/function.h index cb9921883247..2a1fa9fe7742 100644 --- a/toolchain/sem_ir/function.h +++ b/toolchain/sem_ir/function.h @@ -192,8 +192,10 @@ auto GetCalleeFunction(const File& sem_ir, InstId callee_id, -> CalleeFunction; struct DecomposedVirtualFunction { - // The underlying `FunctionDecl`. + // The canonical instruction from the `fn_decl_const_id`. InstId fn_decl_id; + // The constant for the underlying instruction. + ConstantId fn_decl_const_id; // The function. FunctionId function_id; // The specific for the function.