v4 / vlib / v3 / gen / c / interface.v
556 lines · 529 sloc · 15.98 KB · 288feee4702b35beadb4037d02b96b5c8674156b
Raw
1module c
2
3import v3.types
4import v3.flat
5
6// emit_sum_type emits emit sum type output for c.
7fn (mut g FlatGen) emit_sum_type(name string) {
8 variants := g.tc.sum_types[name]
9 g.writeln('struct ${c_name(name)} {')
10 g.writeln('\tint typ;')
11 g.writeln('\tunion {')
12 for v in variants {
13 ct := g.tc.c_type(g.tc.parse_type(v))
14 field := g.sum_field_name(v)
15 g.writeln('\t\t${ct}* ${field};')
16 }
17 g.writeln('\t};')
18 g.writeln('};')
19 g.writeln('')
20}
21
22// sum_type_contains_struct reports whether sum type contains struct applies in c.
23fn (g &FlatGen) sum_type_contains_struct(sum_name string, struct_name string) bool {
24 if sum_name in g.tc.sum_types {
25 for v in g.tc.sum_types[sum_name] {
26 if v == struct_name {
27 return true
28 }
29 }
30 }
31 return false
32}
33
34// variant_references_sum supports variant references sum handling for FlatGen.
35fn (g &FlatGen) variant_references_sum(variant string, sum_name string) bool {
36 _ = variant
37 _ = sum_name
38 return true
39}
40
41// variant_refs_sum_inner supports variant refs sum inner handling for FlatGen.
42fn (g &FlatGen) variant_refs_sum_inner(variant string, sum_name string, mut visited map[string]bool) bool {
43 normalized_variant := g.normalize_variant_name(variant)
44 if normalized_variant == sum_name
45 || normalized_variant.all_after_last('.') == sum_name.all_after_last('.') {
46 return true
47 }
48 if normalized_variant in visited {
49 return false
50 }
51 visited[normalized_variant] = true
52 mut lookup := normalized_variant
53 if lookup !in g.tc.structs && !lookup.contains('.') && sum_name.contains('.') {
54 qualified := '${sum_name.all_before_last('.')}.${lookup}'
55 if qualified in g.tc.structs {
56 lookup = qualified
57 }
58 }
59 if lookup !in g.tc.structs && !lookup.contains('.') {
60 for struct_name, _ in g.tc.structs {
61 if struct_name.all_after_last('.') == lookup {
62 lookup = struct_name
63 break
64 }
65 }
66 }
67 if lookup in g.tc.structs {
68 for f in g.tc.structs[lookup] {
69 if g.type_references_sum(f.typ, sum_name, mut visited) {
70 return true
71 }
72 }
73 }
74 return false
75}
76
77// normalize_variant_name transforms normalize variant name data for c.
78fn (g &FlatGen) normalize_variant_name(name string) string {
79 _ = g
80 mut res := name
81 if res.starts_with('&') {
82 res = res[1..]
83 }
84 if res.starts_with('ptr') && res.len > 3 {
85 res = res[3..]
86 }
87 if res.contains('__') && !res.contains('.') {
88 res = res.replace('__', '.')
89 }
90 return res
91}
92
93// type_references_sum returns type references sum data for FlatGen.
94fn (g &FlatGen) type_references_sum(typ types.Type, sum_name string, mut visited map[string]bool) bool {
95 resolved_sum := g.resolve_sum_name(sum_name)
96 clean := types.unwrap_pointer(typ)
97 if clean is types.Struct && g.resolve_sum_name(clean.name) == resolved_sum {
98 return true
99 }
100 if clean is types.SumType && g.resolve_sum_name(clean.name) == resolved_sum {
101 return true
102 }
103 if clean is types.SumType {
104 return true
105 }
106 if clean is types.Struct {
107 if g.variant_refs_sum_inner(clean.name, resolved_sum, mut visited) {
108 return true
109 }
110 }
111 if clean is types.Array {
112 return g.type_references_sum(clean.elem_type, resolved_sum, mut visited)
113 }
114 return false
115}
116
117// resolve_sum_name resolves resolve sum name information for c.
118fn (g &FlatGen) resolve_sum_name(sum_name string) string {
119 if sum_name in g.tc.sum_types {
120 return sum_name
121 }
122 for name, _ in g.tc.sum_types {
123 if name.all_after_last('.') == sum_name {
124 return name
125 }
126 }
127 return sum_name
128}
129
130// resolve_variant resolves resolve variant information for c.
131fn (g &FlatGen) resolve_variant(sum_name string, variant string) string {
132 resolved_sum := g.resolve_sum_name(sum_name)
133 normalized_variant := g.normalize_variant_name(variant)
134 if resolved_sum in g.tc.sum_types {
135 for v in g.tc.sum_types[resolved_sum] {
136 if v == normalized_variant {
137 return normalized_variant
138 }
139 }
140 for v in g.tc.sum_types[resolved_sum] {
141 if v.all_after_last('.') == normalized_variant {
142 return v
143 }
144 }
145 }
146 return normalized_variant
147}
148
149// sum_field_name supports sum field name handling for FlatGen.
150fn (g &FlatGen) sum_field_name(variant string) string {
151 if variant.starts_with('&') {
152 return g.sum_field_name(variant[1..])
153 }
154 if variant.starts_with('ptr') && variant.len > 3 && variant[3..].contains('.') {
155 return g.sum_field_name(variant[3..])
156 }
157 if variant.starts_with('ptr') && variant.len > 3 && variant[3..].contains('__') {
158 return g.sum_field_name(variant[3..].replace('__', '.'))
159 }
160 if variant.starts_with('[]') {
161 return '_Array_${c_name(variant[2..])}'
162 }
163 if variant.starts_with('map[') {
164 return '_Map_${c_name(variant[4..].replace(']', '_'))}'
165 }
166 return match variant {
167 'int' { '_int' }
168 'i8' { '_i8' }
169 'i16' { '_i16' }
170 'i64' { '_i64' }
171 'u8', 'byte' { '_u8' }
172 'u16' { '_u16' }
173 'u32' { '_u32' }
174 'u64' { '_u64' }
175 'f32' { '_f32' }
176 'f64' { '_f64' }
177 'bool' { '_bool' }
178 'string' { '_string' }
179 else { c_name(variant) }
180 }
181}
182
183// register_interface_strings updates register interface strings state for c.
184fn (mut g FlatGen) register_interface_strings() {
185 for iface_name, methods in g.interfaces {
186 cn := c_name(iface_name)
187 for method in methods {
188 g.intern_string('interface method ${cn}.${method} not implemented')
189 }
190 }
191}
192
193// collect_interface_impls discovers, for every interface, the concrete struct
194// types that implement it (structural typing), and assigns each a stable 1-based
195// type id. The id is stored in the boxed interface value's `_typ` field and is
196// what the generated method-dispatch switch matches on.
197fn (mut g FlatGen) collect_interface_impls() {
198 mut iface_names := []string{}
199 for name, _ in g.interfaces {
200 iface_names << name
201 }
202 iface_names.sort()
203 mut struct_names := []string{}
204 for name, _ in g.tc.structs {
205 struct_names << name
206 }
207 struct_names.sort()
208 for iface in iface_names {
209 if c_name(iface) == 'IError' {
210 continue
211 }
212 mut impls := []string{}
213 for concrete in struct_names {
214 if g.tc.named_type_implements_interface(concrete, iface) {
215 impls << concrete
216 }
217 }
218 g.iface_impls[iface] = impls
219 for idx, concrete in impls {
220 g.iface_type_ids['${iface}::${concrete}'] = idx + 1
221 }
222 }
223}
224
225// iface_type_id returns the 1-based dispatch id assigned to `concrete` for
226// interface `iface`, or 0 if `concrete` does not implement `iface`.
227fn (g &FlatGen) iface_type_id(iface string, concrete string) int {
228 return g.iface_type_ids['${iface}::${concrete}'] or { 0 }
229}
230
231fn (mut g FlatGen) gen_interface_value_expr(id flat.NodeId, expected types.Type) bool {
232 iface_type := if expected is types.Alias { expected.base_type } else { expected }
233 if iface_type !is types.Interface {
234 return false
235 }
236 iface := iface_type as types.Interface
237 node := g.a.nodes[int(id)]
238 mut actual := g.usable_expr_type(id)
239 if node.kind == .ident {
240 if param_type := g.current_param_type(node.value) {
241 actual = param_type
242 }
243 }
244 actual_clean := if actual is types.Pointer { actual.base_type } else { actual }
245 actual_base := if actual_clean is types.Alias { actual_clean.base_type } else { actual_clean }
246 if actual_base is types.Interface {
247 return false
248 }
249 concrete_name := actual_base.name()
250 if concrete_name.len == 0 {
251 return false
252 }
253 type_id := g.iface_type_id(iface.name, concrete_name)
254 ct := g.tc.c_type(iface)
255 fields := g.tc.interface_fields[iface.name] or { []types.StructField{} }
256 concrete_ct := g.tc.c_type(actual_base)
257 if fields.len > 0 {
258 tmp := g.tmp_count
259 g.tmp_count++
260 if actual is types.Pointer {
261 g.write('({ ${concrete_ct}* _iface${tmp} = ')
262 g.gen_expr(id)
263 g.write('; (${ct}){._typ = ${type_id}, ._object = _iface${tmp}')
264 for field in fields {
265 g.write(', .${c_name(field.name)} = _iface${tmp}->${c_name(field.name)}')
266 }
267 g.write('}; })')
268 } else {
269 g.write('({ ${concrete_ct} _iface${tmp} = ')
270 g.gen_expr(id)
271 g.write('; (${ct}){._typ = ${type_id}, ._object = memdup(&_iface${tmp}, sizeof(${concrete_ct}))')
272 for field in fields {
273 g.write(', .${c_name(field.name)} = _iface${tmp}.${c_name(field.name)}')
274 }
275 g.write('}; })')
276 }
277 return true
278 }
279 g.write('(${ct}){._typ = ${type_id}, ._object = ')
280 if actual is types.Pointer {
281 g.gen_expr(id)
282 } else if node.kind in [.ident, .selector, .index] {
283 g.write('memdup(&')
284 g.gen_expr(id)
285 g.write(', sizeof(${concrete_ct}))')
286 } else {
287 g.write('memdup((${concrete_ct}[]){')
288 g.gen_expr(id)
289 g.write('}, sizeof(${concrete_ct}))')
290 }
291 g.write('}')
292 return true
293}
294
295// is_interface_type_name reports whether is interface type name applies in c.
296fn (g &FlatGen) is_interface_type_name(name string) bool {
297 return name in g.interfaces || g.tc.qualify_name(name) in g.interfaces
298}
299
300// has_ierror_interface reports whether has ierror interface applies in c.
301fn (g &FlatGen) has_ierror_interface() bool {
302 for name, _ in g.interfaces {
303 if c_name(name) == 'IError' {
304 return true
305 }
306 }
307 return false
308}
309
310// interface_init_typ_id computes the `_typ` dispatch id for a boxed interface
311// literal by recovering the concrete type from its `_object` field.
312fn (g &FlatGen) interface_init_typ_id(node flat.Node) ?int {
313 iface := if node.value in g.interfaces {
314 node.value
315 } else {
316 g.tc.qualify_name(node.value)
317 }
318 for i in 0 .. node.children_count {
319 field := g.a.child_node(&node, i)
320 if field.kind == .field_init && field.value == '_object' && field.children_count > 0 {
321 obj_type := g.tc.resolve_type(g.a.child(field, 0))
322 concrete := types.unwrap_pointer(obj_type)
323 id := g.iface_type_id(iface, concrete.name())
324 if id != 0 {
325 return id
326 }
327 return none
328 }
329 }
330 return none
331}
332
333// interface_method_stubs emits a dispatch function for every abstract interface
334// method: it switches on the boxed value's `_typ` and forwards to the concrete
335// implementation, passing `_object` as the receiver. Interfaces with no known
336// implementers (and the special builtin `IError`) fall back to a panic stub.
337fn (mut g FlatGen) interface_method_stubs() {
338 for iface_name, methods in g.interfaces {
339 cn := c_name(iface_name)
340 for method in methods {
341 if !g.should_emit_interface_dispatch(iface_name, method) {
342 continue
343 }
344 g.gen_interface_dispatch(iface_name, cn, method)
345 }
346 }
347 if g.interfaces.len > 0 {
348 g.writeln('')
349 }
350}
351
352fn (g &FlatGen) should_emit_interface_dispatch(iface_name string, method string) bool {
353 if !g.has_used_fn_filter() {
354 return true
355 }
356 name := '${iface_name}.${method}'
357 if g.used_interface_dispatch_key(name) {
358 return true
359 }
360 if decl_key := g.interface_method_signature_key(iface_name, method) {
361 if decl_key != name && g.used_interface_dispatch_key(decl_key) {
362 return true
363 }
364 decl_short_name := '${decl_key.all_before_last('.').all_after_last('.')}.${method}'
365 if decl_short_name != decl_key && g.interface_dispatch_short_name_allowed(iface_name)
366 && g.used_interface_dispatch_key(decl_short_name) {
367 return true
368 }
369 }
370 short_name := '${iface_name.all_after_last('.')}.${method}'
371 return short_name != name && g.interface_dispatch_short_name_allowed(iface_name)
372 && g.used_interface_dispatch_key(short_name)
373}
374
375fn (g &FlatGen) used_interface_dispatch_key(name string) bool {
376 return g.used_fn_contains(name) || g.used_fn_contains(c_name(name))
377}
378
379fn (g &FlatGen) interface_dispatch_short_name_allowed(iface_name string) bool {
380 return !iface_name.contains('.')
381}
382
383// gen_interface_dispatch emits interface dispatch output for c.
384fn (mut g FlatGen) gen_interface_dispatch(iface_name string, cn string, method string) {
385 sid := g.intern_string('interface method ${cn}.${method} not implemented')
386 mname := '${iface_name}.${method}'
387 decl_key := g.interface_method_signature_key(iface_name, method) or { mname }
388 impls := g.iface_impls[iface_name] or { []string{} }
389 if cn == 'IError' {
390 ret_ct := if method == 'code' { 'int' } else { 'string' }
391 g.writeln('${ret_ct} ${cn}__${method}(${cn}* i) {')
392 match method {
393 'msg' {
394 g.writeln('\treturn i->message;')
395 }
396 'code' {
397 g.writeln('\treturn i->code;')
398 }
399 else {
400 g.writeln('\tpanic(_str_${sid});')
401 g.writeln('\treturn (${ret_ct}){0};')
402 }
403 }
404
405 g.writeln('}')
406 return
407 }
408 // Interface-declared method signatures store named params unreliably (a named
409 // param like `node &ast.Node` can be split into two type-only params). The
410 // concrete implementer's method is a real fn_decl with a correctly parsed
411 // signature, so derive the dispatch parameter types from the first implementer
412 // that has the method. The receiver convention is resolved per implementer.
413 mut sig_key := ''
414 for concrete in impls {
415 ck := '${concrete}.${method}'
416 if ck in g.tc.fn_param_types {
417 sig_key = ck
418 break
419 }
420 }
421 ret_type := g.tc.fn_ret_types[decl_key] or {
422 if sig_key.len > 0 {
423 g.tc.fn_ret_types[sig_key] or { types.Type(types.void_) }
424 } else {
425 types.Type(types.void_)
426 }
427 }
428 // Use the ABI return type, not the bare value type: a fixed-array return is its `_v_ret_*`
429 // wrapper struct (a C function cannot return an array by value), matching what the concrete
430 // implementer's method returns and what the call site unwraps.
431 ret_ct := g.fn_return_type_name(ret_type)
432 mut sig_params := if sig_key.len > 0 {
433 g.tc.fn_param_types[sig_key] or { []types.Type{} }
434 } else {
435 g.tc.fn_param_types[decl_key] or { []types.Type{} }
436 }
437 mut arg_names := []string{}
438 g.write('${ret_ct} ${cn}__${method}(${cn}* i')
439 for pi := 1; pi < sig_params.len; pi++ {
440 pt := sig_params[pi]
441 pct := if pt is types.OptionType || pt is types.ResultType {
442 g.optional_type_name(pt)
443 } else {
444 g.tc.c_type(pt)
445 }
446 an := '_a${pi - 1}'
447 arg_names << an
448 g.write(', ${pct} ${an}')
449 }
450 g.writeln(') {')
451 if impls.len > 0 {
452 g.writeln('\tswitch (i->_typ) {')
453 for concrete in impls {
454 id := g.iface_type_id(iface_name, concrete)
455 concrete_key := '${concrete}.${method}'
456 if id == 0 || concrete_key !in g.tc.fn_param_types
457 || !g.interface_dispatch_target_is_emitted(concrete_key) {
458 continue
459 }
460 concrete_params := g.tc.fn_param_types[concrete_key] or { []types.Type{} }
461 recv_is_ptr := concrete_params.len > 0 && concrete_params[0] is types.Pointer
462 cct := g.tc.c_type(g.tc.parse_type(concrete))
463 recv := if recv_is_ptr {
464 '(${cct}*)i->_object'
465 } else {
466 '*(${cct}*)i->_object'
467 }
468 g.write('\t\tcase ${id}: ')
469 mut call := '${c_name(concrete_key)}(${recv}'
470 for an in arg_names {
471 call += ', ${an}'
472 }
473 call += ')'
474 if ret_ct == 'void' {
475 g.writeln('${call}; return;')
476 } else {
477 g.writeln('return ${call};')
478 }
479 }
480 g.writeln('\t\tdefault: break;')
481 g.writeln('\t}')
482 }
483 g.writeln('\tpanic(_str_${sid});')
484 if ret_ct != 'void' {
485 g.writeln('\treturn (${ret_ct}){0};')
486 }
487 g.writeln('}')
488}
489
490fn (g &FlatGen) interface_method_signature_key(iface_name string, method string) ?string {
491 key := '${iface_name}.${method}'
492 if key in g.tc.fn_ret_types || key in g.tc.fn_param_types {
493 return key
494 }
495 for embed in g.tc.interface_embeds[iface_name] or { []string{} } {
496 if found := g.interface_method_signature_key(embed, method) {
497 return found
498 }
499 }
500 return none
501}
502
503fn (g &FlatGen) interface_dispatch_target_is_emitted(concrete_key string) bool {
504 if !g.has_used_fn_filter() {
505 return true
506 }
507 if g.used_interface_dispatch_key(concrete_key) {
508 return true
509 }
510 receiver_name := concrete_key.all_before_last('.')
511 if receiver_name.contains('.') {
512 return false
513 }
514 method := concrete_key.all_after_last('.')
515 short_key := '${receiver_name.all_after_last('.')}.${method}'
516 return short_key != concrete_key
517 && g.interface_dispatch_target_short_name_is_unambiguous(short_key, method)
518 && g.used_interface_dispatch_key(short_key)
519}
520
521fn (g &FlatGen) interface_dispatch_target_short_name_is_unambiguous(short_name string, method string) bool {
522 mut seen := map[string]bool{}
523 mut matches := 0
524 for _, impls in g.iface_impls {
525 for concrete in impls {
526 if seen[concrete] {
527 continue
528 }
529 seen[concrete] = true
530 if '${concrete.all_after_last('.')}.${method}' == short_name {
531 matches++
532 if matches > 1 {
533 return false
534 }
535 }
536 }
537 }
538 return matches == 1
539}
540
541// sum_type_index supports sum type index handling for FlatGen.
542fn (g &FlatGen) sum_type_index(sum_name string, variant string) int {
543 if sum_name in g.tc.sum_types {
544 for i, v in g.tc.sum_types[sum_name] {
545 if v == variant {
546 return i + 1
547 }
548 }
549 for i, v in g.tc.sum_types[sum_name] {
550 if v.all_after_last('.') == variant {
551 return i + 1
552 }
553 }
554 }
555 return 0
556}
557