Add a vtableDecl inst and use that in classes instead of VtablePtr (#5945)

This addresses/avoids the duplicate import of vtables.

I went through a few iterations/etc along the way and left them in the
commit
history for the PR in case any of them are useful to illustrate how I
got here,
or worth revisiting.

Essentially I ended up with a circularity in importing - importing the
class
imported the vtable_decl which imported the virtual functions - and then
pending
specifics of the virtual functions needed the self specific of the
enclosing
class which wasn't ready yet.

Adding ImportRef to the vtable_decl to break the cycle caused me trouble
when
naming the vtable_decl instructions - so I tried making the functions in
the
vtable unloaded ImportRefs instead. That worked, but meant that
importing a
class still was doing O(number of vtable entries) even if the vtable
wasn't
used.

So I revisited the lazy vtable_decl - figured out how to make the naming
work
(when building the vtable_ptr, even though the vtable_decl doesn't have
to be
loaded for the vtable_ptr, I force it to be loaded anyway, to load the
vtable so
it's usable by lowering, etc). And then I could go back to the old
non-lazy
loaded vtable entries (using some loaded ImportRefs in the cases where
we needed
them/had already adopted them).

Then thinking about the VtablePtr instruction, went back/forth on
exactly what
it needed - went from VtablePtr's member being a VtableDecl InstId, to a
ClassId, then back to a VtableId as it was before this patch.

Naming the instructions has one oddity, that the VtableDecl and
VtablePtr
instructions seem to need to add the pending name for the VtableId -
despite not
using the VtableId in their own name - should the inst namer be doing
this work
for parameters of instructions rather than requiring the inst to do it
deliberately? (or am I holding it wrong in some way?)

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
David Blaikie
2025-08-15 18:36:54 +00:00
committed by GitHub
co-authored by Richard Smith
parent 7727c62880
commit 3f9fc633fe
12 changed files with 292 additions and 313 deletions
+31 -34
View File
@@ -165,7 +165,7 @@ static auto ConvertAggregateElement(
ConversionTarget::Kind kind, SemIR::InstId target_id,
SemIR::TypeInstId target_elem_type_inst, PendingBlock* target_block,
size_t src_field_index, size_t target_field_index,
SemIR::InstId vtable_ptr_inst_id = SemIR::InstId::None) -> SemIR::InstId {
SemIR::ClassType* vtable_class_type = nullptr) -> SemIR::InstId {
auto src_elem_type =
context.types().GetTypeIdForTypeInstId(src_elem_type_inst);
auto target_elem_type =
@@ -193,7 +193,7 @@ static auto ConvertAggregateElement(
target.init_id = MakeElementAccessInst<TargetAccessInstT>(
context, loc_id, target_id, target_elem_type, *target_block,
target_field_index);
return Convert(context, loc_id, src_elem_id, target, vtable_ptr_inst_id);
return Convert(context, loc_id, src_elem_id, target, vtable_class_type);
}
// Performs a conversion from a tuple to an array type. This function only
@@ -390,7 +390,7 @@ template <typename TargetAccessInstT>
static auto ConvertStructToStructOrClass(
Context& context, SemIR::StructType src_type, SemIR::StructType dest_type,
SemIR::InstId value_id, ConversionTarget target,
SemIR::InstId vtable_ptr_inst_id = SemIR::InstId::None) -> SemIR::InstId {
SemIR::ClassType* vtable_class_type = nullptr) -> SemIR::InstId {
static_assert(std::is_same_v<SemIR::ClassElementAccess, TargetAccessInstT> ||
std::is_same_v<SemIR::StructAccess, TargetAccessInstT>);
constexpr bool ToClass =
@@ -480,11 +480,22 @@ static auto ConvertStructToStructOrClass(
{.type_id = vptr_type_id,
.base_id = target.init_id,
.index = SemIR::ElementIndex(i)});
auto init_id =
AddInst<SemIR::InitializeFrom>(context, value_loc_id,
{.type_id = vptr_type_id,
.src_id = vtable_ptr_inst_id,
.dest_id = dest_id});
auto vtable_decl_id =
context.classes().Get(vtable_class_type->class_id).vtable_decl_id;
LoadImportRef(context, vtable_decl_id);
auto canonical_vtable_decl_id =
context.constant_values().GetConstantInstId(vtable_decl_id);
auto vtable_ptr_id = AddInst<SemIR::VtablePtr>(
context, value_loc_id,
{.type_id = GetPointerType(context, SemIR::VtableType::TypeInstId),
.vtable_id = context.insts()
.GetAs<SemIR::VtableDecl>(canonical_vtable_decl_id)
.vtable_id,
.specific_id = vtable_class_type->specific_id});
auto init_id = AddInst<SemIR::InitializeFrom>(context, value_loc_id,
{.type_id = vptr_type_id,
.src_id = vtable_ptr_id,
.dest_id = dest_id});
new_block.Set(i, init_id);
continue;
}
@@ -526,7 +537,7 @@ static auto ConvertStructToStructOrClass(
context, value_loc_id, value_id, src_field.type_inst_id,
literal_elems, inner_kind, target.init_id, dest_field.type_inst_id,
target.init_block, src_field_index,
src_field_index + dest_vptr_offset, vtable_ptr_inst_id);
src_field_index + dest_vptr_offset, vtable_class_type);
if (init_id == SemIR::ErrorInst::InstId) {
return SemIR::ErrorInst::InstId;
}
@@ -568,10 +579,11 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type,
// Performs a conversion from a struct to a class type. This function only
// converts the type, and does not perform a final conversion to the requested
// expression category.
static auto ConvertStructToClass(
Context& context, SemIR::StructType src_type, SemIR::ClassType dest_type,
SemIR::InstId value_id, ConversionTarget target,
SemIR::InstId dest_vtable_ptr_inst_id = SemIR::InstId::None)
static auto ConvertStructToClass(Context& context, SemIR::StructType src_type,
SemIR::ClassType dest_type,
SemIR::InstId value_id,
ConversionTarget target,
SemIR::ClassType* vtable_class_type)
-> SemIR::InstId {
PendingBlock target_block(&context);
auto& dest_class_info = context.classes().Get(dest_type.class_id);
@@ -598,24 +610,9 @@ static auto ConvertStructToClass(
SemIR::LocId(value_id), {.type_id = target.type_id});
}
if (!dest_vtable_ptr_inst_id.has_value()) {
dest_vtable_ptr_inst_id = dest_class_info.vtable_ptr_id;
if (dest_type.specific_id.has_value() &&
dest_vtable_ptr_inst_id.has_value()) {
LoadImportRef(context, dest_vtable_ptr_inst_id);
dest_vtable_ptr_inst_id = context.constant_values().GetInstId(
GetConstantValueInSpecific(context.sem_ir(), dest_type.specific_id,
dest_vtable_ptr_inst_id));
}
}
if (dest_vtable_ptr_inst_id.has_value()) {
LoadImportRef(context, dest_vtable_ptr_inst_id);
}
auto result_id = ConvertStructToStructOrClass<SemIR::ClassElementAccess>(
context, src_type, dest_struct_type, value_id, target,
dest_vtable_ptr_inst_id);
vtable_class_type ? vtable_class_type : &dest_type);
if (need_temporary) {
target_block.InsertHere();
@@ -799,8 +796,8 @@ static auto DiagnoseConversionFailureToConstraintValue(
static auto PerformBuiltinConversion(
Context& context, SemIR::LocId loc_id, SemIR::InstId value_id,
ConversionTarget target,
SemIR::InstId vtable_ptr_inst_id = SemIR::InstId::None) -> SemIR::InstId {
ConversionTarget target, SemIR::ClassType* vtable_class_type = nullptr)
-> SemIR::InstId {
auto& sem_ir = context.sem_ir();
auto value = sem_ir.insts().Get(value_id);
auto value_type_id = value.type_id();
@@ -974,7 +971,7 @@ static auto PerformBuiltinConversion(
.adapt_id.has_value()) {
return ConvertStructToClass(context, *src_struct_type,
*target_class_type, value_id, target,
vtable_ptr_inst_id);
vtable_class_type);
}
}
@@ -1201,7 +1198,7 @@ auto PerformAction(Context& context, SemIR::LocId loc_id,
}
auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
ConversionTarget target, SemIR::InstId vtable_ptr_inst_id)
ConversionTarget target, SemIR::ClassType* vtable_class_type)
-> SemIR::InstId {
auto& sem_ir = context.sem_ir();
auto orig_expr_id = expr_id;
@@ -1266,7 +1263,7 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
// Check whether any builtin conversion applies.
expr_id = PerformBuiltinConversion(context, loc_id, expr_id, target,
vtable_ptr_inst_id);
vtable_class_type);
if (expr_id == SemIR::ErrorInst::InstId) {
return expr_id;
}