vxx2 / vlib / v3 / transform / interface.v
217 lines · 209 sloc · 7.37 KB · 6c4d26f1f98c5464b012a375667ab345e462f7e0
Raw
1module transform
2
3import v3.flat
4import v3.types
5
6// is_interface_type checks if a type name is a known interface.
7fn (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.
12fn (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.
31fn (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.
73fn (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.
113fn (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.
167fn (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.
195fn (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