| 1 | module transform |
| 2 | |
| 3 | import v3.flat |
| 4 | import v3.types |
| 5 | |
| 6 | // is_interface_type checks if a type name is a known interface. |
| 7 | fn (t &Transformer) is_interface_type(name string) bool { |
| 8 | return t.resolve_interface_type_name(name).len > 0 |
| 9 | } |
| 10 | |
| 11 | // resolve_interface_type_name resolves resolve interface type name information for transform. |
| 12 | fn (t &Transformer) resolve_interface_type_name(name string) string { |
| 13 | if name.len == 0 || isnil(t.tc) { |
| 14 | return '' |
| 15 | } |
| 16 | clean := t.trim_pointer_type(t.normalize_type_alias(name)) |
| 17 | if clean in t.tc.interface_names { |
| 18 | return clean |
| 19 | } |
| 20 | if !clean.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main' |
| 21 | && t.cur_module != 'builtin' { |
| 22 | qname := '${t.cur_module}.${clean}' |
| 23 | if qname in t.tc.interface_names { |
| 24 | return qname |
| 25 | } |
| 26 | } |
| 27 | return '' |
| 28 | } |
| 29 | |
| 30 | // transform_interface_value_for_type supports transform_interface_value_for_type handling. |
| 31 | fn (mut t Transformer) transform_interface_value_for_type(id flat.NodeId, target_type string) ?flat.NodeId { |
| 32 | if int(id) < 0 || target_type.len == 0 || isnil(t.tc) { |
| 33 | return none |
| 34 | } |
| 35 | target_is_ptr := target_type.starts_with('&') |
| 36 | iface_name := t.resolve_interface_type_name(target_type) |
| 37 | if iface_name.len == 0 { |
| 38 | return none |
| 39 | } |
| 40 | // IError has bespoke handling (built via `error()`, fields accessed directly); |
| 41 | // do not route it through the generic interface boxing. |
| 42 | if iface_name.all_after_last('.') == 'IError' { |
| 43 | return none |
| 44 | } |
| 45 | source_type := t.node_type(id) |
| 46 | source_iface := t.resolve_interface_type_name(source_type) |
| 47 | if source_iface == iface_name { |
| 48 | return t.transform_expr(id) |
| 49 | } |
| 50 | if target_is_ptr && source_type.starts_with('&') |
| 51 | && t.resolve_interface_type_name(source_type[1..]) == iface_name { |
| 52 | return t.transform_expr(id) |
| 53 | } |
| 54 | literal := t.make_interface_literal_from_expr(id, iface_name) or { return none } |
| 55 | if !target_is_ptr { |
| 56 | return literal |
| 57 | } |
| 58 | // A `&Interface` value must outlive the current scope (it is commonly returned |
| 59 | // or stored in a global, e.g. `default_rng = &PRNG(rng)`). Heap-allocate the |
| 60 | // interface box rather than taking the address of a local temporary, which |
| 61 | // would dangle. |
| 62 | tmp_name := t.new_temp('iface_box') |
| 63 | t.pending_stmts << t.make_decl_assign_typed(tmp_name, literal, iface_name) |
| 64 | addr := t.make_prefix(.amp, t.make_ident(tmp_name)) |
| 65 | size := t.make_sizeof_type(iface_name) |
| 66 | dup := t.make_call_typed('memdup', arr2(addr, size), 'voidptr') |
| 67 | cast := t.make_cast(target_type, dup, target_type) |
| 68 | t.a.nodes[int(cast)].typ = target_type |
| 69 | return cast |
| 70 | } |
| 71 | |
| 72 | // transform_global_amp_interface_cast supports transform_global_amp_interface_cast handling. |
| 73 | fn (mut t Transformer) transform_global_amp_interface_cast(node flat.Node, target_type string) ?flat.NodeId { |
| 74 | if node.kind != .prefix || node.op != .amp || node.children_count != 1 { |
| 75 | return none |
| 76 | } |
| 77 | child_id := t.a.child(&node, 0) |
| 78 | child := t.a.nodes[int(child_id)] |
| 79 | if child.kind != .cast_expr || child.children_count == 0 { |
| 80 | return none |
| 81 | } |
| 82 | iface_name := t.resolve_interface_type_name(child.value) |
| 83 | if iface_name.len == 0 || iface_name.all_after_last('.') == 'IError' { |
| 84 | return none |
| 85 | } |
| 86 | old_pending := t.pending_stmts.clone() |
| 87 | t.pending_stmts.clear() |
| 88 | literal := t.make_interface_literal_from_expr(t.a.child(&child, 0), iface_name) or { |
| 89 | t.pending_stmts = old_pending |
| 90 | return none |
| 91 | } |
| 92 | has_pending := t.pending_stmts.len > 0 |
| 93 | t.pending_stmts.clear() |
| 94 | t.pending_stmts = old_pending |
| 95 | if has_pending { |
| 96 | return none |
| 97 | } |
| 98 | start := t.a.children.len |
| 99 | t.a.children << literal |
| 100 | ptr_type := if target_type.len > 0 { target_type } else { '&${iface_name}' } |
| 101 | return t.a.add_node(flat.Node{ |
| 102 | kind: .prefix |
| 103 | op: .amp |
| 104 | children_start: start |
| 105 | children_count: 1 |
| 106 | pos: node.pos |
| 107 | value: node.value |
| 108 | typ: ptr_type |
| 109 | }) |
| 110 | } |
| 111 | |
| 112 | // make_interface_literal_from_expr converts make interface literal from expr data for transform. |
| 113 | fn (mut t Transformer) make_interface_literal_from_expr(id flat.NodeId, iface_name string) ?flat.NodeId { |
| 114 | fields := t.tc.interface_fields[iface_name] or { []types.StructField{} } |
| 115 | source_type := t.node_type(id) |
| 116 | if source_type.len == 0 { |
| 117 | return none |
| 118 | } |
| 119 | source_expr := t.transform_expr(id) |
| 120 | source := t.stable_transformed_expr_for_reuse(source_expr, source_type, 'iface_src') |
| 121 | is_ptr := source_type.starts_with('&') |
| 122 | concrete_type := if is_ptr { source_type[1..] } else { source_type } |
| 123 | // `_object` is a pointer to the boxed concrete value; method dispatch reads it |
| 124 | // back and casts it to the concrete type. For pointer sources we store the |
| 125 | // pointer directly; for value sources we heap-copy so the box can outlive the |
| 126 | // source scope. The pointer is typed (`&Concrete`) so codegen can recover the |
| 127 | // concrete type and emit the matching `_typ` dispatch id. |
| 128 | object_expr := if is_ptr { |
| 129 | source |
| 130 | } else { |
| 131 | addr := t.make_prefix(.amp, source) |
| 132 | size := t.make_sizeof_type(concrete_type) |
| 133 | dup := t.make_call_typed('memdup', arr2(addr, size), 'voidptr') |
| 134 | t.make_cast('&${concrete_type}', dup, '&${concrete_type}') |
| 135 | } |
| 136 | field_base := if is_ptr { |
| 137 | base := t.make_prefix(.mul, source) |
| 138 | t.a.nodes[int(base)].typ = concrete_type |
| 139 | base |
| 140 | } else { |
| 141 | source |
| 142 | } |
| 143 | mut field_ids := []flat.NodeId{cap: fields.len + 1} |
| 144 | field_ids << t.make_sum_literal_field('_object', object_expr, '&${concrete_type}') |
| 145 | for field in fields { |
| 146 | field_type := t.normalize_type_alias(field.typ.name()) |
| 147 | field_value := t.make_selector(field_base, field.name, field_type) |
| 148 | field_ids << t.make_sum_literal_field(field.name, field_value, field_type) |
| 149 | } |
| 150 | start := t.a.children.len |
| 151 | for field_id in field_ids { |
| 152 | t.a.children << field_id |
| 153 | } |
| 154 | return t.a.add_node(flat.Node{ |
| 155 | kind: .struct_init |
| 156 | children_start: start |
| 157 | children_count: flat.child_count(field_ids.len) |
| 158 | value: iface_name |
| 159 | typ: iface_name |
| 160 | }) |
| 161 | } |
| 162 | |
| 163 | // transform_interface_cast transforms interface-to-concrete type casts. |
| 164 | // This is a hook for future interface dispatch lowering where interface |
| 165 | // values need to be unwrapped to their concrete types. |
| 166 | // Currently passes through unchanged. |
| 167 | fn (mut t Transformer) transform_interface_cast(id flat.NodeId, node flat.Node) flat.NodeId { |
| 168 | if node.children_count == 0 { |
| 169 | return id |
| 170 | } |
| 171 | mut new_children := []flat.NodeId{cap: int(node.children_count)} |
| 172 | for i in 0 .. node.children_count { |
| 173 | child_id := t.a.child(&node, i) |
| 174 | new_children << t.transform_expr(child_id) |
| 175 | } |
| 176 | start := t.a.children.len |
| 177 | for nc in new_children { |
| 178 | t.a.children << nc |
| 179 | } |
| 180 | return t.a.add_node(flat.Node{ |
| 181 | kind: node.kind |
| 182 | op: node.op |
| 183 | children_start: start |
| 184 | children_count: node.children_count |
| 185 | pos: node.pos |
| 186 | value: node.value |
| 187 | typ: node.typ |
| 188 | }) |
| 189 | } |
| 190 | |
| 191 | // transform_interface_method_call transforms method calls on interface values. |
| 192 | // This is a hook for vtable dispatch lowering where `iface.method(args)` |
| 193 | // needs to be rewritten to indirect calls through the interface vtable. |
| 194 | // Currently passes through unchanged. |
| 195 | fn (mut t Transformer) transform_interface_method_call(id flat.NodeId, node flat.Node) flat.NodeId { |
| 196 | if node.children_count == 0 { |
| 197 | return id |
| 198 | } |
| 199 | mut new_children := []flat.NodeId{cap: int(node.children_count)} |
| 200 | for i in 0 .. node.children_count { |
| 201 | child_id := t.a.child(&node, i) |
| 202 | new_children << t.transform_expr(child_id) |
| 203 | } |
| 204 | start := t.a.children.len |
| 205 | for nc in new_children { |
| 206 | t.a.children << nc |
| 207 | } |
| 208 | return t.a.add_node(flat.Node{ |
| 209 | kind: node.kind |
| 210 | op: node.op |
| 211 | children_start: start |
| 212 | children_count: node.children_count |
| 213 | pos: node.pos |
| 214 | value: node.value |
| 215 | typ: node.typ |
| 216 | }) |
| 217 | } |
| 218 | |