Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,213 @@
|
||||
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
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user