v / vlib / v2 / gen / cleanc / interface.v
1243 lines · 1189 sloc · 35.41 KB · 164b30309e6337b95ec2baacacd0f101fafd3d97
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4
5module cleanc
6
7import v2.ast
8import v2.types
9
10struct InterfaceMethodInfo {
11 name string // method name (e.g., "draw")
12 cast_signature string // function pointer cast (e.g., "int (*)(void*)")
13 ret_type string
14 param_types []string
15}
16
17struct InterfaceWrapperSpec {
18 fn_name string
19 concrete_type string
20 method InterfaceMethodInfo
21}
22
23struct EmbeddedMethodResolve {
24 fn_name string
25 owner_path string
26 receiver_type string
27}
28
29struct InterfaceDataFieldInfo {
30 name string
31 c_type string
32}
33
34fn (mut g Gen) interface_method_info_from_ast(name string, fn_type ast.FnType) InterfaceMethodInfo {
35 mut ret := 'void'
36 if fn_type.return_type !is ast.EmptyExpr {
37 ret = g.expr_type_to_c(fn_type.return_type)
38 }
39 mut param_types := []string{}
40 mut cast_sig := '${ret} (*)(void*'
41 for param in fn_type.params {
42 mut t := g.expr_type_to_c(param.typ)
43 if param.is_mut && !t.ends_with('*') {
44 t += '*'
45 }
46 cast_sig += ', ${t}'
47 param_types << t
48 }
49 cast_sig += ')'
50 return InterfaceMethodInfo{
51 name: name
52 cast_signature: cast_sig
53 ret_type: ret
54 param_types: param_types
55 }
56}
57
58fn (mut g Gen) interface_method_info_from_raw(name string, raw types.Type) ?InterfaceMethodInfo {
59 if !type_has_valid_data(raw) {
60 return none
61 }
62 match raw {
63 types.FnType {
64 mut ret := 'void'
65 if ret_type := raw.get_return_type() {
66 ret = g.types_type_to_c(ret_type)
67 }
68 mut param_types := []string{}
69 for param_type in raw.get_param_types() {
70 param_types << g.types_type_to_c(param_type)
71 }
72 mut cast_sig := '${ret} (*)(void*'
73 for param_type in param_types {
74 cast_sig += ', ${param_type}'
75 }
76 cast_sig += ')'
77 return InterfaceMethodInfo{
78 name: name
79 cast_signature: cast_sig
80 ret_type: ret
81 param_types: param_types
82 }
83 }
84 types.Alias {
85 if raw.base_type is types.FnType {
86 return g.interface_method_info_from_raw(name, raw.base_type)
87 }
88 return none
89 }
90 else {
91 return none
92 }
93 }
94}
95
96fn (mut g Gen) interface_method_info(field ast.FieldDecl) ?InterfaceMethodInfo {
97 if !field.is_interface_method {
98 return none
99 }
100 if fn_type := g.get_fn_type_from_expr(field.typ) {
101 return g.interface_method_info_from_ast(field.name, fn_type)
102 }
103 if raw := g.get_raw_type(field.typ) {
104 // Only treat as method if the AST type was directly a FnType,
105 // NOT if it's a named type alias for a function (e.g. `on_scroll_change ScrollViewChangedFn`).
106 // Named fn-type aliases are interface data fields, not vtable methods.
107 if raw is types.Alias && raw.base_type is types.FnType {
108 // This is a fn-type alias field, not a method declaration
109 return none
110 }
111 return g.interface_method_info_from_raw(field.name, raw)
112 }
113 return none
114}
115
116fn interface_type_id_for_name(name string) int {
117 match name {
118 'None__' {
119 return 1
120 }
121 'Error' {
122 return 2
123 }
124 'MessageError' {
125 return 3
126 }
127 else {
128 // Derive a deterministic non-zero id for all other concrete/interface types.
129 // This keeps `if iface is ConcreteType` checks working beyond IError variants.
130 if name.len == 0 {
131 return 0
132 }
133 mut hash := u32(2166136261)
134 for b in name.bytes() {
135 hash ^= u32(b)
136 hash *= 16777619
137 }
138 mut type_id := int(hash & 0x7fffffff)
139 if type_id <= 3 {
140 type_id += 4
141 }
142 return type_id
143 }
144 }
145}
146
147fn interface_wrapper_name(iface_name string, concrete_type string, method_name string) string {
148 return '__iface_wrap_' + mangle_alias_component(iface_name) + '_' +
149 mangle_alias_component(concrete_type) + '_' + method_name
150}
151
152fn interface_clone_fn_name(iface_name string) string {
153 return '__v_interface_clone__' + mangle_alias_component(iface_name)
154}
155
156fn (mut g Gen) collect_interface_wrapper_specs() {
157 if g.interface_wrapper_specs.len > 0 {
158 return
159 }
160 mut iface_names := g.interface_methods.keys()
161 iface_names.sort()
162 mut fn_names := g.fn_param_is_ptr.keys()
163 fn_names.sort()
164 for iface_name in iface_names {
165 methods := g.interface_methods[iface_name]
166 for method in methods {
167 suffix := '__${method.name}'
168 for fn_name in fn_names {
169 if !fn_name.ends_with(suffix) {
170 continue
171 }
172 ptr_params := g.fn_param_is_ptr[fn_name] or { continue }
173 if ptr_params.len == 0 || ptr_params[0] {
174 // Pointer receivers can be called directly through void*.
175 continue
176 }
177 fn_params := g.fn_param_types[fn_name] or { continue }
178 if fn_params.len != method.param_types.len + 1 {
179 continue
180 }
181 if fn_ret := g.fn_return_types[fn_name] {
182 if fn_ret != method.ret_type {
183 continue
184 }
185 }
186 concrete_type := fn_name[..fn_name.len - suffix.len]
187 receiver_type := fn_params[0].trim_right('*')
188 if receiver_type != concrete_type {
189 continue
190 }
191 wrapper_name := interface_wrapper_name(iface_name, concrete_type, method.name)
192 if wrapper_name in g.interface_wrapper_specs {
193 continue
194 }
195 g.interface_wrapper_specs[wrapper_name] = InterfaceWrapperSpec{
196 fn_name: fn_name
197 concrete_type: concrete_type
198 method: method
199 }
200 }
201 }
202 }
203 for iface_name in iface_names {
204 methods := g.interface_methods[iface_name]
205 if g.has_flat() {
206 for i in 0 .. g.flat.files.len {
207 fc := g.flat.file_cursor(i)
208 g.set_file_cursor_module(fc)
209 stmts := fc.stmts()
210 for j in 0 .. stmts.len() {
211 stmt := stmts.at(j)
212 if stmt.kind() != .stmt_struct_decl {
213 continue
214 }
215 decl := stmt.struct_decl()
216 if decl.language != .v {
217 continue
218 }
219 concrete_type := g.get_struct_name(decl)
220 if concrete_type == '' || g.is_interface_type(concrete_type) {
221 continue
222 }
223 for method in methods {
224 direct_name := '${concrete_type}__${method.name}'
225 if direct_name in g.fn_param_is_ptr || direct_name in g.fn_return_types {
226 continue
227 }
228 resolved := g.resolve_embedded_method_info(concrete_type, method.name) or {
229 continue
230 }
231 wrapper_name := interface_wrapper_name(iface_name, concrete_type,
232 method.name)
233 if wrapper_name in g.interface_wrapper_specs {
234 continue
235 }
236 g.interface_wrapper_specs[wrapper_name] = InterfaceWrapperSpec{
237 fn_name: resolved.fn_name
238 concrete_type: concrete_type
239 method: method
240 }
241 }
242 }
243 }
244 continue
245 }
246 for file in g.files {
247 g.set_file_module(file)
248 for stmt in file.stmts {
249 if stmt is ast.StructDecl && stmt.language == .v {
250 concrete_type := g.get_struct_name(stmt)
251 if concrete_type == '' || g.is_interface_type(concrete_type) {
252 continue
253 }
254 for method in methods {
255 direct_name := '${concrete_type}__${method.name}'
256 if direct_name in g.fn_param_is_ptr || direct_name in g.fn_return_types {
257 continue
258 }
259 resolved := g.resolve_embedded_method_info(concrete_type, method.name) or {
260 continue
261 }
262 wrapper_name := interface_wrapper_name(iface_name, concrete_type,
263 method.name)
264 if wrapper_name in g.interface_wrapper_specs {
265 continue
266 }
267 g.interface_wrapper_specs[wrapper_name] = InterfaceWrapperSpec{
268 fn_name: resolved.fn_name
269 concrete_type: concrete_type
270 method: method
271 }
272 }
273 }
274 }
275 }
276 }
277}
278
279fn (mut g Gen) emit_interface_method_wrapper_decls() {
280 mut names := g.interface_wrapper_specs.keys()
281 names.sort()
282 for name in names {
283 spec := g.interface_wrapper_specs[name]
284 g.sb.write_string('static ${spec.method.ret_type} ${name}(void* _obj')
285 for i, param_type in spec.method.param_types {
286 g.sb.write_string(', ${param_type} _arg${i}')
287 }
288 g.sb.writeln(');')
289 }
290 if names.len > 0 {
291 g.sb.writeln('')
292 }
293}
294
295fn (mut g Gen) emit_interface_method_wrapper_body(name string, spec InterfaceWrapperSpec) {
296 g.sb.write_string('static ${spec.method.ret_type} ${name}(void* _obj')
297 for i, param_type in spec.method.param_types {
298 g.sb.write_string(', ${param_type} _arg${i}')
299 }
300 g.sb.writeln(') {')
301 if spec.method.ret_type != 'void' {
302 g.sb.write_string('\treturn ')
303 } else {
304 g.sb.write_string('\t')
305 }
306 mut receiver_type := spec.concrete_type
307 if param_types := g.fn_param_types[spec.fn_name] {
308 if param_types.len > 0 {
309 receiver_type = param_types[0].trim_space().trim_right('*')
310 }
311 }
312 ptr_params := g.fn_param_is_ptr[spec.fn_name] or { []bool{} }
313 receiver_is_ptr := ptr_params.len > 0 && ptr_params[0]
314 owner_path := if receiver_type != spec.concrete_type {
315 g.embedded_owner_path_for_type(spec.concrete_type, receiver_type)
316 } else {
317 ''
318 }
319 g.sb.write_string('${spec.fn_name}(')
320 if owner_path != '' {
321 if receiver_is_ptr {
322 g.sb.write_string('&(((${spec.concrete_type}*)_obj)->${owner_path})')
323 } else {
324 g.sb.write_string('(((${spec.concrete_type}*)_obj)->${owner_path})')
325 }
326 } else if receiver_is_ptr {
327 g.sb.write_string('((${spec.concrete_type}*)_obj)')
328 } else {
329 g.sb.write_string('*(((${spec.concrete_type}*)_obj))')
330 }
331 for i in 0 .. spec.method.param_types.len {
332 g.sb.write_string(', _arg${i}')
333 }
334 g.sb.writeln(');')
335 g.sb.writeln('}')
336 g.sb.writeln('')
337}
338
339fn (mut g Gen) emit_needed_interface_method_wrappers() {
340 mut names := g.needed_interface_wrappers.keys()
341 names.sort()
342 if names.len == 0 {
343 return
344 }
345 for name in names {
346 spec := g.interface_wrapper_specs[name] or { continue }
347 g.emit_interface_method_wrapper_body(name, spec)
348 }
349}
350
351fn (mut g Gen) emit_interface_clone_decls() {
352 mut names := g.emitted_interface_bodies.keys()
353 names.sort()
354 for name in names {
355 g.sb.writeln('static inline ${name} ${interface_clone_fn_name(name)}(${name} x);')
356 }
357 if names.len > 0 {
358 g.sb.writeln('')
359 }
360}
361
362fn (mut g Gen) interface_clone_ptr_expr(ctype string, ptr_name string) string {
363 if ctype == 'string' || ctype == 'builtin__string' {
364 return '(${ctype}*)memdup(&(${ctype}){string__clone(*(${ctype}*)${ptr_name})}, sizeof(${ctype}))'
365 }
366 if ctype == 'map' || ctype.starts_with('Map_') {
367 return '(${ctype}*)memdup(&(${ctype}){map__clone((${ctype}*)${ptr_name})}, sizeof(${ctype}))'
368 }
369 if ctype == 'array' || ctype.starts_with('Array_') {
370 depth := g.array_clone_depth_for_c_type(ctype)
371 return '(${ctype}*)memdup(&(${ctype}){array__clone_to_depth((${ctype}*)${ptr_name}, ${depth})}, sizeof(${ctype}))'
372 }
373 return '(${ctype}*)memdup(${ptr_name}, sizeof(${ctype}))'
374}
375
376fn (mut g Gen) emit_interface_clone_body(iface_name string) {
377 clone_fn := interface_clone_fn_name(iface_name)
378 g.sb.writeln('static inline ${iface_name} ${clone_fn}(${iface_name} x) {')
379 g.sb.writeln('\tif (x._object == 0) {')
380 g.sb.writeln('\t\treturn x;')
381 g.sb.writeln('\t}')
382 data_fields := g.interface_data_fields[iface_name] or { []InterfaceDataFieldInfo{} }
383 for ctype in g.find_concrete_types_for_interface(iface_name) {
384 type_short := if ctype.contains('__') {
385 ctype.all_after_last('__')
386 } else {
387 ctype
388 }
389 type_id := interface_type_id_for_name(type_short)
390 clone_ptr_expr := g.interface_clone_ptr_expr(ctype, 'x._object')
391 g.sb.writeln('\tif (x._type_id == ${type_id}) {')
392 g.sb.writeln('\t\t${ctype}* _clone = ${clone_ptr_expr};')
393 g.sb.writeln('\t\t${iface_name} cloned = x;')
394 g.sb.writeln('\t\tcloned._object = (void*)_clone;')
395 for df in data_fields {
396 owner := g.embedded_owner_for(ctype, df.name)
397 if owner != '' {
398 g.sb.writeln('\t\tcloned.${df.name} = &(_clone->${owner}.${df.name});')
399 } else {
400 g.sb.writeln('\t\tcloned.${df.name} = &(_clone->${df.name});')
401 }
402 }
403 g.sb.writeln('\t\treturn cloned;')
404 g.sb.writeln('\t}')
405 }
406 g.sb.writeln('\treturn x;')
407 g.sb.writeln('}')
408 g.sb.writeln('')
409}
410
411fn (mut g Gen) emit_interface_clone_helpers() {
412 mut names := g.emitted_interface_bodies.keys()
413 names.sort()
414 for name in names {
415 g.emit_interface_clone_body(name)
416 }
417}
418
419fn (mut g Gen) emit_option_string_clone_helper() {
420 if '_option_string' !in g.option_aliases {
421 return
422 }
423 g.sb.writeln('static _option_string builtin__Option_string__clone(_option_string s) {')
424 g.sb.writeln('\tif (s.state == 0) {')
425 g.sb.writeln('\t\tstring _val = string__clone(*(string*)(((u8*)(&s.err)) + sizeof(IError)));')
426 g.sb.writeln('\t\t_option_string _opt = (_option_string){ .state = 2 };')
427 g.sb.writeln('\t\t_option_ok(&_val, (_option*)&_opt, sizeof(_val));')
428 g.sb.writeln('\t\treturn _opt;')
429 g.sb.writeln('\t}')
430 g.sb.writeln('\treturn s;')
431 g.sb.writeln('}')
432 g.sb.writeln('')
433}
434
435fn (mut g Gen) struct_type_embeds_error(st types.Struct) bool {
436 for embedded in st.embedded {
437 embedded_c := g.types_type_to_c(embedded)
438 if short_type_name(embedded_c) in ['Error', 'MessageError']
439 || ('${embedded_c}__msg' in g.fn_return_types
440 && '${embedded_c}__code' in g.fn_return_types) {
441 return true
442 }
443 live_emb := if embedded.fields.len == 0 {
444 g.lookup_struct_type_by_c_name(embedded_c)
445 } else {
446 embedded
447 }
448 if (live_emb.fields.len > 0 || live_emb.embedded.len > 0)
449 && g.struct_type_embeds_error(live_emb) {
450 return true
451 }
452 }
453 return false
454}
455
456fn (mut g Gen) struct_c_type_embeds_error(c_type string) bool {
457 st := g.lookup_struct_type_by_c_name(c_type.trim_right('*'))
458 if st.fields.len == 0 && st.embedded.len == 0 {
459 return false
460 }
461 return g.struct_type_embeds_error(st)
462}
463
464fn (mut g Gen) concrete_ierror_base_for_c_type(c_type string) string {
465 base := g.qualify_ierror_concrete_base(c_type.trim_right('*'))
466 if base == '' || base == 'int' || base in ['IError', 'builtin__IError'] {
467 return ''
468 }
469 if g.c_type_has_ierror_shape(base) || g.struct_c_type_embeds_error(base) {
470 return base
471 }
472 return ''
473}
474
475fn (mut g Gen) c_type_has_ierror_shape(base string) bool {
476 if '${base}__msg' in g.fn_return_types {
477 return true
478 }
479 mut names := [base]
480 if base.contains('__') {
481 names << base.all_after_last('__')
482 }
483 if g.env != unsafe { nil } {
484 for name in names {
485 if _ := g.env.lookup_method(name, 'msg') {
486 return true
487 }
488 }
489 }
490 return false
491}
492
493fn is_ierror_c_type(c_type string) bool {
494 mut typ := c_type.trim_space().trim_right('*').trim_space()
495 if typ.starts_with('struct ') {
496 typ = typ['struct '.len..].trim_space()
497 }
498 return is_ierror_interface_name(typ)
499}
500
501fn ierror_type_label_for_base(base string) string {
502 if base.contains('__') {
503 short_name := base.all_after_last('__')
504 if short_name != '' {
505 return short_name
506 }
507 }
508 return base
509}
510
511fn (mut g Gen) gen_ierror_from_base_expr(base string, expr ast.Expr, expr_type string) bool {
512 if base == '' || base == 'int' || base in ['IError', 'builtin__IError'] {
513 return false
514 }
515 g.ierror_wrapper_bases[base] = true
516 g.needed_ierror_wrapper_bases[base] = true
517 type_label := ierror_type_label_for_base(base)
518 type_id := interface_type_id_for_name(type_label)
519 if expr_type.ends_with('*') {
520 g.sb.write_string('((IError){._object = (void*)(')
521 g.expr(expr)
522 g.sb.write_string('), ._type_id = ${type_id}, .type_name = IError_${base}_type_name_wrapper, .msg = IError_${base}_msg_wrapper, .code = IError_${base}_code_wrapper})')
523 return true
524 }
525 tmp_name := '_ierr_obj${g.tmp_counter}'
526 g.tmp_counter++
527 malloc_call := g.c_heap_malloc_call('sizeof(${base})')
528 g.sb.write_string('({ ${base}* ${tmp_name} = (${base}*)${malloc_call}; *${tmp_name} = ')
529 g.expr(expr)
530 g.sb.write_string('; ((IError){._object = (void*)${tmp_name}, ._type_id = ${type_id}, .type_name = IError_${base}_type_name_wrapper, .msg = IError_${base}_msg_wrapper, .code = IError_${base}_code_wrapper}); })')
531 return true
532}
533
534fn (mut g Gen) gen_ierror_from_concrete_expr(expr ast.Expr, expr_type string) bool {
535 mut base := ''
536 expr_base := g.ierror_concrete_base_for_expr(expr)
537 if expr_base != '' {
538 base = g.concrete_ierror_base_for_c_type(expr_base)
539 }
540 if base == '' {
541 base = g.concrete_ierror_base_for_c_type(expr_type)
542 }
543 if base == '' {
544 return false
545 }
546 return g.gen_ierror_from_base_expr(base, expr, expr_type)
547}
548
549fn (mut g Gen) collect_ierror_wrapper_bases() {
550 for fn_name, ret_type in g.fn_return_types {
551 if !fn_name.ends_with('__msg') || ret_type != 'string' {
552 continue
553 }
554 base := fn_name[..fn_name.len - '__msg'.len]
555 if base == '' || !is_c_identifier_like(base) {
556 continue
557 }
558 g.ierror_wrapper_bases[base] = true
559 }
560 saved_module := g.cur_module
561 if g.has_flat() {
562 for i in 0 .. g.flat.files.len {
563 fc := g.flat.file_cursor(i)
564 g.set_file_cursor_module(fc)
565 stmts := fc.stmts()
566 for j in 0 .. stmts.len() {
567 stmt := stmts.at(j)
568 if stmt.kind() == .stmt_struct_decl {
569 decl := stmt.struct_decl()
570 if decl.language == .v && decl.embedded.len > 0 {
571 base := g.get_struct_name(decl)
572 if g.struct_c_type_embeds_error(base) {
573 g.ierror_wrapper_bases[base] = true
574 }
575 }
576 } else if stmt.kind() == .stmt_const_decl {
577 const_decl := stmt.const_decl()
578 for field in const_decl.fields {
579 if base := g.ierror_const_wrapper_base(field.value) {
580 g.ierror_wrapper_bases[base] = true
581 }
582 }
583 }
584 }
585 }
586 g.cur_module = saved_module
587 return
588 }
589 for file in g.files {
590 g.set_file_module(file)
591 for stmt in file.stmts {
592 if stmt is ast.StructDecl && stmt.language == .v && stmt.embedded.len > 0 {
593 base := g.get_struct_name(stmt)
594 if g.struct_c_type_embeds_error(base) {
595 g.ierror_wrapper_bases[base] = true
596 }
597 } else if stmt is ast.ConstDecl {
598 const_decl := stmt as ast.ConstDecl
599 for field in const_decl.fields {
600 if base := g.ierror_const_wrapper_base(field.value) {
601 g.ierror_wrapper_bases[base] = true
602 }
603 }
604 }
605 }
606 }
607 g.cur_module = saved_module
608}
609
610fn (mut g Gen) ierror_const_wrapper_base(expr ast.Expr) ?string {
611 match expr {
612 ast.CastExpr {
613 type_name := g.expr_type_to_c(expr.typ)
614 if type_name in ['IError', 'builtin__IError'] {
615 mut base :=
616 g.raw_concrete_type_for_interface_value(type_name, expr.expr).trim_right('*')
617 if base == '' || base == 'int' || base in ['IError', 'builtin__IError'] {
618 base = g.ierror_init_expr_base(expr.expr) or { '' }
619 }
620 base = g.qualify_ierror_concrete_base(base.trim_right('*'))
621 if base != '' && base != 'int' && base !in ['IError', 'builtin__IError'] {
622 return base
623 }
624 }
625 return g.ierror_const_wrapper_base(expr.expr)
626 }
627 ast.ParenExpr {
628 return g.ierror_const_wrapper_base(expr.expr)
629 }
630 else {
631 return none
632 }
633 }
634}
635
636fn (mut g Gen) ierror_init_expr_base(expr ast.Expr) ?string {
637 match expr {
638 ast.PrefixExpr {
639 return g.ierror_init_expr_base(expr.expr)
640 }
641 ast.ParenExpr {
642 return g.ierror_init_expr_base(expr.expr)
643 }
644 ast.InitExpr {
645 base := g.expr_type_to_c(expr.typ).trim_right('*')
646 if base != '' && base != 'int' {
647 return base
648 }
649 }
650 else {}
651 }
652
653 return none
654}
655
656fn (mut g Gen) mark_needed_ierror_wrapper_from_ident(name string) {
657 base := ierror_wrapper_base_from_ident(name)
658 if base == '' {
659 return
660 }
661 g.needed_ierror_wrapper_bases[base] = true
662}
663
664fn (g &Gen) should_emit_ierror_wrappers() bool {
665 return g.has_emitted_ierror_body()
666}
667
668fn (g &Gen) has_emitted_ierror_body() bool {
669 for name, _ in g.emitted_interface_bodies {
670 if is_ierror_interface_name(name) {
671 return true
672 }
673 }
674 return false
675}
676
677fn (mut g Gen) emit_ierror_wrapper_decls() {
678 if !g.should_emit_ierror_wrappers() {
679 return
680 }
681 g.collect_ierror_wrapper_bases()
682 mut bases := g.ierror_wrapper_bases.keys()
683 bases.sort()
684 for base in bases {
685 g.sb.writeln('static string IError_${base}_type_name_wrapper(void* _obj);')
686 g.sb.writeln('static string IError_${base}_msg_wrapper(void* _obj);')
687 g.sb.writeln('static int IError_${base}_code_wrapper(void* _obj);')
688 }
689 if bases.len > 0 {
690 g.sb.writeln('')
691 }
692}
693
694fn (mut g Gen) emit_ierror_wrapper_body(base string) {
695 type_label := ierror_type_label_for_base(base)
696 g.sb.writeln('static string IError_${base}_type_name_wrapper(void* _obj) {')
697 g.sb.writeln('\t(void)_obj;')
698 g.sb.writeln('\treturn ${c_static_v_string_expr(type_label)};')
699 g.sb.writeln('}')
700 g.sb.writeln('static string IError_${base}_msg_wrapper(void* _obj) {')
701 if '${base}__msg' in g.fn_return_types {
702 g.sb.writeln('\treturn ${base}__msg(*(${base}*)_obj);')
703 } else if resolved := g.resolve_embedded_method_info(base, 'msg') {
704 if params := g.fn_param_is_ptr[resolved.fn_name] {
705 if params.len > 0 && params[0] {
706 g.sb.writeln('\treturn ${resolved.fn_name}(&(((${base}*)_obj)->${resolved.owner_path}));')
707 } else {
708 g.sb.writeln('\treturn ${resolved.fn_name}(((${base}*)_obj)->${resolved.owner_path});')
709 }
710 } else {
711 g.sb.writeln('\treturn ${resolved.fn_name}(((${base}*)_obj)->${resolved.owner_path});')
712 }
713 } else {
714 g.sb.writeln('\t(void)_obj;')
715 g.sb.writeln('\treturn ${c_empty_v_string_expr()};')
716 }
717 g.sb.writeln('}')
718 g.sb.writeln('static int IError_${base}_code_wrapper(void* _obj) {')
719 code_fn := '${base}__code'
720 if code_fn in g.fn_return_types {
721 g.sb.writeln('\treturn ${base}__code(*(${base}*)_obj);')
722 } else if resolved := g.resolve_embedded_method_info(base, 'code') {
723 if params := g.fn_param_is_ptr[resolved.fn_name] {
724 if params.len > 0 && params[0] {
725 g.sb.writeln('\treturn ${resolved.fn_name}(&(((${base}*)_obj)->${resolved.owner_path}));')
726 } else {
727 g.sb.writeln('\treturn ${resolved.fn_name}(((${base}*)_obj)->${resolved.owner_path});')
728 }
729 } else {
730 g.sb.writeln('\treturn ${resolved.fn_name}(((${base}*)_obj)->${resolved.owner_path});')
731 }
732 } else {
733 g.sb.writeln('\t(void)_obj;')
734 g.sb.writeln('\treturn 1;')
735 }
736 g.sb.writeln('}')
737}
738
739fn (mut g Gen) emit_fn_decl_by_c_name(fn_name string) {
740 if fn_name == '' || 'fn_${fn_name}' in g.emitted_types {
741 return
742 }
743 saved_file_name := g.cur_file_name
744 saved_module := g.cur_module
745 saved_import_modules := g.cur_import_modules.clone()
746 defer {
747 g.cur_file_name = saved_file_name
748 g.cur_module = saved_module
749 g.cur_import_modules = saved_import_modules.clone()
750 g.is_module_ident_cache.clear()
751 g.resolved_module_names.clear()
752 }
753 if g.has_flat() {
754 for i in 0 .. g.flat.files.len {
755 fc := g.flat.file_cursor(i)
756 g.set_file_cursor_module(fc)
757 if (g.emit_modules.len > 0 || g.emit_files.len > 0) && !g.should_emit_current_file() {
758 continue
759 }
760 stmts := fc.stmts()
761 for j in 0 .. stmts.len() {
762 stmt := stmts.at(j)
763 if stmt.kind() != .stmt_fn_decl {
764 continue
765 }
766 decl_sig := stmt.fn_decl_signature()
767 if g.get_fn_name(decl_sig) == fn_name {
768 decl := stmt.fn_decl()
769 g.gen_fn_decl_with_name(decl, fn_name)
770 return
771 }
772 }
773 }
774 return
775 }
776 for file in g.files {
777 g.set_file_module(file)
778 if (g.emit_modules.len > 0 || g.emit_files.len > 0) && !g.should_emit_current_file() {
779 continue
780 }
781 for stmt in file.stmts {
782 if stmt is ast.FnDecl && g.get_fn_name(stmt) == fn_name {
783 g.gen_fn_decl_with_name(stmt, fn_name)
784 return
785 }
786 }
787 }
788}
789
790fn (mut g Gen) emit_ierror_wrapper_dependencies(base string) {
791 for method_name in ['msg', 'code'] {
792 direct_name := '${base}__${method_name}'
793 if direct_name in g.fn_return_types || direct_name in g.v_fn_return_types {
794 g.emit_fn_decl_by_c_name(direct_name)
795 continue
796 }
797 if resolved := g.resolve_embedded_method_info(base, method_name) {
798 g.emit_fn_decl_by_c_name(resolved.fn_name)
799 }
800 }
801}
802
803fn (mut g Gen) emit_needed_ierror_wrappers() {
804 if !g.should_emit_ierror_wrappers() {
805 return
806 }
807 mut bases := g.needed_ierror_wrapper_bases.keys()
808 bases.sort()
809 if bases.len == 0 {
810 return
811 }
812 mut emitted_any := false
813 for base in bases {
814 if base !in g.ierror_wrapper_bases {
815 continue
816 }
817 g.emit_ierror_wrapper_dependencies(base)
818 g.emit_ierror_wrapper_body(base)
819 emitted_any = true
820 }
821 if emitted_any {
822 g.sb.writeln('')
823 }
824}
825
826fn (mut g Gen) get_interface_name(node ast.InterfaceDecl) string {
827 if g.cur_module != '' && g.cur_module != 'main' && g.cur_module != 'builtin' {
828 return '${g.cur_module}__${node.name}'
829 }
830 return node.name
831}
832
833fn (g &Gen) lookup_interface_data_field(iface_name string, field_name string) ?InterfaceDataFieldInfo {
834 mut candidates := []string{cap: 3}
835 candidates << iface_name
836 if iface_name.contains('__') {
837 candidates << iface_name.all_after_last('__')
838 }
839 for candidate in candidates {
840 if fields := g.interface_data_fields[candidate] {
841 for field in fields {
842 if field.name == field_name {
843 return field
844 }
845 }
846 }
847 }
848 return none
849}
850
851fn (mut g Gen) selector_interface_data_field(sel ast.SelectorExpr) ?InterfaceDataFieldInfo {
852 if raw_type := g.get_raw_type(sel.lhs) {
853 match raw_type {
854 types.Interface {
855 return g.lookup_interface_data_field(g.types_type_to_c(raw_type), sel.rhs.name)
856 }
857 types.Pointer {
858 if raw_type.base_type is types.Interface {
859 return g.lookup_interface_data_field(g.types_type_to_c(raw_type.base_type),
860 sel.rhs.name)
861 }
862 }
863 else {}
864 }
865 }
866 lhs_type := g.get_expr_type(sel.lhs)
867 if lhs_type != '' {
868 if field := g.lookup_interface_data_field(lhs_type, sel.rhs.name) {
869 return field
870 }
871 }
872 return none
873}
874
875fn (g &Gen) interface_type_ready(type_name string) bool {
876 typ := type_name.trim_space()
877 if typ == '' || typ == 'void' {
878 return true
879 }
880 if typ in primitive_types {
881 return true
882 }
883 if g.is_c_type_name(typ) {
884 return true
885 }
886 if typ.ends_with('*') || typ.ends_with('ptr') {
887 return true
888 }
889 if typ == 'string' || typ == 'builtin__string' {
890 return 'body_string' in g.emitted_types || 'body_builtin__string' in g.emitted_types
891 }
892 if typ.starts_with('_option_') {
893 return typ in g.emitted_option_structs
894 }
895 if typ.starts_with('_result_') {
896 return typ in g.emitted_result_structs
897 }
898 if typ.starts_with('Array_fixed_') {
899 return 'body_${typ}' in g.emitted_types || 'alias_${typ}' in g.emitted_types
900 }
901 if typ == 'array' || typ.starts_with('Array_') || typ in g.array_aliases {
902 return 'body_array' in g.emitted_types
903 }
904 if typ == 'map' || typ.starts_with('Map_') || typ in g.map_aliases {
905 return 'body_map' in g.emitted_types
906 }
907 return 'body_${typ}' in g.emitted_types || 'enum_${typ}' in g.emitted_types
908 || 'alias_${typ}' in g.emitted_types
909}
910
911fn (mut g Gen) interface_fields_resolved(node ast.InterfaceDecl) bool {
912 if !g.interface_type_ready('string') {
913 return false
914 }
915 for field in node.fields {
916 if method := g.interface_method_info(field) {
917 if !g.interface_type_ready(method.ret_type) {
918 return false
919 }
920 for param_type in method.param_types {
921 if !g.interface_type_ready(param_type) {
922 return false
923 }
924 }
925 continue
926 }
927 field_type := g.field_type_name(field.typ)
928 if !g.interface_type_ready(field_type) {
929 return false
930 }
931 }
932 return true
933}
934
935// resolve_embedded_method checks if a method exists on an embedded struct type.
936// For example, ssl__SSLConn embeds openssl__SSLConn, so ssl__SSLConn__addr
937// resolves to openssl__SSLConn__addr.
938fn (mut g Gen) resolve_embedded_method(struct_name string, method_name string) string {
939 if resolved := g.resolve_embedded_method_info(struct_name, method_name) {
940 return resolved.fn_name
941 }
942 return ''
943}
944
945fn (mut g Gen) resolve_embedded_method_info(struct_name string, method_name string) ?EmbeddedMethodResolve {
946 st := g.lookup_struct_type_by_c_name(struct_name)
947 for emb in st.embedded {
948 emb_c_name := g.types_type_to_c(types.Type(emb)).trim_right('*')
949 if emb_c_name == '' {
950 continue
951 }
952 owner := embedded_owner_field_name(emb_c_name)
953 candidate := '${emb_c_name}__${method_name}'
954 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
955 return EmbeddedMethodResolve{
956 fn_name: candidate
957 owner_path: owner
958 receiver_type: emb_c_name
959 }
960 }
961 nested := g.resolve_embedded_method_info(emb_c_name, method_name) or { continue }
962 return EmbeddedMethodResolve{
963 fn_name: nested.fn_name
964 owner_path: '${owner}.${nested.owner_path}'
965 receiver_type: nested.receiver_type
966 }
967 }
968 return none
969}
970
971fn (mut g Gen) embedded_owner_path_for_type(struct_name string, target_type string) string {
972 st := g.lookup_struct_type_by_c_name(struct_name)
973 target_base := target_type.trim_right('*')
974 for emb in st.embedded {
975 emb_c_name := g.types_type_to_c(types.Type(emb)).trim_right('*')
976 if emb_c_name == '' {
977 continue
978 }
979 owner := embedded_owner_field_name(emb_c_name)
980 if emb_c_name == target_base || short_type_name(emb_c_name) == short_type_name(target_base) {
981 return owner
982 }
983 nested := g.embedded_owner_path_for_type(emb_c_name, target_base)
984 if nested != '' {
985 return '${owner}.${nested}'
986 }
987 }
988 return ''
989}
990
991fn (mut g Gen) resolve_embedded_interface_fields(emb_name string) ?[]ast.FieldDecl {
992 if decl := g.interface_decls[emb_name] {
993 mut fields := []ast.FieldDecl{}
994 // Recursively resolve further embeddings
995 for emb_expr in decl.embedded {
996 inner_name := g.expr_type_to_c(emb_expr)
997 if inner_name != '' {
998 if inner_fields := g.resolve_embedded_interface_fields(inner_name) {
999 fields << inner_fields
1000 }
1001 }
1002 }
1003 fields << decl.fields
1004 return fields
1005 }
1006 return none
1007}
1008
1009fn (mut g Gen) gen_interface_decl(node ast.InterfaceDecl) {
1010 name := g.get_interface_name(node)
1011 body_key := 'body_${name}'
1012 if body_key in g.emitted_types {
1013 return
1014 }
1015 g.emitted_types[body_key] = true
1016
1017 g.sb.writeln('struct ${name} {')
1018 g.sb.writeln('\tvoid* _object;')
1019 g.sb.writeln('\tint _type_id;')
1020 mut has_type_name := false
1021 for field in node.fields {
1022 if field.name == 'type_name' {
1023 has_type_name = true
1024 break
1025 }
1026 }
1027 if !has_type_name {
1028 g.sb.writeln('\tstring (*type_name)(void*);')
1029 }
1030 // Collect all fields including those from embedded interfaces
1031 mut all_fields := []ast.FieldDecl{}
1032 for emb_expr in node.embedded {
1033 emb_name := g.expr_type_to_c(emb_expr)
1034 if emb_name != '' {
1035 // Look up the embedded interface declaration and include its fields
1036 if emb_fields := g.resolve_embedded_interface_fields(emb_name) {
1037 all_fields << emb_fields
1038 }
1039 }
1040 }
1041 all_fields << node.fields
1042 // Generate function pointers for each method
1043 mut methods := []InterfaceMethodInfo{}
1044 mut data_fields := []InterfaceDataFieldInfo{}
1045 for field in all_fields {
1046 if method := g.interface_method_info(field) {
1047 g.sb.write_string('\t${method.ret_type} (*${method.name})(void*')
1048 for param_type in method.param_types {
1049 g.sb.write_string(', ${param_type}')
1050 }
1051 g.sb.writeln(');')
1052 methods << method
1053 continue
1054 }
1055 // IError.msg() and IError.code() — the AST may not expose the fn
1056 // type, but the C layout requires function pointer fields.
1057 if name == 'IError' && field.name == 'msg' {
1058 info := InterfaceMethodInfo{
1059 name: 'msg'
1060 cast_signature: 'string (*)(void*)'
1061 ret_type: 'string'
1062 param_types: []string{}
1063 }
1064 g.sb.writeln('\tstring (*msg)(void*);')
1065 methods << info
1066 continue
1067 }
1068 if name == 'IError' && field.name == 'code' {
1069 info := InterfaceMethodInfo{
1070 name: 'code'
1071 cast_signature: 'int (*)(void*)'
1072 ret_type: 'int'
1073 param_types: []string{}
1074 }
1075 g.sb.writeln('\tint (*code)(void*);')
1076 methods << info
1077 continue
1078 }
1079 // Regular field
1080 t := g.expr_type_to_c(field.typ)
1081 // Match the v1 C layout for interface data fields: store pointers to the
1082 // concrete value's fields instead of embedding copies by value. This avoids
1083 // impossible recursive layouts like `?Interface` inside the interface itself.
1084 g.sb.writeln('\t${t}* ${field.name};')
1085 data_fields << InterfaceDataFieldInfo{
1086 name: field.name
1087 c_type: t
1088 }
1089 }
1090 g.interface_methods[name] = methods
1091 g.interface_data_fields[name] = data_fields
1092 g.sb.writeln('};')
1093 g.sb.writeln('')
1094 g.emitted_interface_bodies[name] = true
1095}
1096
1097// find_concrete_types_for_interface returns all concrete type names that implement
1098// the given interface (i.e., have all required methods AND data fields).
1099fn (mut g Gen) find_concrete_types_for_interface(iface_name string) []string {
1100 methods := g.interface_methods[iface_name] or { return [] }
1101 data_fields := g.interface_data_fields[iface_name] or { []InterfaceDataFieldInfo{} }
1102 if methods.len == 0 && data_fields.len == 0 {
1103 return []
1104 }
1105 // Collect candidate concrete types from the first method's suffix
1106 mut candidates := map[string]bool{}
1107 if methods.len > 0 {
1108 first_suffix := '__${methods[0].name}'
1109 for fn_name, _ in g.fn_return_types {
1110 if fn_name.ends_with(first_suffix) {
1111 base := fn_name[..fn_name.len - first_suffix.len]
1112 if base.len == 0 || g.is_interface_type(base) {
1113 continue
1114 }
1115 param_types := g.fn_param_types[fn_name] or { []string{} }
1116 if param_types.len == 0 {
1117 continue
1118 }
1119 receiver_type := param_types[0].trim_space().trim_right('*')
1120 if receiver_type != base {
1121 continue
1122 }
1123 candidates[base] = true
1124 }
1125 }
1126 }
1127 // Filter to types that have ALL methods AND all data fields
1128 mut result := []string{}
1129 for cand, _ in candidates {
1130 mut has_all := true
1131 for method in methods {
1132 fn_name := '${cand}__${method.name}'
1133 if fn_name !in g.fn_return_types {
1134 has_all = false
1135 break
1136 }
1137 param_types := g.fn_param_types[fn_name] or { []string{} }
1138 if param_types.len == 0 {
1139 has_all = false
1140 break
1141 }
1142 receiver_type := param_types[0].trim_space().trim_right('*')
1143 if receiver_type != cand {
1144 has_all = false
1145 break
1146 }
1147 }
1148 if has_all {
1149 // Also check that the concrete type has all required data fields
1150 for df in data_fields {
1151 field_key := '${cand}.${df.name}'
1152 if field_key !in g.struct_field_types {
1153 // Try with embedded struct owner
1154 emb_key := '${cand}.${df.name}'
1155 short_cand := if cand.contains('__') { cand.all_after_last('__') } else { cand }
1156 emb_short_key := '${short_cand}.${df.name}'
1157 if emb_key !in g.embedded_field_owner
1158 && emb_short_key !in g.embedded_field_owner {
1159 has_all = false
1160 break
1161 }
1162 }
1163 }
1164 }
1165 if has_all {
1166 body_key := 'body_${cand}'
1167 if body_key in g.emitted_types || body_key in g.pending_late_body_keys {
1168 result << cand
1169 }
1170 }
1171 }
1172 result.sort()
1173 return result
1174}
1175
1176// gen_iface_to_iface_cast generates an inline interface-to-interface conversion
1177// using a GCC statement expression. Switches on _type_id to find the concrete type,
1178// then constructs the target interface from (ConcreteType*)(_object).
1179fn (mut g Gen) gen_iface_to_iface_cast(src_iface string, tgt_iface string, value_expr ast.Expr) bool {
1180 concrete_types := g.find_concrete_types_for_interface(tgt_iface)
1181 if concrete_types.len == 0 {
1182 // No known concrete types — fall back to raw expression
1183 g.expr(value_expr)
1184 return true
1185 }
1186 tmp := '_iconv${g.tmp_counter}'
1187 g.tmp_counter++
1188 g.sb.write_string('({ ${src_iface} ${tmp} = ')
1189 g.expr(value_expr)
1190 g.sb.write_string('; ')
1191 for i, ctype in concrete_types {
1192 type_short := if ctype.contains('__') {
1193 ctype.all_after_last('__')
1194 } else {
1195 ctype
1196 }
1197 type_id := interface_type_id_for_name(type_short)
1198 if i == 0 {
1199 g.sb.write_string('(${tmp}._type_id == ${type_id}) ? ')
1200 } else {
1201 g.sb.write_string(' : (${tmp}._type_id == ${type_id}) ? ')
1202 }
1203 // Construct target interface from (ConcreteType*)(src._object)
1204 g.sb.write_string('(${tgt_iface}){._object = ${tmp}._object, ._type_id = ${tmp}._type_id')
1205 // Method pointers
1206 if methods := g.interface_methods[tgt_iface] {
1207 for method in methods {
1208 fn_name := '${ctype}__${method.name}'
1209 mut target_name := fn_name
1210 if ptr_params := g.fn_param_is_ptr[fn_name] {
1211 if ptr_params.len > 0 && !ptr_params[0] {
1212 target_name = interface_wrapper_name(tgt_iface, ctype, method.name)
1213 g.needed_interface_wrappers[target_name] = true
1214 if target_name !in g.interface_wrapper_specs {
1215 g.interface_wrapper_specs[target_name] = InterfaceWrapperSpec{
1216 fn_name: fn_name
1217 concrete_type: ctype
1218 method: method
1219 }
1220 }
1221 }
1222 }
1223 g.sb.write_string(', .${method.name} = (${method.cast_signature})${target_name}')
1224 }
1225 }
1226 // Data field pointers
1227 if data_fields := g.interface_data_fields[tgt_iface] {
1228 for df in data_fields {
1229 embedded_owner := g.embedded_owner_for(ctype, df.name)
1230 if embedded_owner != '' {
1231 g.sb.write_string(', .${df.name} = &((${ctype}*)(${tmp}._object))->${embedded_owner}.${df.name}')
1232 } else {
1233 g.sb.write_string(', .${df.name} = &((${ctype}*)(${tmp}._object))->${df.name}')
1234 }
1235 }
1236 }
1237 g.sb.write_string('}')
1238 }
1239 // Default: zeroed struct
1240 g.sb.write_string(' : (${tgt_iface}){0}')
1241 g.sb.write_string('; })')
1242 return true
1243}
1244