Files

214 lines
7.0 KiB
V

module transform
import v3.flat
import v3.types
// is_interface_type checks if a type name is a known interface.
fn (t &Transformer) is_interface_type(name string) bool {
return t.resolve_interface_type_name(name).len > 0
}
fn (t &Transformer) resolve_interface_type_name(name string) string {
if name.len == 0 || isnil(t.tc) {
return ''
}
clean := t.trim_pointer_type(t.normalize_type_alias(name))
if clean in t.tc.interface_names {
return clean
}
if !clean.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
&& t.cur_module != 'builtin' {
qname := '${t.cur_module}.${clean}'
if qname in t.tc.interface_names {
return qname
}
}
return ''
}
fn (mut t Transformer) transform_interface_value_for_type(id flat.NodeId, target_type string) ?flat.NodeId {
if int(id) < 0 || target_type.len == 0 || isnil(t.tc) {
return none
}
target_is_ptr := target_type.starts_with('&')
iface_name := t.resolve_interface_type_name(target_type)
if iface_name.len == 0 {
return none
}
// IError has bespoke handling (built via `error()`, fields accessed directly);
// do not route it through the generic interface boxing.
if iface_name.all_after_last('.') == 'IError' {
return none
}
source_type := t.node_type(id)
source_iface := t.resolve_interface_type_name(source_type)
if source_iface == iface_name {
return t.transform_expr(id)
}
if target_is_ptr && source_type.starts_with('&')
&& t.resolve_interface_type_name(source_type[1..]) == iface_name {
return t.transform_expr(id)
}
literal := t.make_interface_literal_from_expr(id, iface_name) or { return none }
if !target_is_ptr {
return literal
}
// A `&Interface` value must outlive the current scope (it is commonly returned
// or stored in a global, e.g. `default_rng = &PRNG(rng)`). Heap-allocate the
// interface box rather than taking the address of a local temporary, which
// would dangle.
tmp_name := t.new_temp('iface_box')
t.pending_stmts << t.make_decl_assign_typed(tmp_name, literal, iface_name)
addr := t.make_prefix(.amp, t.make_ident(tmp_name))
size := t.make_sizeof_type(iface_name)
dup := t.make_call_typed('memdup', arr2(addr, size), 'voidptr')
cast := t.make_cast(target_type, dup, target_type)
t.a.nodes[int(cast)].typ = target_type
return cast
}
fn (mut t Transformer) transform_global_amp_interface_cast(node flat.Node, target_type string) ?flat.NodeId {
if node.kind != .prefix || node.op != .amp || node.children_count != 1 {
return none
}
child_id := t.a.child(&node, 0)
child := t.a.nodes[int(child_id)]
if child.kind != .cast_expr || child.children_count == 0 {
return none
}
iface_name := t.resolve_interface_type_name(child.value)
if iface_name.len == 0 || iface_name.all_after_last('.') == 'IError' {
return none
}
old_pending := t.pending_stmts.clone()
t.pending_stmts.clear()
literal := t.make_interface_literal_from_expr(t.a.child(&child, 0), iface_name) or {
t.pending_stmts = old_pending
return none
}
has_pending := t.pending_stmts.len > 0
t.pending_stmts.clear()
t.pending_stmts = old_pending
if has_pending {
return none
}
start := t.a.children.len
t.a.children << literal
ptr_type := if target_type.len > 0 { target_type } else { '&${iface_name}' }
return t.a.add_node(flat.Node{
kind: .prefix
op: .amp
children_start: start
children_count: 1
pos: node.pos
value: node.value
typ: ptr_type
})
}
fn (mut t Transformer) make_interface_literal_from_expr(id flat.NodeId, iface_name string) ?flat.NodeId {
fields := t.tc.interface_fields[iface_name] or { []types.StructField{} }
source_type := t.node_type(id)
if source_type.len == 0 {
return none
}
source_expr := t.transform_expr(id)
source := t.stable_transformed_expr_for_reuse(source_expr, source_type, 'iface_src')
is_ptr := source_type.starts_with('&')
concrete_type := if is_ptr { source_type[1..] } else { source_type }
// `_object` is a pointer to the boxed concrete value; method dispatch reads it
// back and casts it to the concrete type. For pointer sources we store the
// pointer directly; for value sources we heap-copy so the box can outlive the
// source scope. The pointer is typed (`&Concrete`) so codegen can recover the
// concrete type and emit the matching `_typ` dispatch id.
object_expr := if is_ptr {
source
} else {
addr := t.make_prefix(.amp, source)
size := t.make_sizeof_type(concrete_type)
dup := t.make_call_typed('memdup', arr2(addr, size), 'voidptr')
t.make_cast('&${concrete_type}', dup, '&${concrete_type}')
}
field_base := if is_ptr {
base := t.make_prefix(.mul, source)
t.a.nodes[int(base)].typ = concrete_type
base
} else {
source
}
mut field_ids := []flat.NodeId{cap: fields.len + 1}
field_ids << t.make_sum_literal_field('_object', object_expr, '&${concrete_type}')
for field in fields {
field_type := t.normalize_type_alias(field.typ.name())
field_value := t.make_selector(field_base, field.name, field_type)
field_ids << t.make_sum_literal_field(field.name, field_value, field_type)
}
start := t.a.children.len
for field_id in field_ids {
t.a.children << field_id
}
return t.a.add_node(flat.Node{
kind: .struct_init
children_start: start
children_count: flat.child_count(field_ids.len)
value: iface_name
typ: iface_name
})
}
// transform_interface_cast transforms interface-to-concrete type casts.
// This is a hook for future interface dispatch lowering where interface
// values need to be unwrapped to their concrete types.
// Currently passes through unchanged.
fn (mut t Transformer) transform_interface_cast(id flat.NodeId, node flat.Node) flat.NodeId {
if node.children_count == 0 {
return id
}
mut new_children := []flat.NodeId{cap: int(node.children_count)}
for i in 0 .. node.children_count {
child_id := t.a.child(&node, i)
new_children << t.transform_expr(child_id)
}
start := t.a.children.len
for nc in new_children {
t.a.children << nc
}
return t.a.add_node(flat.Node{
kind: node.kind
op: node.op
children_start: start
children_count: node.children_count
pos: node.pos
value: node.value
typ: node.typ
})
}
// transform_interface_method_call transforms method calls on interface values.
// This is a hook for vtable dispatch lowering where `iface.method(args)`
// needs to be rewritten to indirect calls through the interface vtable.
// Currently passes through unchanged.
fn (mut t Transformer) transform_interface_method_call(id flat.NodeId, node flat.Node) flat.NodeId {
if node.children_count == 0 {
return id
}
mut new_children := []flat.NodeId{cap: int(node.children_count)}
for i in 0 .. node.children_count {
child_id := t.a.child(&node, i)
new_children << t.transform_expr(child_id)
}
start := t.a.children.len
for nc in new_children {
t.a.children << nc
}
return t.a.add_node(flat.Node{
kind: node.kind
op: node.op
children_start: start
children_count: node.children_count
pos: node.pos
value: node.value
typ: node.typ
})
}