v / vlib / v2 / gen / cleanc / cleanc_test.v
7074 lines · 6726 sloc · 174.25 KB
Raw
1// vtest build: macos
2module cleanc
3
4import os
5import v2.ast
6import v2.parser
7import v2.pref as vpref
8import v2.token
9import v2.transformer
10import v2.types
11
12fn cleanc_csrc_for_test_source(name string, source string) string {
13 tmp_file := '/tmp/v2_cleanc_${name}_${os.getpid()}.v'
14 os.write_file(tmp_file, source) or { panic('failed to write temp file') }
15 defer {
16 os.rm(tmp_file) or {}
17 }
18 prefs := &vpref.Preferences{
19 backend: .cleanc
20 no_parallel: true
21 }
22 mut file_set := token.FileSet.new()
23 mut par := parser.Parser.new(prefs)
24 files := par.parse_files([tmp_file], mut file_set)
25 mut env := types.Environment.new()
26 mut checker := types.Checker.new(prefs, file_set, env)
27 checker.check_files(files)
28 mut trans := transformer.Transformer.new_with_pref(env, prefs)
29 trans.set_file_set(file_set)
30 transformed_files := trans.transform_files(files)
31 mut gen := Gen.new_with_env_and_pref(transformed_files, env, prefs)
32 return gen.gen()
33}
34
35fn cleanc_csrc_for_test_source_flat_transform(name string, source string) string {
36 tmp_file := '/tmp/v2_cleanc_flat_${name}_${os.getpid()}.v'
37 os.write_file(tmp_file, source) or { panic('failed to write temp file') }
38 defer {
39 os.rm(tmp_file) or {}
40 }
41 prefs := &vpref.Preferences{
42 backend: .cleanc
43 no_parallel: true
44 }
45 mut file_set := token.FileSet.new()
46 mut par := parser.Parser.new(prefs)
47 files := par.parse_files([tmp_file], mut file_set)
48 mut env := types.Environment.new()
49 mut checker := types.Checker.new(prefs, file_set, env)
50 checker.check_files(files)
51 flat := ast.flatten_files(files)
52 mut trans := transformer.Transformer.new_with_pref(env, prefs)
53 trans.set_file_set(file_set)
54 _, transformed_files := trans.transform_files_to_flat_via_driver(&flat, files)
55 mut gen := Gen.new_with_env_and_pref(transformed_files, env, prefs)
56 return gen.gen()
57}
58
59fn test_c_string_literal_content_to_c_single_line() {
60 out := c_string_literal_content_to_c('hello')
61 assert out == '"hello"'
62}
63
64fn test_c_string_literal_content_to_c_multiline() {
65 out := c_string_literal_content_to_c('hello\nworld')
66 assert out == '"hello\\n"\n"world"'
67}
68
69fn test_c_string_literal_content_to_c_trailing_newline() {
70 out := c_string_literal_content_to_c('hello\n')
71 assert out == '"hello\\n"\n""'
72}
73
74fn test_c_string_literal_content_to_c_escapes_quote() {
75 out := c_string_literal_content_to_c('say "hello"')
76 assert out == '"say \\"hello\\""'
77}
78
79fn test_c_string_literal_content_to_c_preserves_percent_placeholders() {
80 out := c_string_literal_content_to_c('"%s"')
81 assert out == '"\\"%s\\""'
82}
83
84fn test_c_string_literal_content_to_c_splits_hex_escape_before_hex_digit() {
85 out := c_string_literal_content_to_c(r'\x0c8')
86 assert out == '"\\x0c""8"'
87}
88
89fn test_const_shadowing_declared_fn_uses_renamed_storage() {
90 decl := ast.ConstDecl{
91 fields: [
92 ast.FieldInit{
93 name: 'test_strings'
94 value: ast.Expr(ast.BasicLiteral{
95 kind: .number
96 value: '1'
97 })
98 },
99 ]
100 }
101
102 mut g := Gen.new([])
103 g.declared_fn_names['test_strings'] = true
104 g.gen_const_decl(decl)
105 csrc := g.sb.str()
106 assert csrc.contains('static const int __v_const_test_strings = 1;')
107 assert !csrc.contains('static const int test_strings = 1;')
108
109 mut expr_g := Gen.new([])
110 expr_g.const_c_names['test_strings'] = '__v_const_test_strings'
111 expr_g.expr(ast.Expr(ast.Ident{
112 name: 'test_strings'
113 }))
114 assert expr_g.sb.str() == '__v_const_test_strings'
115
116 mut extern_g := Gen.new([])
117 extern_g.declared_fn_names['test_strings'] = true
118 extern_g.gen_const_decl_extern(decl)
119 extern_src := extern_g.sb.str()
120 assert extern_src.contains('static const int __v_const_test_strings = 1;')
121 assert !extern_src.contains('static const int test_strings = 1;')
122}
123
124fn test_scalar_const_references_later_const_are_inlined() {
125 csrc := cleanc_csrc_for_test_source('scalar_const_later_dependency', 'module main
126
127const first = second
128const second = 2 * third
129const third = 5
130
131fn main() {
132 _ := first
133}
134')
135 assert !csrc.contains('static const int first = second;')
136 assert csrc.contains('static const int first = (2 * 5);')
137}
138
139fn test_scalar_const_resolution_preserves_numeric_casts() {
140 csrc := cleanc_csrc_for_test_source('scalar_const_cast_dependency', 'module main
141
142const shift = 64 - 11 - 1
143const frac_mask = u64((u64(1) << u64(shift)) - u64(1))
144
145fn main() {
146 _ := frac_mask
147}
148')
149 assert csrc.contains('frac_mask = ((u64)')
150 assert csrc.contains('((u64)(1)) << ((u64)')
151 assert csrc.contains('64 - 11')
152 assert csrc.contains('- ((u64)(1))')
153 assert !csrc.contains('#define frac_mask (((1 << ((64 - 11) - 1))) - 1)')
154}
155
156fn test_vec_module_generic_vec4_keeps_struct_initializers() {
157 csrc := cleanc_csrc_for_test_source('vec_module_generic_vec4', 'module vec
158
159pub struct Vec4[T] {
160pub mut:
161 x T
162 y T
163 z T
164 w T
165}
166
167pub fn vec4[T](x T, y T, z T, w T) Vec4[T] {
168 return Vec4[T]{
169 x: x
170 y: y
171 z: z
172 w: w
173 }
174}
175
176pub fn use_vec4() Vec4[f32] {
177 return vec4[f32](1.0, 2.0, 3.0, 4.0)
178}
179')
180 assert !csrc.contains('return ((vec__SimdFloat4){')
181 assert csrc.contains('return ((vec__Vec4_T_f32){')
182}
183
184fn test_vec_generic_type_expr_prefers_local_simd_alias_for_imported_vec() {
185 mut g := Gen.new([])
186 g.cur_module = 'viper'
187 g.declared_type_names_by_mod[type_decl_module_key('viper', 'viper__SimdFloat4')] = true
188 c_type := g.expr_type_to_c(ast.Expr(ast.Type(ast.GenericType{
189 name: ast.Expr(ast.SelectorExpr{
190 lhs: ast.Expr(ast.Ident{
191 name: 'vec'
192 })
193 rhs: ast.Ident{
194 name: 'Vec4'
195 }
196 })
197 params: [
198 ast.Expr(ast.Ident{
199 name: 'f32'
200 }),
201 ]
202 })))
203 assert c_type == 'viper__SimdFloat4'
204}
205
206fn test_vec_suffix_alias_ignores_plain_local_vec_names_without_simd_alias() {
207 mut g := Gen.new([])
208 g.cur_module = 'viper'
209 g.declared_type_names_by_mod[type_decl_module_key('viper', 'viper__MyVec4')] = true
210
211 c_type := g.expr_type_to_c(ast.Expr(ast.Ident{
212 name: 'MyVec4'
213 }))
214
215 assert c_type == 'MyVec4'
216}
217
218fn test_local_variable_uses_escaped_c_keyword_name() {
219 mut g := Gen.new([])
220 g.gen_assign_stmt(ast.AssignStmt{
221 op: .decl_assign
222 lhs: [ast.Expr(ast.Ident{
223 name: 'signed'
224 })]
225 rhs: [ast.Expr(ast.BasicLiteral{
226 kind: .key_true
227 value: 'true'
228 })]
229 })
230 assert g.sb.str().contains('bool _signed = true;')
231
232 mut expr_g := Gen.new([])
233 expr_g.runtime_local_types['signed'] = 'bool'
234 expr_g.expr(ast.Expr(ast.Ident{
235 name: 'signed'
236 }))
237 assert expr_g.sb.str() == '_signed'
238 assert c_fn_param_name('unix', 0) == '_unix'
239}
240
241fn test_local_variable_shadowing_enum_member_is_addressable() {
242 mut g := Gen.new([])
243 g.runtime_local_types['name'] = 'string'
244 g.enum_value_to_enum['name'] = 'token__Token'
245 g.fn_param_is_ptr['map__delete'] = [true, true]
246 g.fn_param_types['map__delete'] = ['map*', 'void*']
247 g.gen_call_arg('map__delete', 1, ast.Expr(ast.Ident{
248 name: 'name'
249 }))
250 assert g.sb.str() == '&name'
251}
252
253fn test_generic_struct_field_dependency_uses_concrete_bindings() {
254 csrc := cleanc_csrc_for_test_source('generic_struct_field_dependency', '
255module json2
256
257struct Node[T] {
258mut:
259 value T
260 next &Node[T] = unsafe { nil }
261}
262
263struct ValueInfo {
264 position int
265}
266
267struct Decoder {
268 values_info LinkedList[ValueInfo]
269 current_node &Node[ValueInfo] = unsafe { nil }
270}
271
272struct LinkedList[T] {
273mut:
274 head &Node[T] = unsafe { nil }
275 tail &Node[T] = unsafe { nil }
276 len int
277}
278')
279 linked_list_pos := csrc.index('struct json2__LinkedList_T_json2_ValueInfo {') or {
280 panic('missing LinkedList body')
281 }
282 decoder_pos := csrc.index('struct json2__Decoder {') or { panic('missing Decoder body') }
283 assert linked_list_pos < decoder_pos
284 assert csrc.contains('json2__LinkedList_T_json2_ValueInfo values_info;')
285}
286
287fn test_generic_pointer_empty_init_uses_null_pointer() {
288 mut g := Gen.new([])
289 g.active_generic_types['V'] = types.Type(types.Pointer{
290 base_type: types.Type(types.NamedType('string'))
291 })
292 g.gen_init_expr(ast.InitExpr{
293 typ: ast.Ident{
294 name: 'V'
295 }
296 })
297 assert g.sb.str() == '0'
298}
299
300fn test_decl_assign_unwraps_nested_static_modifier_lhs() {
301 mut g := Gen.new([])
302 static_lhs := ast.Expr(ast.ModifierExpr{
303 kind: .key_mut
304 expr: ast.ModifierExpr{
305 kind: .key_static
306 expr: ast.Ident{
307 name: 'ptimers'
308 }
309 }
310 })
311 g.gen_assign_stmt(ast.AssignStmt{
312 op: .decl_assign
313 lhs: [static_lhs]
314 rhs: [
315 ast.Expr(ast.CastExpr{
316 typ: ast.Ident{
317 name: 'Timers*'
318 }
319 expr: ast.BasicLiteral{
320 kind: .number
321 value: '0'
322 }
323 }),
324 ]
325 })
326 g.gen_assign_stmt(ast.AssignStmt{
327 op: .assign
328 lhs: [
329 ast.Expr(ast.Ident{
330 name: 'ptimers'
331 }),
332 ]
333 rhs: [
334 ast.Expr(ast.Ident{
335 name: 'p'
336 }),
337 ]
338 })
339 csrc := g.sb.str()
340 assert csrc.contains('static Timers* ptimers = ')
341 assert csrc.contains('ptimers = p;')
342 assert !csrc.contains('Timers* ptimers = p;')
343}
344
345fn test_receiver_panic_method_call_keeps_method_name() {
346 csrc := cleanc_csrc_for_test_source('receiver_panic_method_call', '
347struct Table {}
348
349fn (t &Table) panic(message string) {
350 _ = message
351}
352
353fn (t &Table) register() {
354 t.panic("duplicate")
355}
356')
357 assert csrc.contains('Table__panic(t, (string){.str = "duplicate"')
358 assert !csrc.contains('v_panic((*t),')
359 assert !csrc.contains('v_panic(t,')
360}
361
362fn test_transformed_map_temps_keep_position_types_when_names_repeat() {
363 csrc := cleanc_csrc_for_test_source('map_temp_position_types', '
364struct StructField {
365 name string
366}
367
368fn (f StructField) equals(other StructField) bool {
369 return f.name == other.name
370}
371
372fn f(fields []StructField) {
373 mut field_map := map[string]StructField{}
374 mut field_usages := map[string]int{}
375 for field in fields {
376 if field.name !in field_map {
377 field_map[field.name] = field
378 field_usages[field.name]++
379 } else if field.equals(field_map[field.name]) {
380 field_usages[field.name]++
381 }
382 }
383}
384')
385 for segment in csrc.split(';') {
386 if string_idx := segment.index('string _or_t') {
387 tail := segment[string_idx..]
388 assert !tail.contains('= ((int)'), segment
389 assert !tail.contains('= ((bool)'), segment
390 }
391 if field_idx := segment.index('StructField _or_t') {
392 tail := segment[field_idx..]
393 assert !tail.contains('= ((int)'), segment
394 }
395 }
396 assert csrc.contains('map__set(&field_usages')
397}
398
399fn test_module_storage_selector_uses_declaring_module_prefix() {
400 mut g := Gen.new([])
401 g.cur_module = 'checker'
402 g.cur_import_modules['ast'] = 'ast'
403 g.expr(ast.Expr(ast.SelectorExpr{
404 lhs: ast.Expr(ast.Ident{
405 name: 'ast'
406 })
407 rhs: ast.Ident{
408 name: 'global_table'
409 }
410 }))
411 assert g.sb.str() == 'ast__global_table'
412
413 mut local_g := Gen.new([])
414 local_g.cur_module = 'checker'
415 local_g.cur_import_modules['ast'] = 'ast'
416 local_g.module_storage_vars['ast__global_table'] = 'ast'
417 local_g.runtime_local_types['global_table'] = 'int'
418 local_g.expr(ast.Expr(ast.Ident{
419 name: 'global_table'
420 }))
421 assert local_g.sb.str() == 'global_table'
422}
423
424fn test_module_storage_selector_type_resolves_receiver_methods() {
425 mut g := Gen.new([])
426 g.cur_module = 'transformer'
427 g.cur_import_modules['ast'] = 'ast'
428 g.global_var_types['ast__global_table'] = 'ast__Table*'
429 g.fn_return_types['ast__Table__type_to_str'] = 'string'
430 g.fn_param_is_ptr['ast__Table__type_to_str'] = [true, false]
431 g.fn_param_types['ast__Table__type_to_str'] = ['ast__Table*', 'ast__Type']
432 g.runtime_local_types['typ'] = 'ast__Type'
433 g.call_expr(ast.Expr(ast.SelectorExpr{
434 lhs: ast.Expr(ast.SelectorExpr{
435 lhs: ast.Expr(ast.Ident{
436 name: 'ast'
437 })
438 rhs: ast.Ident{
439 name: 'global_table'
440 }
441 })
442 rhs: ast.Ident{
443 name: 'type_to_str'
444 }
445 }), [
446 ast.Expr(ast.Ident{
447 name: 'typ'
448 }),
449 ])
450 out := g.sb.str()
451 assert out.contains('ast__Table__type_to_str(ast__global_table, typ)'), out
452 assert !out.contains('int__type_to_str'), out
453}
454
455fn test_generic_arg_or_index_field_selector_prefers_indexable_field_type() {
456 u8_type := types.builtin_type('u8') or { panic('missing u8 type') }
457 embedded_file_type := types.Type(types.Struct{
458 name: 'EmbeddedFile'
459 fields: [
460 types.Field{
461 name: 'bytes'
462 typ: types.Type(types.Array{
463 elem_type: u8_type
464 })
465 },
466 ]
467 })
468 mut env := types.Environment.new()
469 mut scope := types.new_scope(unsafe { nil })
470 scope.insert('emfile', embedded_file_type)
471 env.set_fn_scope('main', 'f', scope)
472 mut g := Gen.new_with_env([], env)
473 g.cur_module = 'main'
474 g.cur_fn_name = 'f'
475 g.runtime_local_types['emfile'] = 'EmbeddedFile'
476 g.struct_field_types['EmbeddedFile.bytes'] = 'Array_u8'
477 g.fn_return_types['string__bytes'] = 'Array_u8'
478
479 ambiguous := ast.GenericArgOrIndexExpr{
480 lhs: ast.SelectorExpr{
481 lhs: ast.Ident{
482 name: 'emfile'
483 }
484 rhs: ast.Ident{
485 name: 'bytes'
486 }
487 }
488 expr: ast.BasicLiteral{
489 kind: .number
490 value: '0'
491 }
492 }
493 selector := ambiguous.lhs as ast.SelectorExpr
494 assert g.generic_arg_or_index_expr_is_index(ambiguous), g.selector_field_type(selector)
495 g.expr(ast.Expr(ambiguous))
496
497 csrc := g.sb.str()
498 assert csrc.contains('emfile.bytes'), csrc
499 assert csrc.contains('.data'), csrc
500 assert !csrc.contains('string__bytes'), csrc
501}
502
503fn test_return_mut_param_value_derefs_pointer_param() {
504 mut g := Gen.new([])
505 g.cur_fn_ret_type = 'Array_ast__Stmt'
506 g.runtime_local_types['nodes'] = 'Array_ast__Stmt*'
507 g.cur_fn_mut_params['nodes'] = true
508 g.gen_stmt(ast.ReturnStmt{
509 exprs: [ast.Expr(ast.Ident{
510 name: 'nodes'
511 })]
512 })
513 assert g.sb.str().contains('return (*nodes);')
514}
515
516fn test_return_mut_sumtype_param_derefs_before_sumtype_cast() {
517 mut g := Gen.new([])
518 g.cur_fn_ret_type = 'ast__Stmt'
519 g.sum_type_variants['ast__Stmt'] = ['ast__ExprStmt']
520 g.runtime_local_types['node'] = 'ast__Stmt*'
521 g.cur_fn_mut_params['node'] = true
522 g.gen_stmt(ast.ReturnStmt{
523 exprs: [ast.Expr(ast.Ident{
524 name: 'node'
525 })]
526 })
527 assert g.sb.str().contains('return (*node);')
528 assert !g.sb.str().contains('((ast__Stmt)(node))')
529}
530
531fn test_declared_function_pointer_call_return_type_from_runtime_alias() {
532 tmp_file := '/tmp/v2_cleanc_fn_ptr_alias_${os.getpid()}.v'
533 os.write_file(tmp_file, 'module pool\npub type ThreadCB = fn () voidptr\n') or {
534 panic('failed to write temp file')
535 }
536 defer {
537 os.rm(tmp_file) or {}
538 }
539 prefs := &vpref.Preferences{}
540 mut file_set := token.FileSet.new()
541 mut par := parser.Parser.new(prefs)
542 files := par.parse_files([tmp_file], mut file_set)
543 mut env := types.Environment.new()
544 mut checker := types.Checker.new(prefs, file_set, env)
545 checker.check_files(files)
546 mut g := Gen.new_with_env([], env)
547 g.cur_module = 'pool'
548 g.runtime_decl_types['cb'] = 'pool__ThreadCB'
549 g.gen_assign_stmt(ast.AssignStmt{
550 op: .decl_assign
551 lhs: [ast.Expr(ast.Ident{
552 name: 'res'
553 })]
554 rhs: [
555 ast.Expr(ast.CallExpr{
556 lhs: ast.Expr(ast.Ident{
557 name: 'cb'
558 })
559 }),
560 ]
561 })
562 csrc := g.sb.str()
563 assert !csrc.contains('int res = cb(')
564 assert csrc.contains('voidptr res = cb(') || csrc.contains('void* res = cb(')
565}
566
567fn test_function_pointer_struct_field_call_uses_field_selector() {
568 tmp_file := '/tmp/v2_cleanc_fn_ptr_field_alias_${os.getpid()}.v'
569 os.write_file(tmp_file, 'module main\ntype LabelFn = fn (string, voidptr) string\n') or {
570 panic('failed to write temp file')
571 }
572 defer {
573 os.rm(tmp_file) or {}
574 }
575 prefs := &vpref.Preferences{}
576 mut file_set := token.FileSet.new()
577 mut par := parser.Parser.new(prefs)
578 files := par.parse_files([tmp_file], mut file_set)
579 mut env := types.Environment.new()
580 mut checker := types.Checker.new(prefs, file_set, env)
581 checker.check_files(files)
582 env.set_expr_type(97, types.Type(types.FnType{}))
583 mut g := Gen.new_with_env([], env)
584 g.cur_module = 'main'
585 g.struct_field_types['Config.cb'] = 'LabelFn'
586 g.struct_field_types['Config.ctx'] = 'voidptr'
587 g.remember_runtime_local_type('cfg', 'Config')
588 g.remember_runtime_local_type('label', 'string')
589 g.call_expr(ast.Expr(ast.SelectorExpr{
590 lhs: ast.Expr(ast.Ident{
591 name: 'cfg'
592 })
593 rhs: ast.Ident{
594 name: 'cb'
595 }
596 pos: token.Pos{
597 id: 97
598 }
599 }), [
600 ast.Expr(ast.Ident{
601 name: 'label'
602 }),
603 ast.Expr(ast.SelectorExpr{
604 lhs: ast.Expr(ast.Ident{
605 name: 'cfg'
606 })
607 rhs: ast.Ident{
608 name: 'ctx'
609 }
610 }),
611 ])
612 csrc := g.sb.str()
613 assert csrc == 'cfg.cb(label, cfg.ctx)'
614 assert !csrc.contains('Config__cb')
615}
616
617fn test_static_constructor_call_uses_syntactic_type_receiver_without_emitted_type() {
618 mut g := Gen.new([])
619 g.cur_module = 'searcher'
620 g.fn_return_types['searcher__ByteSliceReader__new'] = 'searcher__ByteSliceReader'
621 g.fn_param_types['searcher__ByteSliceReader__new'] = ['string']
622 g.fn_param_is_ptr['searcher__ByteSliceReader__new'] = [false]
623 g.fn_return_types['searcher__LineBufferReader__new'] = 'searcher__LineBufferReader'
624 g.fn_param_types['searcher__LineBufferReader__new'] = [
625 'searcher__ByteSliceReader*',
626 'searcher__LineBuffer*',
627 ]
628 g.fn_param_is_ptr['searcher__LineBufferReader__new'] = [true, true]
629 g.remember_runtime_local_type('source', 'searcher__ByteSliceReader')
630 g.remember_runtime_local_type('linebuf', 'searcher__LineBuffer')
631 g.gen_assign_stmt(ast.AssignStmt{
632 op: .decl_assign
633 lhs: [ast.Expr(ast.Ident{
634 name: 'rdr'
635 })]
636 rhs: [
637 ast.Expr(ast.CallExpr{
638 lhs: ast.Expr(ast.SelectorExpr{
639 lhs: ast.Expr(ast.Ident{
640 name: 'LineBufferReader'
641 })
642 rhs: ast.Ident{
643 name: 'new'
644 }
645 })
646 args: [
647 ast.Expr(ast.PrefixExpr{
648 op: .amp
649 expr: ast.Expr(ast.Ident{
650 name: 'source'
651 })
652 }),
653 ast.Expr(ast.PrefixExpr{
654 op: .amp
655 expr: ast.Expr(ast.Ident{
656 name: 'linebuf'
657 })
658 }),
659 ]
660 }),
661 ]
662 })
663 csrc := g.sb.str()
664 assert csrc.contains('searcher__LineBufferReader rdr = searcher__LineBufferReader__new('), csrc
665
666 assert !csrc.contains('searcher__ByteSliceReader rdr = searcher__ByteSliceReader__new('), csrc
667}
668
669fn test_mangled_static_constructor_call_is_not_rewritten_from_first_arg_receiver() {
670 mut g := Gen.new([])
671 g.cur_module = 'searcher'
672 g.fn_return_types['searcher__ByteSliceReader__new'] = 'searcher__ByteSliceReader'
673 g.fn_param_types['searcher__ByteSliceReader__new'] = ['string']
674 g.fn_param_is_ptr['searcher__ByteSliceReader__new'] = [false]
675 g.fn_return_types['searcher__LineBufferReader__new'] = 'searcher__LineBufferReader'
676 g.fn_param_types['searcher__LineBufferReader__new'] = [
677 'io__Reader*',
678 'searcher__LineBuffer*',
679 ]
680 g.fn_param_is_ptr['searcher__LineBufferReader__new'] = [true, true]
681 g.remember_runtime_local_type('source', 'searcher__ByteSliceReader')
682 g.remember_runtime_local_type('linebuf', 'searcher__LineBuffer')
683 g.gen_assign_stmt(ast.AssignStmt{
684 op: .decl_assign
685 lhs: [ast.Expr(ast.Ident{
686 name: 'rdr'
687 })]
688 rhs: [
689 ast.Expr(ast.CallExpr{
690 lhs: ast.Expr(ast.Ident{
691 name: 'searcher__LineBufferReader__new'
692 })
693 args: [
694 ast.Expr(ast.PrefixExpr{
695 op: .amp
696 expr: ast.Expr(ast.Ident{
697 name: 'source'
698 })
699 }),
700 ast.Expr(ast.PrefixExpr{
701 op: .amp
702 expr: ast.Expr(ast.Ident{
703 name: 'linebuf'
704 })
705 }),
706 ]
707 }),
708 ]
709 })
710 csrc := g.sb.str()
711 assert csrc.contains('searcher__LineBufferReader rdr = searcher__LineBufferReader__new('), csrc
712 assert !csrc.contains('searcher__ByteSliceReader rdr = searcher__ByteSliceReader__new('), csrc
713}
714
715fn test_statement_else_unsafe_expr_emits_nested_if_guard_stmts() {
716 mut g := Gen.new([])
717 outer_if := ast.IfExpr{
718 cond: ast.Expr(ast.Ident{
719 name: 'outer_ok'
720 })
721 stmts: [
722 ast.Stmt(ast.ExprStmt{
723 expr: ast.Expr(ast.CallExpr{
724 lhs: ast.Expr(ast.Ident{
725 name: 'outer_call'
726 })
727 })
728 }),
729 ]
730 else_expr: ast.Expr(ast.UnsafeExpr{
731 stmts: [
732 ast.Stmt(ast.AssignStmt{
733 op: .decl_assign
734 lhs: [ast.Expr(ast.Ident{
735 name: '_opt_t'
736 })]
737 rhs: [ast.Expr(ast.BasicLiteral{
738 kind: .number
739 value: '1'
740 })]
741 }),
742 ast.Stmt(ast.ExprStmt{
743 expr: ast.Expr(ast.IfExpr{
744 cond: ast.Expr(ast.Ident{
745 name: 'inner_ok'
746 })
747 stmts: [
748 ast.Stmt(ast.ExprStmt{
749 expr: ast.Expr(ast.CallExpr{
750 lhs: ast.Expr(ast.Ident{
751 name: 'inner_call'
752 })
753 })
754 }),
755 ]
756 })
757 }),
758 ]
759 })
760 }
761 g.gen_if_expr_stmt(&outer_if)
762 csrc := g.sb.str()
763 assert csrc.contains('else {'), csrc
764 assert csrc.contains('int _opt_t = 1;'), csrc
765 assert csrc.contains('if (inner_ok)'), csrc
766 assert csrc.contains('inner_call();'), csrc
767}
768
769fn test_interface_pointer_data_field_selector_uses_pointer_separator() {
770 mut gen := Gen.new([])
771 gen.interface_data_fields['IResolverType'] = [
772 InterfaceDataFieldInfo{
773 name: 'file'
774 c_type: 'ast__File*'
775 },
776 ]
777 gen.struct_field_types['TypeResolver.resolver'] = 'IResolverType'
778 gen.struct_field_types['ast__File.path'] = 'string'
779 gen.remember_runtime_local_type('t', 'TypeResolver*')
780 file_expr := ast.Expr(ast.SelectorExpr{
781 lhs: ast.Expr(ast.SelectorExpr{
782 lhs: ast.Expr(ast.Ident{
783 name: 't'
784 })
785 rhs: ast.Ident{
786 name: 'resolver'
787 }
788 })
789 rhs: ast.Ident{
790 name: 'file'
791 }
792 })
793 assert gen.selector_field_type(file_expr as ast.SelectorExpr) == 'ast__File*'
794 assert gen.expr_is_pointer(file_expr)
795
796 gen.expr(ast.Expr(ast.SelectorExpr{
797 lhs: file_expr
798 rhs: ast.Ident{
799 name: 'path'
800 }
801 }))
802 out := gen.sb.str()
803 assert out.contains('))->path')
804 assert !out.contains(')).path')
805}
806
807fn test_option_return_auto_derefs_pointer_value_expr() {
808 mut gen := Gen.new([])
809 gen.cur_fn_ret_type = '_option_ast__Fn'
810 gen.remember_runtime_local_type('method', 'ast__Fn*')
811 gen.gen_stmt(ast.Stmt(ast.ReturnStmt{
812 exprs: [
813 ast.Expr(ast.Ident{
814 name: 'method'
815 }),
816 ]
817 }))
818 out := gen.sb.str()
819 assert out.contains('ast__Fn _val = (*method);')
820 assert !out.contains('ast__Fn _val = method;')
821}
822
823fn test_decl_temp_auto_derefs_pointer_rhs_for_value_scope_type() {
824 mut gen := Gen.new([])
825 mut scope := types.new_scope(unsafe { nil })
826 scope.insert_or_update('_defer_t1', types.Type(types.SumType{
827 name: 'ast__Stmt'
828 }))
829 gen.cur_fn_scope = scope
830 gen.remember_runtime_local_type('node', 'ast__Stmt*')
831 gen.gen_assign_stmt(ast.AssignStmt{
832 op: .decl_assign
833 lhs: [ast.Expr(ast.Ident{
834 name: '_defer_t1'
835 })]
836 rhs: [ast.Expr(ast.Ident{
837 name: 'node'
838 })]
839 })
840 out := gen.sb.str()
841 assert out.contains('ast__Stmt _defer_t1 = (*node);')
842 assert !out.contains('ast__Stmt _defer_t1 = node;')
843}
844
845fn test_value_return_auto_derefs_pointer_expr() {
846 mut gen := Gen.new([])
847 gen.cur_fn_ret_type = 'ast__Stmt'
848 gen.remember_runtime_local_type('_defer_t1', 'ast__Stmt*')
849 gen.gen_stmt(ast.Stmt(ast.ReturnStmt{
850 exprs: [
851 ast.Expr(ast.Ident{
852 name: '_defer_t1'
853 }),
854 ]
855 }))
856 out := gen.sb.str()
857 assert out.contains('return (*_defer_t1);')
858 assert !out.contains('return ((ast__Stmt)(_defer_t1));')
859}
860
861fn test_known_c_typedef_selectors_do_not_emit_struct_prefix() {
862 mut g := Gen.new([])
863 for name in ['atomic_uintptr_t', 'pthread_condattr_t', 'pthread_rwlockattr_t'] {
864 c_type := g.expr_type_to_c(ast.Expr(ast.SelectorExpr{
865 lhs: ast.Expr(ast.Ident{
866 name: 'C'
867 })
868 rhs: ast.Ident{
869 name: name
870 }
871 }))
872 assert c_type == name
873 }
874}
875
876fn test_signature_generic_struct_forward_typedefs_use_metadata() {
877 mut g := Gen.new([])
878 g.fn_return_types['veb__encode_gzip_T_GitHubContributor'] = 'veb__MiddlewareOptions_T_GitHubContributor'
879 g.fn_return_types['math__div_T_ast_Fileptr'] = 'math__DivResult_T_ast__Fileptr'
880 g.fn_param_types['use_middleware'] = ['veb__MiddlewareOptions_T_GitHubUser*']
881 g.emit_forward_typedefs_for_signature_types()
882 csrc := g.sb.str()
883 assert csrc.contains('typedef struct veb__MiddlewareOptions_T_GitHubContributor veb__MiddlewareOptions_T_GitHubContributor;')
884 assert csrc.contains('typedef struct veb__MiddlewareOptions_T_GitHubUser veb__MiddlewareOptions_T_GitHubUser;')
885 assert csrc.contains('typedef struct math__DivResult_T_ast__Fileptr math__DivResult_T_ast__Fileptr;')
886 assert !csrc.contains('math__DivResult_T_ast__File*')
887 assert csrc.count('typedef struct veb__MiddlewareOptions_T_GitHubUser veb__MiddlewareOptions_T_GitHubUser;') == 1
888}
889
890fn test_fn_head_emits_forward_typedef_for_concrete_generic_receiver() {
891 mut g := Gen.new([])
892 g.cur_module = 'printer'
893 count_method := ast.FnDecl{
894 name: 'count'
895 is_method: true
896 receiver: ast.Parameter{
897 name: 'w'
898 typ: ast.Expr(ast.Type(ast.GenericType{
899 name: ast.Expr(ast.SelectorExpr{
900 lhs: ast.Expr(ast.Ident{
901 name: 'printer'
902 })
903 rhs: ast.Ident{
904 name: 'CounterWriter'
905 }
906 })
907 params: [
908 ast.Expr(ast.Ident{
909 name: 'string'
910 }),
911 ]
912 }))
913 }
914 typ: ast.FnType{
915 return_type: ast.Expr(ast.Ident{
916 name: 'u64'
917 })
918 }
919 language: .v
920 }
921 fn_name := 'printer__CounterWriter_T_string__count'
922 g.fn_param_types[fn_name] = ['printer__CounterWriter_T_string']
923 // Forward typedefs for concrete generic signature types are emitted by the
924 // centralized signature pass (not inline by each fn head).
925 g.emit_forward_typedefs_for_signature_types()
926 g.gen_fn_head_with_name(count_method, fn_name)
927 g.sb.writeln(';')
928 csrc := g.sb.str()
929 assert csrc.contains('typedef struct printer__CounterWriter_T_string printer__CounterWriter_T_string;'), csrc
930 assert csrc.contains('u64 printer__CounterWriter_T_string__count(printer__CounterWriter_T_string w);'), csrc
931}
932
933fn test_register_fn_signature_preserves_pointer_receiver_on_concrete_generic_struct_instance() {
934 mut g := Gen.new([])
935 g.cur_module = 'printer'
936 g.generic_struct_instances['printer__Sink'] = [
937 GenericStructInstance{
938 params_key: 'string'
939 bindings: {
940 'W': types.Type(types.string_)
941 }
942 c_name: 'printer__Sink_T_string'
943 },
944 ]
945 stats_method := ast.FnDecl{
946 name: 'stats'
947 is_method: true
948 receiver: ast.Parameter{
949 name: 'sink'
950 typ: ast.Expr(ast.Type(ast.PointerType{
951 base_type: ast.Expr(ast.Type(ast.GenericType{
952 name: ast.Expr(ast.SelectorExpr{
953 lhs: ast.Expr(ast.Ident{
954 name: 'printer'
955 })
956 rhs: ast.Ident{
957 name: 'Sink'
958 }
959 })
960 params: [
961 ast.Expr(ast.Ident{
962 name: 'string'
963 }),
964 ]
965 }))
966 }))
967 }
968 typ: ast.FnType{
969 return_type: ast.Expr(ast.Ident{
970 name: 'int'
971 })
972 }
973 language: .v
974 }
975 fn_name := 'printer__Sink_T_string__stats'
976 g.register_fn_signature(stats_method, fn_name)
977 assert g.fn_param_types[fn_name][0] == 'printer__Sink_T_string*'
978 assert g.fn_param_is_ptr[fn_name][0]
979 g.gen_fn_head_with_name(stats_method, fn_name)
980 csrc := g.sb.str()
981 assert csrc.contains('int printer__Sink_T_string__stats(printer__Sink_T_string* sink)'), csrc
982}
983
984fn test_interface_fn_type_result_alias_is_forward_declared_before_interface_body() {
985 csrc := cleanc_csrc_for_test_source('interface_fn_type_result_alias', '
986module main
987
988struct NoCaptures {}
989
990interface Matcher {
991 new_captures() !NoCaptures
992}
993')
994 alias_pos := csrc.index('typedef struct _result_NoCaptures _result_NoCaptures;') or {
995 panic('missing result alias forward declaration')
996 }
997 iface_pos := csrc.index('struct Matcher {') or { panic('missing interface body') }
998 assert alias_pos < iface_pos
999 assert csrc.contains('_result_NoCaptures (*new_captures)(void*)')
1000}
1001
1002fn test_record_generic_struct_bindings_preserves_pointer_params() {
1003 mut env := types.Environment.new()
1004 mut printer_scope := types.new_scope(unsafe { nil })
1005 printer_scope.insert('CounterWriter', types.Type(types.Struct{
1006 name: 'printer__CounterWriter'
1007 generic_params: ['W']
1008 }))
1009 lock env.scopes {
1010 env.scopes['printer'] = printer_scope
1011 }
1012 mut g := Gen.new_with_env([], env)
1013 g.cur_module = 'printer'
1014 g.record_generic_struct_bindings('CounterWriter', 'printer__CounterWriter', [
1015 ast.Expr(ast.Ident{
1016 name: 'stringptr'
1017 }),
1018 ])
1019 instances := g.generic_struct_instances['printer__CounterWriter']
1020 assert instances.len == 1
1021 assert instances[0].params_key == 'stringptr'
1022 binding := instances[0].bindings['W'] or { panic('missing W binding') }
1023 assert binding is types.Pointer
1024 assert (binding as types.Pointer).base_type.name() == 'string'
1025 g.active_generic_types = instances[0].bindings.clone()
1026 assert g.expr_type_to_c(ast.Expr(ast.Ident{
1027 name: 'W'
1028 })) == 'string*'
1029}
1030
1031fn test_init_expr_uses_specialized_return_type_for_unqualified_generic_literal() {
1032 mut gen := Gen.new([])
1033 gen.cur_fn_ret_type = 'core__SearchWorker_T_stringptr'
1034 gen.emitted_types['body_core__SearchWorker_T_stringptr'] = true
1035 gen.emitted_types['body_core__SearchWorker_T_int'] = true
1036 gen.gen_init_expr(ast.InitExpr{
1037 typ: ast.Ident{
1038 name: 'SearchWorker'
1039 }
1040 })
1041 assert gen.sb.str() == '((core__SearchWorker_T_stringptr){0})'
1042}
1043
1044fn test_init_expr_uses_qualified_return_type_for_primary_generic_literal() {
1045 mut gen := Gen.new([])
1046 gen.cur_fn_ret_type = 'core__SearchWorker'
1047 gen.gen_init_expr(ast.InitExpr{
1048 typ: ast.Ident{
1049 name: 'SearchWorker'
1050 }
1051 })
1052 assert gen.sb.str() == '((core__SearchWorker){0})'
1053}
1054
1055fn test_init_expr_does_not_use_array_return_type_for_element_literal() {
1056 mut gen := Gen.new([])
1057 gen.cur_fn_ret_type = 'Array_printer__SubMatch'
1058 gen.cur_fn_c_name = 'printer__json_submatches'
1059 gen.gen_init_expr(ast.InitExpr{
1060 typ: ast.Ident{
1061 name: 'SubMatch'
1062 }
1063 })
1064 assert gen.sb.str() == '((printer__SubMatch){0})'
1065}
1066
1067fn test_init_expr_qualifies_unqualified_literal_from_pending_late_struct() {
1068 mut gen := Gen.new([])
1069 gen.pending_late_body_keys['body_core__SearchWorker'] = true
1070 gen.gen_init_expr(ast.InitExpr{
1071 typ: ast.Ident{
1072 name: 'SearchWorker'
1073 }
1074 })
1075 assert gen.sb.str() == '((core__SearchWorker){0})'
1076}
1077
1078fn test_init_expr_qualifies_unqualified_literal_from_module_scope_type() {
1079 mut env := types.Environment.new()
1080 mut core_scope := types.new_scope(unsafe { nil })
1081 core_scope.insert('SearchWorker', types.Type(types.Struct{
1082 name: 'core__SearchWorker'
1083 }))
1084 lock env.scopes {
1085 env.scopes['core'] = core_scope
1086 }
1087 mut gen := Gen.new_with_env([], env)
1088 gen.cur_fn_c_name = 'core__SearchWorkerBuilder__build_T_core_BufferWriter'
1089 gen.gen_init_expr(ast.InitExpr{
1090 typ: ast.Ident{
1091 name: 'SearchWorker'
1092 }
1093 })
1094 assert gen.sb.str() == '((core__SearchWorker){0})'
1095}
1096
1097fn test_init_expr_keeps_builtin_option_type_unqualified_in_module_function() {
1098 mut gen := Gen.new([])
1099 gen.cur_fn_c_name = 'searcher__Searcher__new'
1100 gen.gen_init_expr(ast.InitExpr{
1101 typ: ast.Ident{
1102 name: '_option_u64'
1103 }
1104 fields: [
1105 ast.FieldInit{
1106 name: 'state'
1107 value: ast.Expr(ast.BasicLiteral{
1108 kind: .number
1109 value: '2'
1110 })
1111 },
1112 ]
1113 })
1114 assert gen.sb.str() == '((_option_u64){ .state = 2 })'
1115}
1116
1117fn test_init_expr_keeps_external_c_type_unqualified_in_module_function() {
1118 mut gen := Gen.new([])
1119 gen.cur_fn_c_name = 'time__init_time_base'
1120 gen.gen_init_expr(ast.InitExpr{
1121 typ: ast.Ident{
1122 name: 'mach_timebase_info_data_t'
1123 }
1124 })
1125 assert gen.sb.str() == '((mach_timebase_info_data_t){0})'
1126}
1127
1128fn test_stdatomic_compat_directive_is_guarded_during_emit() {
1129 mut g := Gen.new([])
1130 mut seen := map[string]bool{}
1131 g.emit_directive(ast.Directive{
1132 name: 'include'
1133 value: '"/tmp/vroot/thirdparty/stdatomic/nix/atomic.h"'
1134 }, '/tmp/x.v', true, mut seen)
1135 csrc := g.sb.str()
1136 assert csrc.contains('#define extern static')
1137 assert csrc.contains('#include "/tmp/vroot/thirdparty/stdatomic/nix/atomic.h"')
1138 assert csrc.contains('#undef extern')
1139}
1140
1141fn test_implementation_define_is_only_emitted_for_current_file() {
1142 mut imported := Gen.new([])
1143 mut imported_seen := map[string]bool{}
1144 imported.emit_directive(ast.Directive{
1145 name: 'define'
1146 value: 'STB_IMAGE_IMPLEMENTATION'
1147 }, '/tmp/imported.v', false, mut imported_seen)
1148 assert imported.sb.str() == ''
1149
1150 mut ordinary := Gen.new([])
1151 mut ordinary_seen := map[string]bool{}
1152 ordinary.emit_directive(ast.Directive{
1153 name: 'define'
1154 value: 'APP_MAX_LIGHTS 32'
1155 }, '/tmp/imported.v', false, mut ordinary_seen)
1156 assert ordinary.sb.str() == '#define APP_MAX_LIGHTS 32\n'
1157
1158 mut current := Gen.new([])
1159 mut current_seen := map[string]bool{}
1160 current.emit_directive(ast.Directive{
1161 name: 'define'
1162 value: 'STB_IMAGE_IMPLEMENTATION'
1163 }, '/tmp/current.v', true, mut current_seen)
1164 assert current.sb.str() == '#define STB_IMAGE_IMPLEMENTATION\n'
1165}
1166
1167fn test_sum_type_call_arg_wraps_pointer_variant_arg() {
1168 mut g := Gen.new([])
1169 g.sum_type_variants['ast__Expr'] = ['SelectorExpr']
1170 g.runtime_local_types['node'] = 'ast__SelectorExpr*'
1171 assert g.gen_sum_type_call_arg('ast__Expr', ast.Expr(ast.Ident{
1172 name: 'node'
1173 }))
1174 out := g.sb.str()
1175 assert out.contains('(ast__Expr){._tag = 0, ._data._SelectorExpr =')
1176 assert out.contains('*node')
1177}
1178
1179fn test_sum_type_call_arg_keeps_declared_sum_storage_for_smartcasted_ident() {
1180 mut g := Gen.new([])
1181 g.sum_type_variants['ast__ScopeObject'] = ['ast__Var']
1182 g.runtime_local_types['obj'] = 'ast__Var'
1183 g.runtime_decl_types['obj'] = 'ast__ScopeObject'
1184 assert g.gen_sum_type_call_arg('ast__ScopeObject', ast.Expr(ast.Ident{
1185 name: 'obj'
1186 }))
1187 out := g.sb.str()
1188 assert out == 'obj'
1189 assert !out.contains('._data._Var')
1190 assert !out.contains('memdup')
1191}
1192
1193fn test_sum_type_wrap_uses_module_qualified_payload_type() {
1194 mut g := Gen.new([])
1195 g.sum_type_variants['ast__Expr'] = ['EmptyExpr', 'StructInit']
1196 g.remember_runtime_local_type('empty_expr', 'EmptyExpr')
1197 g.gen_type_cast_expr('ast__Expr', ast.Expr(ast.Ident{
1198 name: 'empty_expr'
1199 }))
1200 out := g.sb.str()
1201 assert out.contains('ast__EmptyExpr _st')
1202 assert out.contains('sizeof(ast__EmptyExpr)')
1203 assert !out.contains(' EmptyExpr _st')
1204 assert !out.contains('sizeof(EmptyExpr)')
1205}
1206
1207fn test_sum_type_wrap_uses_module_qualified_alias_literal_type() {
1208 mut g := Gen.new([])
1209 g.emitted_types['alias_ast__EmptyExpr'] = true
1210 g.sum_type_variants['ast__Expr'] = ['EmptyExpr', 'StructInit']
1211 g.gen_type_cast_expr('ast__Expr', ast.Expr(ast.InitExpr{
1212 typ: ast.Expr(ast.Ident{
1213 name: 'EmptyExpr'
1214 })
1215 }))
1216 out := g.sb.str()
1217 assert out.contains('ast__EmptyExpr _st')
1218 assert out.contains('((ast__EmptyExpr){0})')
1219 assert out.contains('sizeof(ast__EmptyExpr)')
1220 assert !out.contains(' EmptyExpr _st')
1221 assert !out.contains('((EmptyExpr){0})')
1222 assert !out.contains('sizeof(EmptyExpr)')
1223}
1224
1225fn test_pointer_arg_wraps_enum_member_ident() {
1226 mut g := Gen.new([])
1227 g.emitted_types['enum_token__Precedence'] = true
1228 g.enum_type_fields['token__Precedence'] = {
1229 'lowest': true
1230 }
1231 g.fn_param_is_ptr['__new_array_with_default_noscan'] = [false, false, false, true]
1232 g.gen_call_arg('__new_array_with_default_noscan', 3, ast.Expr(ast.Ident{
1233 name: 'token__Precedence__lowest'
1234 }))
1235 out := g.sb.str()
1236 assert out == '&((token__Precedence[1]){token__Precedence__lowest}[0])'
1237 assert !out.contains('&token__Precedence__lowest')
1238}
1239
1240fn test_enum_member_c_name_uses_declared_enum_owner() {
1241 mut g := Gen.new([])
1242 g.emitted_types['enum_StrIntpType'] = true
1243 g.emitted_types['enum_c__StrIntpType'] = true
1244 g.enum_type_fields['StrIntpType'] = {
1245 'si_no_str': true
1246 }
1247 assert g.enum_member_c_name('c__StrIntpType', 'si_no_str') == 'StrIntpType__si_no_str'
1248}
1249
1250fn test_enum_member_c_name_keeps_real_qualified_enum_owner() {
1251 mut g := Gen.new([])
1252 g.emitted_types['enum_Kind'] = true
1253 g.emitted_types['enum_foo__Kind'] = true
1254 g.enum_type_fields['Kind'] = {
1255 'a': true
1256 }
1257 g.enum_type_fields['foo__Kind'] = {
1258 'b': true
1259 }
1260 assert g.enum_member_c_name('foo__Kind', 'b') == 'foo__Kind__b'
1261}
1262
1263fn test_enum_from_string_helper_emits_option_enum_result() {
1264 mut g := Gen.new([])
1265 g.option_aliases['_option_Mode'] = true
1266 g.emitted_types['enum_Mode'] = true
1267 g.emitted_interface_bodies['IError'] = true
1268 g.emitted_types['body_None__'] = true
1269 g.emitted_types['body_string'] = true
1270 g.emit_option_result_structs()
1271 g.gen_enum_from_string_helper(ast.EnumDecl{
1272 name: 'Mode'
1273 fields: [
1274 ast.FieldDecl{
1275 name: 'fast'
1276 },
1277 ast.FieldDecl{
1278 name: 'slow'
1279 },
1280 ]
1281 })
1282 csrc := g.sb.str()
1283 assert csrc.contains('struct _option_Mode')
1284 assert csrc.contains('_option_Mode Mode__from_string(string s);')
1285 assert csrc.contains('memcmp(s.str, "fast", 4) == 0')
1286 assert csrc.contains('Mode _val = Mode__fast;')
1287 assert csrc.contains('_option_ok(&_val, (_option*)&_opt, sizeof(_val));')
1288 assert csrc.contains('return (_option_Mode){ .state = 2 };')
1289}
1290
1291fn test_cached_builtin_init_calls_plain_builtin_const_init() {
1292 mut g := Gen.new([])
1293 g.export_const_symbols = true
1294 g.cache_bundle_name = 'builtin'
1295 g.emit_modules['builtin'] = true
1296 g.fn_return_types['__v_init_consts_builtin'] = 'void'
1297 g.emit_cached_module_init_function()
1298 csrc := g.sb.str()
1299 assert csrc.contains('void __v2_cached_init_builtin(void) {')
1300 assert csrc.contains('\t__v_init_consts_builtin();')
1301 assert !csrc.contains('builtin____v_init_consts_builtin();')
1302}
1303
1304fn test_generated_test_main_runs_main_runtime_consts_after_module_init() {
1305 mut g := Gen.new([])
1306 g.test_fn_names << 'test_smoke'
1307 g.fn_return_types['__v_init_consts_main'] = 'void'
1308 g.fn_return_types['rand__init'] = 'void'
1309 csrc := g.gen_finalize()
1310 rand_init_idx := csrc.index('\trand__init();') or { panic('missing rand__init call') }
1311 main_consts_idx := csrc.index('\t__v_init_consts_main();') or {
1312 panic('missing main runtime const init call')
1313 }
1314 assert rand_init_idx < main_consts_idx
1315 assert csrc.count('__v_init_consts_main();') == 1
1316}
1317
1318fn test_sum_common_field_selector_uses_payload_pointer() {
1319 mut gen := Gen.new([])
1320 gen.sum_type_variants['ast__Stmt'] = ['FnDecl', 'TypeDecl']
1321 gen.sum_type_variants['ast__TypeDecl'] = ['AliasTypeDecl', 'FnTypeDecl']
1322 gen.struct_field_types['ast__FnDecl.pos'] = 'token__Pos'
1323 gen.struct_field_types['FnDecl.pos'] = 'token__Pos'
1324 gen.struct_field_types['ast__AliasTypeDecl.pos'] = 'token__Pos'
1325 gen.struct_field_types['AliasTypeDecl.pos'] = 'token__Pos'
1326 gen.struct_field_types['ast__FnTypeDecl.pos'] = 'token__Pos'
1327 gen.struct_field_types['FnTypeDecl.pos'] = 'token__Pos'
1328 gen.remember_runtime_local_type('stmt', 'ast__Stmt')
1329 gen.expr(ast.Expr(ast.SelectorExpr{
1330 lhs: ast.Ident{
1331 name: 'stmt'
1332 }
1333 rhs: ast.Ident{
1334 name: 'pos'
1335 }
1336 }))
1337 out := gen.sb.str()
1338 assert out.contains('switch (_sum_cf')
1339 assert out.contains('._data._FnDecl')
1340 assert out.contains('._data._TypeDecl')
1341 assert out.contains('ast__TypeDecl*')
1342 assert !out.contains('stmt.pos')
1343}
1344
1345fn test_sum_common_field_selector_respects_concrete_local_type() {
1346 mut gen := Gen.new([])
1347 gen.sum_type_variants['ast__ScopeObject'] = ['EmptyScopeObject', 'Var']
1348 gen.struct_field_types['ast__EmptyScopeObject.typ'] = 'ast__Type'
1349 gen.struct_field_types['EmptyScopeObject.typ'] = 'ast__Type'
1350 gen.struct_field_types['ast__Var.typ'] = 'ast__Type'
1351 gen.struct_field_types['Var.typ'] = 'ast__Type'
1352 gen.remember_runtime_local_type('obj', 'ast__Var')
1353 gen.expr(ast.Expr(ast.SelectorExpr{
1354 lhs: ast.Ident{
1355 name: 'obj'
1356 }
1357 rhs: ast.Ident{
1358 name: 'typ'
1359 }
1360 }))
1361 out := gen.sb.str()
1362 assert out == 'obj.typ'
1363 assert !out.contains('switch')
1364}
1365
1366fn test_sum_common_field_assignment_uses_variant_switch() {
1367 mut gen := Gen.new([])
1368 gen.sum_type_variants['ast__ScopeObject'] = ['EmptyScopeObject', 'Var']
1369 gen.struct_field_types['ast__EmptyScopeObject.typ'] = 'ast__Type'
1370 gen.struct_field_types['EmptyScopeObject.typ'] = 'ast__Type'
1371 gen.struct_field_types['ast__Var.typ'] = 'ast__Type'
1372 gen.struct_field_types['Var.typ'] = 'ast__Type'
1373 gen.remember_runtime_local_type('obj', 'ast__ScopeObject')
1374 gen.remember_runtime_local_type('ptype', 'ast__Type')
1375 gen.gen_stmt(ast.Stmt(ast.AssignStmt{
1376 op: .assign
1377 lhs: [
1378 ast.Expr(ast.SelectorExpr{
1379 lhs: ast.Expr(ast.Ident{
1380 name: 'obj'
1381 })
1382 rhs: ast.Ident{
1383 name: 'typ'
1384 }
1385 }),
1386 ]
1387 rhs: [
1388 ast.Expr(ast.Ident{
1389 name: 'ptype'
1390 }),
1391 ]
1392 }))
1393 out := gen.sb.str()
1394 assert out.contains('ast__ScopeObject _sum_cf')
1395 assert out.contains('ast__Type _field_cf')
1396 assert out.contains('switch (_sum_cf')
1397 assert out.contains('->typ = _field_cf')
1398 assert !out.contains('}) = ptype')
1399}
1400
1401fn test_mut_arg_sum_common_field_selector_uses_variant_field_address() {
1402 fn_name := 'checker__Checker__stmts_ending_with_expression'
1403 for arg in [
1404 ast.Expr(ast.ModifierExpr{
1405 kind: token.Token.key_mut
1406 expr: sum_common_field_stmts_selector_for_test()
1407 }),
1408 ast.Expr(ast.PrefixExpr{
1409 op: token.Token.amp
1410 expr: sum_common_field_stmts_selector_for_test()
1411 }),
1412 ast.Expr(ast.PrefixExpr{
1413 op: token.Token.amp
1414 expr: ast.Expr(ast.SelectorExpr{
1415 lhs: ast.Expr(ast.ParenExpr{
1416 expr: ast.Expr(ast.SelectorExpr{
1417 lhs: ast.Expr(ast.Ident{
1418 name: 'expr'
1419 })
1420 rhs: ast.Ident{
1421 name: 'or_expr'
1422 }
1423 })
1424 })
1425 rhs: ast.Ident{
1426 name: 'stmts'
1427 }
1428 })
1429 }),
1430 ast.Expr(ast.PrefixExpr{
1431 op: token.Token.amp
1432 expr: ast.Expr(ast.SelectorExpr{
1433 lhs: ast.Expr(ast.CastExpr{
1434 typ: ast.Expr(ast.Ident{
1435 name: 'ast__OrExpr'
1436 })
1437 expr: ast.Expr(ast.SelectorExpr{
1438 lhs: ast.Expr(ast.Ident{
1439 name: 'expr'
1440 })
1441 rhs: ast.Ident{
1442 name: 'or_expr'
1443 }
1444 })
1445 })
1446 rhs: ast.Ident{
1447 name: 'stmts'
1448 }
1449 })
1450 }),
1451 ast.Expr(ast.PrefixExpr{
1452 op: token.Token.amp
1453 expr: ast.Expr(ast.ModifierExpr{
1454 kind: token.Token.key_mut
1455 expr: sum_common_field_stmts_selector_for_test()
1456 })
1457 }),
1458 ] {
1459 mut gen := Gen.new([])
1460 gen.fn_param_is_ptr[fn_name] = [true, true, false]
1461 gen.fn_param_types[fn_name] = ['checker__Checker*', 'Array_ast__Stmt*', 'ast__Type']
1462 gen.sum_type_variants['checker__ORMExpr'] = ['ast__SqlExpr', 'ast__SqlStmt']
1463 gen.sum_type_variants['ast__Stmt'] = ['ast__ExprStmt']
1464 gen.struct_field_types['ast__SqlExpr.or_expr'] = 'ast__OrExpr'
1465 gen.struct_field_types['ast__SqlStmt.or_expr'] = 'ast__OrExpr'
1466 gen.struct_field_types['ast__OrExpr.stmts'] = 'Array_ast__Stmt'
1467 gen.remember_runtime_local_type('expr', 'checker__ORMExpr*')
1468 gen.gen_call_arg(fn_name, 1, arg)
1469 out := gen.sb.str()
1470 assert out.contains('Array_ast__Stmt* _field_cf'), out
1471 assert out.contains('&((((ast__SqlExpr*)'), out
1472 assert out.contains(')->or_expr.stmts)'), out
1473 assert out.contains('&((((ast__SqlStmt*)'), out
1474 assert !out.contains('&({ checker__ORMExpr*'), out
1475 }
1476}
1477
1478fn test_address_of_sum_common_field_selector_uses_variant_field_address() {
1479 mut gen := Gen.new([])
1480 gen.sum_type_variants['checker__ORMExpr'] = ['ast__SqlExpr', 'ast__SqlStmt']
1481 gen.struct_field_types['ast__SqlExpr.or_expr'] = 'ast__OrExpr'
1482 gen.struct_field_types['ast__SqlStmt.or_expr'] = 'ast__OrExpr'
1483 gen.struct_field_types['ast__OrExpr.stmts'] = 'Array_ast__Stmt'
1484 gen.remember_runtime_local_type('expr', 'checker__ORMExpr*')
1485 gen.expr(ast.Expr(ast.PrefixExpr{
1486 op: token.Token.amp
1487 expr: sum_common_field_stmts_selector_for_test()
1488 }))
1489 out := gen.sb.str()
1490 assert out.contains('Array_ast__Stmt* _field_cf'), out
1491 assert out.contains(')->or_expr.stmts)'), out
1492 assert !out.contains('&({ checker__ORMExpr*'), out
1493}
1494
1495fn sum_common_field_stmts_selector_for_test() ast.Expr {
1496 return ast.Expr(ast.SelectorExpr{
1497 lhs: ast.Expr(ast.SelectorExpr{
1498 lhs: ast.Expr(ast.Ident{
1499 name: 'expr'
1500 })
1501 rhs: ast.Ident{
1502 name: 'or_expr'
1503 }
1504 })
1505 rhs: ast.Ident{
1506 name: 'stmts'
1507 }
1508 })
1509}
1510
1511fn test_sum_variant_check_supports_nested_sum_variants() {
1512 mut gen := Gen.new([])
1513 gen.sum_type_variants['ast__Node'] = ['CallArg', 'Expr', 'IfBranch']
1514 gen.sum_type_variants['ast__Expr'] = ['Ident', 'InfixExpr']
1515 gen.remember_runtime_local_type('node', 'ast__Node')
1516 gen.expr(ast.Expr(ast.InfixExpr{
1517 op: token.Token.eq
1518 lhs: ast.Expr(ast.Ident{
1519 name: 'node'
1520 })
1521 rhs: ast.Expr(ast.SelectorExpr{
1522 lhs: ast.Expr(ast.Ident{
1523 name: 'ast'
1524 })
1525 rhs: ast.Ident{
1526 name: 'InfixExpr'
1527 }
1528 })
1529 }))
1530 out := gen.sb.str()
1531 assert out.contains('node._tag == 1')
1532 assert out.contains('((ast__Expr*)(node._data._Expr))')
1533 assert out.contains('->_tag == 1')
1534 assert !out.contains('node == ast__InfixExpr')
1535}
1536
1537fn test_selector_variant_check_uses_storage_path_for_nested_sum_variant() {
1538 mut gen := Gen.new([])
1539 gen.sum_type_variants['ast__Expr'] = ['ast__Type']
1540 gen.sum_type_variants['ast__Type'] = ['ast__AnonStructType', 'ast__ArrayFixedType']
1541 gen.struct_field_types['ast__ArrayInitExpr.typ'] = 'ast__Expr'
1542 gen.struct_field_types['ArrayInitExpr.typ'] = 'ast__Expr'
1543 gen.remember_runtime_local_type('array_init', 'ast__ArrayInitExpr')
1544 typ_selector := ast.Expr(ast.SelectorExpr{
1545 lhs: ast.Expr(ast.Ident{
1546 name: 'array_init'
1547 })
1548 rhs: ast.Ident{
1549 name: 'typ'
1550 }
1551 pos: token.Pos{
1552 id: 77
1553 }
1554 })
1555 // Emulate the RHS of `array_init.typ is ast.Type && array_init.typ is ast.ArrayFixedType`:
1556 // the checker has narrowed the selector expression to ast.Type, but the C storage is
1557 // still ast.Expr and needs a nested tag-path check.
1558 gen.selector_field_type_cache['77|typ'] = 'ast__Type'
1559 gen.expr(ast.Expr(ast.InfixExpr{
1560 op: token.Token.key_is
1561 lhs: typ_selector
1562 rhs: ast.Expr(ast.SelectorExpr{
1563 lhs: ast.Expr(ast.Ident{
1564 name: 'ast'
1565 })
1566 rhs: ast.Ident{
1567 name: 'ArrayFixedType'
1568 }
1569 })
1570 }))
1571 out := gen.sb.str()
1572 assert out.contains('array_init.typ._tag == 0'), out
1573 assert out.contains('array_init.typ._data._ast__Type'), out
1574 assert out.contains('->_tag == 1'), out
1575 assert !out.contains('(array_init.typ._tag == 1)'), out
1576}
1577
1578fn test_selector_variant_check_uses_narrowed_type_to_recover_storage_path() {
1579 mut gen := Gen.new([])
1580 gen.sum_type_variants['ast__Expr'] = ['ast__Type']
1581 gen.sum_type_variants['ast__Type'] = ['ast__AnonStructType', 'ast__ArrayFixedType']
1582 typ_selector := ast.Expr(ast.SelectorExpr{
1583 lhs: ast.Expr(ast.Ident{
1584 name: 'array_init'
1585 })
1586 rhs: ast.Ident{
1587 name: 'typ'
1588 }
1589 pos: token.Pos{
1590 id: 78
1591 }
1592 })
1593 gen.selector_field_type_cache['78|typ'] = 'ast__Type'
1594 gen.expr(ast.Expr(ast.InfixExpr{
1595 op: token.Token.key_is
1596 lhs: typ_selector
1597 rhs: ast.Expr(ast.SelectorExpr{
1598 lhs: ast.Expr(ast.Ident{
1599 name: 'ast'
1600 })
1601 rhs: ast.Ident{
1602 name: 'ArrayFixedType'
1603 }
1604 })
1605 }))
1606 out := gen.sb.str()
1607 assert out.contains('array_init.typ._tag == 0'), out
1608 assert out.contains('array_init.typ._data._ast__Type'), out
1609 assert out.contains('->_tag == 1'), out
1610 assert !out.contains('(array_init.typ._tag == 1)'), out
1611}
1612
1613fn test_nested_sumtype_selector_is_check_after_transform_uses_payload_tag() {
1614 csrc := cleanc_csrc_for_test_source_flat_transform('nested_sumtype_selector_is_check', '
1615module ast
1616
1617struct EmptyExpr {}
1618struct OtherExpr {}
1619struct BasicLiteral {}
1620struct AnonStructType {}
1621struct ArrayFixedType {
1622 len Expr
1623}
1624
1625type Type = AnonStructType | ArrayFixedType
1626type Expr = EmptyExpr | OtherExpr | Type
1627
1628struct ArrayInitExpr {
1629 typ Expr
1630 len Expr
1631}
1632
1633fn marker(array_init ArrayInitExpr) bool {
1634 return array_init.len !is EmptyExpr
1635}
1636
1637fn use(array_init ArrayInitExpr) int {
1638 if (array_init.typ is Type && array_init.typ is ArrayFixedType) || marker(array_init) {
1639 mut fixed_len := 0
1640 if array_init.typ is Type && array_init.typ is ArrayFixedType {
1641 fixed_typ := array_init.typ as ArrayFixedType
1642 fixed_len = if fixed_typ.len is BasicLiteral { 1 } else { 2 }
1643 } else {
1644 fixed_len = -1
1645 }
1646 return fixed_len
1647 }
1648 return -2
1649}
1650')
1651 assert csrc.contains('array_init.typ._data._ast__Type'), csrc
1652 assert !csrc.contains('(array_init.typ._tag == 1)'), csrc
1653}
1654
1655fn test_chained_selector_smartcast_after_flat_transform_uses_payload_path() {
1656 csrc := cleanc_csrc_for_test_source_flat_transform('chained_selector_smartcast', '
1657module ast
1658
1659struct IfExpr {}
1660
1661struct ComptimeExpr {
1662 expr Expr
1663}
1664
1665struct ExprStmt {
1666 expr Expr
1667}
1668
1669struct OtherStmt {}
1670
1671type Expr = ComptimeExpr | IfExpr
1672type Stmt = ExprStmt | OtherStmt
1673
1674fn use(stmt Stmt) int {
1675 if stmt is ExprStmt && stmt.expr is ComptimeExpr && stmt.expr.expr is IfExpr {
1676 return 1
1677 }
1678 return 0
1679}
1680')
1681 assert csrc.contains('stmt._data._ast__ExprStmt'), csrc
1682 assert csrc.contains('expr._data._ast__ComptimeExpr'), csrc
1683 assert !csrc.contains('stmt.expr.expr'), csrc
1684}
1685
1686fn test_selector_is_check_keeps_direct_sumtype_field_tag() {
1687 mut gen := Gen.new([])
1688 gen.sum_type_variants['types__Object'] = ['types__Const', 'types__Fn', 'types__Global',
1689 'types__Module', 'types__SmartCastSelector', 'types__Type', 'types__TypeObject']
1690 gen.sum_type_variants['types__Type'] = ['types__Alias', 'types__String']
1691 gen.struct_field_types['types__Alias.base_type'] = 'types__Type'
1692 gen.struct_field_types['Alias.base_type'] = 'types__Type'
1693 gen.remember_runtime_local_type('lhs_type', 'types__Alias')
1694 gen.expr(ast.Expr(ast.InfixExpr{
1695 op: token.Token.key_is
1696 lhs: ast.Expr(ast.SelectorExpr{
1697 lhs: ast.Expr(ast.Ident{
1698 name: 'lhs_type'
1699 })
1700 rhs: ast.Ident{
1701 name: 'base_type'
1702 }
1703 })
1704 rhs: ast.Expr(ast.SelectorExpr{
1705 lhs: ast.Expr(ast.Ident{
1706 name: 'types'
1707 })
1708 rhs: ast.Ident{
1709 name: 'String'
1710 }
1711 })
1712 }))
1713 out := gen.sb.str()
1714 assert out.contains('lhs_type.base_type._tag == 1'), out
1715 assert !out.contains('_data._types__Type'), out
1716}
1717
1718fn test_assign_wraps_concrete_value_for_sum_type_field() {
1719 mut gen := Gen.new([])
1720 gen.sum_type_variants['ast__TypeInfo'] = ['Interface', 'SumType']
1721 gen.struct_field_types['ast__TypeSymbol.info'] = 'ast__TypeInfo'
1722 gen.remember_runtime_local_type('expr_type_sym', 'ast__TypeSymbol*')
1723 gen.remember_runtime_local_type('info', 'ast__Interface')
1724 gen.gen_stmt(ast.Stmt(ast.AssignStmt{
1725 op: .assign
1726 lhs: [
1727 ast.Expr(ast.SelectorExpr{
1728 lhs: ast.Expr(ast.Ident{
1729 name: 'expr_type_sym'
1730 })
1731 rhs: ast.Ident{
1732 name: 'info'
1733 }
1734 }),
1735 ]
1736 rhs: [
1737 ast.Expr(ast.Ident{
1738 name: 'info'
1739 }),
1740 ]
1741 }))
1742 out := gen.sb.str()
1743 assert out.contains('expr_type_sym->info = ((ast__TypeInfo){._tag = 0, ._data._Interface =')
1744 assert out.contains('memdup(&_st')
1745 assert !out.contains('expr_type_sym->info = info;')
1746}
1747
1748fn test_init_expr_wraps_concrete_value_for_sum_type_field() {
1749 mut gen := Gen.new([])
1750 gen.sum_type_variants['ast__TypeInfo'] = ['Interface', 'SumType']
1751 gen.struct_field_types['ast__TypeSymbol.info'] = 'ast__TypeInfo'
1752 gen.remember_runtime_local_type('info', 'ast__Interface')
1753 gen.gen_init_expr(ast.InitExpr{
1754 typ: ast.Ident{
1755 name: 'ast__TypeSymbol'
1756 }
1757 fields: [
1758 ast.FieldInit{
1759 name: 'info'
1760 value: ast.Expr(ast.Ident{
1761 name: 'info'
1762 })
1763 },
1764 ]
1765 })
1766 out := gen.sb.str()
1767 assert out.contains('.info = ((ast__TypeInfo){._tag = 0, ._data._Interface ='), out
1768 assert out.contains('memdup(&_st'), out
1769 assert !out.contains('.info = info'), out
1770}
1771
1772fn test_sum_variant_check_accepts_type_rhs_for_selector_field() {
1773 mut gen := Gen.new([])
1774 gen.sum_type_variants['ast__TypeInfo'] = ['UnknownTypeInfo', 'Array']
1775 gen.struct_field_types['ast__TypeSymbol.info'] = 'ast__TypeInfo'
1776 gen.remember_runtime_local_type('elem_sym', 'ast__TypeSymbol*')
1777 gen.expr(ast.Expr(ast.InfixExpr{
1778 op: token.Token.eq
1779 lhs: ast.Expr(ast.ModifierExpr{
1780 kind: token.Token.key_mut
1781 expr: ast.Expr(ast.SelectorExpr{
1782 lhs: ast.Expr(ast.Ident{
1783 name: 'elem_sym'
1784 })
1785 rhs: ast.Ident{
1786 name: 'info'
1787 }
1788 })
1789 })
1790 rhs: ast.Expr(ast.Ident{
1791 name: 'Array'
1792 })
1793 }))
1794 out := gen.sb.str()
1795 assert out.contains('elem_sym->info._tag == 1'), out
1796 assert !out.contains('== Array'), out
1797}
1798
1799fn test_selector_field_on_narrowed_sum_selector_extracts_payload() {
1800 mut env := types.Environment.new()
1801 env.set_expr_type(92, types.Type(types.Struct{
1802 name: 'ast__Array'
1803 }))
1804 mut gen := Gen.new_with_env([], env)
1805 gen.sum_type_variants['ast__TypeInfo'] = ['UnknownTypeInfo', 'Array']
1806 gen.struct_field_types['ast__TypeSymbol.info'] = 'ast__TypeInfo'
1807 gen.struct_field_types['ast__Array.elem_type'] = 'ast__Type'
1808 gen.remember_runtime_local_type('elem_sym', 'ast__TypeSymbol*')
1809 info_sel := ast.Expr(ast.SelectorExpr{
1810 lhs: ast.Expr(ast.Ident{
1811 name: 'elem_sym'
1812 })
1813 rhs: ast.Ident{
1814 name: 'info'
1815 }
1816 pos: token.Pos{
1817 id: 92
1818 }
1819 })
1820 gen.expr(ast.Expr(ast.SelectorExpr{
1821 lhs: ast.Expr(ast.ModifierExpr{
1822 kind: token.Token.key_mut
1823 expr: info_sel
1824 })
1825 rhs: ast.Ident{
1826 name: 'elem_type'
1827 }
1828 }))
1829 out := gen.sb.str()
1830 assert out.contains('._data._Array'), out
1831 assert out.contains('->elem_type'), out
1832 assert !out.contains('info.elem_type'), out
1833}
1834
1835fn test_decl_assign_uses_position_type_before_function_scope_name_collision() {
1836 mut env := types.Environment.new()
1837 env.set_expr_type(77, types.int_)
1838 mut fn_scope := types.new_scope(unsafe { nil })
1839 fn_scope.insert('value', types.Type(types.string_))
1840
1841 mut gen := Gen.new_with_env([], env)
1842 gen.cur_fn_scope = fn_scope
1843 gen.gen_stmt(ast.Stmt(ast.AssignStmt{
1844 op: .decl_assign
1845 lhs: [
1846 ast.Expr(ast.Ident{
1847 pos: token.Pos{
1848 id: 77
1849 }
1850 name: 'value'
1851 }),
1852 ]
1853 rhs: [
1854 ast.Expr(ast.BasicLiteral{
1855 kind: .number
1856 value: '0'
1857 }),
1858 ]
1859 }))
1860 out := gen.sb.str().trim_space()
1861 assert out == 'int value = 0;'
1862 assert !out.contains('string value')
1863}
1864
1865fn test_expr_type_to_c_prefers_metadata_before_module_local_type() {
1866 mut env := types.Environment.new()
1867 env.set_expr_type(42, types.Type(types.Struct{
1868 name: 'Context'
1869 }))
1870 mut veb_scope := types.new_scope(unsafe { nil })
1871 veb_scope.insert_type('Context', types.Type(types.Struct{
1872 name: 'veb__Context'
1873 }))
1874 lock env.scopes {
1875 env.scopes['veb'] = veb_scope
1876 }
1877
1878 mut gen := Gen.new_with_env([], env)
1879 gen.cur_module = 'veb'
1880 typ := gen.expr_type_to_c(ast.Expr(ast.Ident{
1881 pos: token.Pos{
1882 id: 42
1883 }
1884 name: 'Context'
1885 }))
1886 assert typ == 'Context'
1887}
1888
1889fn test_decl_assign_uses_synthetic_lhs_alias_type_before_scope_collision() {
1890 mut env := types.Environment.new()
1891 env.set_expr_type(-77, types.Type(types.Alias{
1892 name: 'ssa__ValueID'
1893 base_type: types.int_
1894 }))
1895 mut fn_scope := types.new_scope(unsafe { nil })
1896 fn_scope.insert('op', types.Type(types.string_))
1897
1898 mut gen := Gen.new_with_env([], env)
1899 gen.cur_fn_scope = fn_scope
1900 gen.gen_stmt(ast.Stmt(ast.AssignStmt{
1901 op: .decl_assign
1902 lhs: [
1903 ast.Expr(ast.Ident{
1904 pos: token.Pos{
1905 id: -77
1906 }
1907 name: 'op'
1908 }),
1909 ]
1910 rhs: [
1911 ast.Expr(ast.BasicLiteral{
1912 kind: .number
1913 value: '0'
1914 }),
1915 ]
1916 }))
1917 out := gen.sb.str().trim_space()
1918 assert out == 'ssa__ValueID op = ((ssa__ValueID){0});'
1919 assert !out.contains('string op')
1920}
1921
1922fn test_infix_compare_uses_runtime_payload_decl_types_before_stale_env() {
1923 mut env := types.Environment.new()
1924 env.set_expr_type(1771, types.Type(types.string_))
1925 env.set_expr_type(1772, types.Type(types.string_))
1926 mut gen := Gen.new_with_env([], env)
1927 gen.remember_runtime_local_type('_or_t1', '_option_int')
1928 gen.remember_runtime_local_type('_or_t2', '_option_int')
1929 gen.gen_stmts([
1930 ast.Stmt(ast.AssignStmt{
1931 op: .decl_assign
1932 lhs: [ast.Expr(ast.Ident{
1933 name: 'lhs_value'
1934 })]
1935 rhs: [
1936 ast.Expr(ast.SelectorExpr{
1937 lhs: ast.Expr(ast.Ident{
1938 name: '_or_t1'
1939 })
1940 rhs: ast.Ident{
1941 name: 'data'
1942 }
1943 }),
1944 ]
1945 }),
1946 ast.Stmt(ast.AssignStmt{
1947 op: .decl_assign
1948 lhs: [
1949 ast.Expr(ast.Ident{
1950 name: 'rhs_value'
1951 }),
1952 ]
1953 rhs: [
1954 ast.Expr(ast.SelectorExpr{
1955 lhs: ast.Expr(ast.Ident{
1956 name: '_or_t2'
1957 })
1958 rhs: ast.Ident{
1959 name: 'data'
1960 }
1961 }),
1962 ]
1963 }),
1964 ast.Stmt(ast.AssignStmt{
1965 op: .decl_assign
1966 lhs: [
1967 ast.Expr(ast.Ident{
1968 name: 'matches'
1969 }),
1970 ]
1971 rhs: [
1972 ast.Expr(ast.InfixExpr{
1973 lhs: ast.Expr(ast.Ident{
1974 name: 'lhs_value'
1975 pos: token.Pos{
1976 id: 1771
1977 }
1978 })
1979 op: .eq
1980 rhs: ast.Expr(ast.Ident{
1981 name: 'rhs_value'
1982 pos: token.Pos{
1983 id: 1772
1984 }
1985 })
1986 }),
1987 ]
1988 }),
1989 ])
1990 out := gen.sb.str()
1991 assert out.contains('int lhs_value ='), out
1992 assert out.contains('int rhs_value ='), out
1993 assert out.contains('lhs_value == rhs_value'), out
1994 assert !out.contains('string__eq(lhs_value, rhs_value)'), out
1995}
1996
1997fn test_if_guard_infix_compare_uses_active_payload_decl_type() {
1998 csrc := cleanc_csrc_for_test_source('if_guard_payload_compare_shadowing', '
1999fn maybe_int() ?int {
2000 return 1
2001}
2002
2003fn maybe_string() ?string {
2004 return "x"
2005}
2006
2007fn check_values() ?bool {
2008 if lhs_value := maybe_int() {
2009 rhs_value := maybe_int() or { return none }
2010 matches := lhs_value == rhs_value
2011 return matches
2012 }
2013 if lhs_value := maybe_string() {
2014 rhs_value := maybe_string() or { return none }
2015 matches := lhs_value == rhs_value
2016 return matches
2017 }
2018 return none
2019}
2020')
2021 assert csrc.contains('int lhs_value ='), csrc
2022 assert csrc.contains('int rhs_value ='), csrc
2023 int_branch := csrc.all_after('int rhs_value =').all_before('_option_string _or_t3')
2024 assert int_branch.contains('lhs_value == rhs_value'), csrc
2025 assert !int_branch.contains('string__eq(lhs_value, rhs_value)'), csrc
2026 string_branch := csrc.all_after('string rhs_value =')
2027 assert string_branch.contains('string__eq(lhs_value, rhs_value)'), csrc
2028}
2029
2030fn test_generic_float_result_interpolation_uses_float_str() {
2031 csrc := cleanc_csrc_for_test_source('generic_float_result_str', '
2032fn identity[T](x T) T {
2033 return x
2034}
2035
2036fn main() {
2037 ret := identity(0.0)
2038 assert "\${ret}" == "0.0"
2039}
2040')
2041 assert csrc.contains('f64 ret ='), csrc
2042 assert csrc.contains('"%s", f64__str(ret).str'), csrc
2043 assert !csrc.contains('int__str(ret)'), csrc
2044 assert !csrc.contains('"%d", ret'), csrc
2045}
2046
2047fn test_generic_abs_int_literal_decl_stays_int() {
2048 csrc := cleanc_csrc_for_test_source('generic_abs_int_literal_decl', '
2049fn abs[T](a T) T {
2050 return if a < 0 { -a } else { a }
2051}
2052
2053fn main() {
2054 ret1 := abs(0)
2055 assert "\${ret1}" == "0"
2056 ret2 := abs(0.0)
2057 assert "\${ret2}" == "0.0"
2058}
2059')
2060 assert csrc.contains('int ret1 ='), csrc
2061 assert csrc.contains('f64 ret2 ='), csrc
2062 assert !csrc.contains('f64 ret1 ='), csrc
2063}
2064
2065fn test_variadic_voidptr_hex_uses_explicit_int_cast_deref_type() {
2066 mut env := types.Environment.new()
2067 deref_pos := token.Pos{
2068 id: 5251
2069 }
2070 env.set_expr_type(deref_pos.id, types.Type(types.f64_))
2071 mut gen := Gen.new_with_env([], env)
2072 typ := gen.get_expr_type(ast.Expr(ast.PrefixExpr{
2073 op: .mul
2074 expr: ast.Expr(ast.PrefixExpr{
2075 op: .amp
2076 expr: ast.Expr(ast.CallOrCastExpr{
2077 lhs: ast.Expr(ast.Ident{
2078 name: 'int'
2079 })
2080 expr: ast.Expr(ast.Ident{
2081 name: 'raw'
2082 })
2083 })
2084 })
2085 pos: deref_pos
2086 }))
2087 assert typ == 'int'
2088}
2089
2090fn test_variadic_voidptr_hex_source_decl_uses_explicit_int_cast_deref_type() {
2091 csrc := cleanc_csrc_for_test_source('variadic_voidptr_hex_cast_deref', '
2092fn format_value(use_int bool, pt ...voidptr) int {
2093 mut p_index := 0
2094 if use_int {
2095 x := unsafe { *(&int(pt[p_index])) }
2096 return x
2097 }
2098 x := unsafe { *(&f64(pt[p_index])) }
2099 _ = x
2100 return 0
2101}
2102')
2103 assert csrc.contains('int x ='), csrc
2104 assert csrc.contains('f64 x ='), csrc
2105 assert !csrc.contains('f64 x = (*((int*)'), csrc
2106}
2107
2108fn test_decl_assign_does_not_use_position_type_for_or_temp() {
2109 mut env := types.Environment.new()
2110 env.set_expr_type(88, types.Type(types.Array{
2111 elem_type: types.string_
2112 }))
2113 mut gen := Gen.new_with_env([], env)
2114 gen.fn_return_types['map__get_check'] = 'void*'
2115 gen.gen_stmt(ast.Stmt(ast.AssignStmt{
2116 op: .decl_assign
2117 lhs: [
2118 ast.Expr(ast.Ident{
2119 pos: token.Pos{
2120 id: 88
2121 }
2122 name: '_or_t1'
2123 }),
2124 ]
2125 rhs: [
2126 ast.Expr(ast.CallExpr{
2127 lhs: ast.Expr(ast.Ident{
2128 name: 'map__get_check'
2129 })
2130 args: [
2131 ast.Expr(ast.Ident{
2132 name: 'm'
2133 }),
2134 ast.Expr(ast.Ident{
2135 name: 'key'
2136 }),
2137 ]
2138 }),
2139 ]
2140 }))
2141 out := gen.sb.str()
2142 assert out.contains('_or_t1 = map__get_check')
2143 assert !out.contains('Array_string _or_t1')
2144}
2145
2146fn test_addr_of_temp_compound_literal_uses_rhs_type_before_stale_scope_type() {
2147 mut gen := Gen.new([])
2148 gen.fn_return_types['string__plus'] = 'string'
2149 gen.runtime_local_types['_or_t_key'] = 'string*'
2150 gen.expr(ast.Expr(ast.UnsafeExpr{
2151 stmts: [
2152 ast.Stmt(ast.AssignStmt{
2153 op: .decl_assign
2154 lhs: [ast.Expr(ast.Ident{
2155 name: '_or_t_key'
2156 })]
2157 rhs: [
2158 ast.Expr(ast.CallExpr{
2159 lhs: ast.Expr(ast.Ident{
2160 name: 'string__plus'
2161 })
2162 }),
2163 ]
2164 }),
2165 ast.Stmt(ast.ExprStmt{
2166 expr: ast.Expr(ast.PrefixExpr{
2167 op: .amp
2168 expr: ast.Expr(ast.Ident{
2169 name: '_or_t_key'
2170 })
2171 })
2172 }),
2173 ]
2174 }))
2175 out := gen.sb.str()
2176 assert out.contains('&((string[1]){')
2177 assert !out.contains('&((string*[1]){')
2178}
2179
2180fn test_at_vexeroot_ident_emits_string_literal() {
2181 mut gen := Gen.new([])
2182 prefs := &vpref.Preferences{
2183 vroot: '/tmp/vroot'
2184 }
2185 gen.pref = prefs
2186 gen.expr(ast.Expr(ast.Ident{
2187 name: '@VEXEROOT'
2188 }))
2189 out := gen.sb.str()
2190 assert out.contains('"/tmp/vroot"')
2191 assert !out.contains('@VEXEROOT')
2192}
2193
2194fn test_as_cast_selector_uses_declared_sum_field_type() {
2195 mut gen := Gen.new([])
2196 gen.sum_type_variants['ast__Expr'] = ['CallExpr', 'AsCast']
2197 gen.struct_field_types['ast__ParExpr.expr'] = 'ast__Expr'
2198 gen.remember_runtime_local_type('par', 'ast__ParExpr')
2199 gen.expr(ast.Expr(ast.SelectorExpr{
2200 lhs: ast.Expr(ast.AsCastExpr{
2201 expr: ast.Expr(ast.SelectorExpr{
2202 lhs: ast.Expr(ast.Ident{
2203 name: 'par'
2204 })
2205 rhs: ast.Ident{
2206 name: 'expr'
2207 }
2208 })
2209 typ: ast.Expr(ast.SelectorExpr{
2210 lhs: ast.Expr(ast.Ident{
2211 name: 'ast'
2212 })
2213 rhs: ast.Ident{
2214 name: 'AsCast'
2215 }
2216 })
2217 })
2218 rhs: ast.Ident{
2219 name: 'typ'
2220 }
2221 }))
2222 out := gen.sb.str()
2223 assert out.contains('par.expr')
2224 assert out.contains('._data._AsCast')
2225 assert !out.contains('._data._CallExpr)._data')
2226}
2227
2228fn test_decl_assign_as_cast_uses_cast_target_before_env_type() {
2229 mut env := types.Environment.new()
2230 env.set_expr_type(91, types.Type(types.Struct{
2231 name: 'ast__Expr'
2232 }))
2233 mut gen := Gen.new_with_env([], env)
2234 gen.sum_type_variants['ast__Expr'] = ['ast__PrefixExpr']
2235 gen.remember_runtime_local_type('node', 'ast__Expr')
2236 gen.gen_assign_stmt(ast.AssignStmt{
2237 op: .decl_assign
2238 lhs: [
2239 ast.Expr(ast.Ident{
2240 name: 'inner'
2241 }),
2242 ]
2243 rhs: [
2244 ast.Expr(ast.AsCastExpr{
2245 expr: ast.Expr(ast.Ident{
2246 name: 'node'
2247 })
2248 typ: ast.Expr(ast.SelectorExpr{
2249 lhs: ast.Expr(ast.Ident{
2250 name: 'ast'
2251 })
2252 rhs: ast.Ident{
2253 name: 'PrefixExpr'
2254 }
2255 })
2256 pos: token.Pos{
2257 id: 91
2258 }
2259 }),
2260 ]
2261 })
2262 out := gen.sb.str().trim_space()
2263 assert out.starts_with('ast__PrefixExpr inner =')
2264 assert !out.contains('ast__Expr inner =')
2265}
2266
2267fn test_cast_expr_sum_variant_extract_uses_declared_selector_field_type() {
2268 mut env := types.Environment.new()
2269 env.set_expr_type(92, types.Type(types.Struct{
2270 name: 'ast__ArrayDecompose'
2271 }))
2272 mut gen := Gen.new_with_env([], env)
2273 gen.sum_type_variants['ast__Expr'] = ['ast__ArrayDecompose']
2274 gen.struct_field_types['ast__CallArg.expr'] = 'ast__Expr'
2275 gen.remember_runtime_local_type('arg', 'ast__CallArg')
2276 gen.expr(ast.Expr(ast.CastExpr{
2277 typ: ast.Expr(ast.SelectorExpr{
2278 lhs: ast.Expr(ast.Ident{
2279 name: 'ast'
2280 })
2281 rhs: ast.Ident{
2282 name: 'ArrayDecompose'
2283 }
2284 })
2285 expr: ast.Expr(ast.SelectorExpr{
2286 lhs: ast.Expr(ast.Ident{
2287 name: 'arg'
2288 })
2289 rhs: ast.Ident{
2290 name: 'expr'
2291 }
2292 pos: token.Pos{
2293 id: 92
2294 }
2295 })
2296 }))
2297 out := gen.sb.str()
2298 assert out.contains('arg.expr')
2299 assert out.contains('._data._ast__ArrayDecompose')
2300 assert !out.contains('((ast__ArrayDecompose)(arg.expr))')
2301
2302 mut ptr_gen := Gen.new([])
2303 ptr_gen.sum_type_variants['ast__Expr'] = ['ast__ArrayDecompose']
2304 ptr_gen.struct_field_types['ast__CallArg.expr'] = 'ast__Expr'
2305 ptr_sel := ast.SelectorExpr{
2306 lhs: ast.Expr(ast.PrefixExpr{
2307 op: .mul
2308 expr: ast.Expr(ast.CastExpr{
2309 typ: ast.Expr(ast.Ident{
2310 name: 'ast__CallArg*'
2311 })
2312 expr: ast.Expr(ast.CallExpr{
2313 lhs: ast.Expr(ast.Ident{
2314 name: 'array__last'
2315 })
2316 args: [
2317 ast.Expr(ast.Ident{
2318 name: 'args'
2319 }),
2320 ]
2321 })
2322 })
2323 })
2324 rhs: ast.Ident{
2325 name: 'expr'
2326 }
2327 }
2328 assert ptr_gen.selector_declared_field_type(ptr_sel) == 'ast__Expr'
2329 ptr_cast := ast.CastExpr{
2330 typ: ast.Expr(ast.SelectorExpr{
2331 lhs: ast.Expr(ast.Ident{
2332 name: 'ast'
2333 })
2334 rhs: ast.Ident{
2335 name: 'ArrayDecompose'
2336 }
2337 })
2338 expr: ast.Expr(ptr_sel)
2339 }
2340 assert ptr_gen.get_sum_type_variants_for('ast__Expr').len == 1
2341 assert ptr_gen.expr_type_to_c(ptr_cast.typ) == 'ast__ArrayDecompose'
2342 assert ptr_gen.cast_expr_is_sum_variant_extract(ptr_cast, 'ast__ArrayDecompose')
2343 mut as_gen := Gen.new([])
2344 as_gen.sum_type_variants['ast__Expr'] = ['ast__ArrayDecompose']
2345 as_gen.struct_field_types['ast__CallArg.expr'] = 'ast__Expr'
2346 as_gen.gen_as_cast_expr(ast.AsCastExpr{
2347 expr: ast.Expr(ptr_sel)
2348 typ: ptr_cast.typ
2349 })
2350 as_out := as_gen.sb.str()
2351 assert as_out.contains('._data._'), as_out
2352 ptr_gen.expr(ast.Expr(ptr_cast))
2353 out2 := ptr_gen.sb.str()
2354 assert out2.contains('._data._'), out2
2355 assert !out2.contains('((ast__ArrayDecompose)((*(ast__CallArg*)array__last(args)).expr))')
2356}
2357
2358fn test_cast_expr_same_aggregate_value_omits_c_struct_cast() {
2359 mut env := types.Environment.new()
2360 env.set_expr_type(93, types.Type(types.Struct{
2361 name: 'ast__ArrayInit'
2362 }))
2363 mut gen := Gen.new_with_env([], env)
2364 gen.expr(ast.Expr(ast.CastExpr{
2365 typ: ast.Expr(ast.Ident{
2366 name: 'ast__ArrayInit'
2367 })
2368 expr: ast.Expr(ast.ParenExpr{
2369 expr: ast.Expr(ast.PrefixExpr{
2370 op: .mul
2371 expr: ast.Expr(ast.CastExpr{
2372 typ: ast.Expr(ast.Ident{
2373 name: 'ast__ArrayInit*'
2374 })
2375 expr: ast.Expr(ast.Ident{
2376 name: 'payload'
2377 })
2378 })
2379 })
2380 pos: token.Pos{
2381 id: 93
2382 }
2383 })
2384 }))
2385 out := gen.sb.str()
2386 assert out.contains('(*((ast__ArrayInit*)'), out
2387 assert !out.contains('((ast__ArrayInit)('), out
2388}
2389
2390fn test_address_of_sum_type_cast_materializes_value() {
2391 mut gen := Gen.new([])
2392 gen.sum_type_variants['ast__Expr'] = ['IfExpr']
2393 gen.sum_type_variants['ast__HashStmtNode'] = ['IfExpr', 'HashStmt']
2394 gen.remember_runtime_local_type('node', 'ast__Expr')
2395 gen.expr(ast.Expr(ast.PrefixExpr{
2396 op: .amp
2397 expr: ast.Expr(ast.CastExpr{
2398 typ: ast.Expr(ast.SelectorExpr{
2399 lhs: ast.Expr(ast.Ident{
2400 name: 'ast'
2401 })
2402 rhs: ast.Ident{
2403 name: 'HashStmtNode'
2404 }
2405 })
2406 expr: ast.Expr(ast.AsCastExpr{
2407 expr: ast.Expr(ast.Ident{
2408 name: 'node'
2409 })
2410 typ: ast.Expr(ast.SelectorExpr{
2411 lhs: ast.Expr(ast.Ident{
2412 name: 'ast'
2413 })
2414 rhs: ast.Ident{
2415 name: 'IfExpr'
2416 }
2417 })
2418 })
2419 })
2420 }))
2421 out := gen.sb.str()
2422 assert out.contains('malloc(sizeof(ast__HashStmtNode))')
2423 assert out.contains('._tag = 0')
2424 assert !out.contains('((ast__HashStmtNode*)(')
2425}
2426
2427fn test_address_of_sum_type_cast_keeps_pointer_cast() {
2428 mut gen := Gen.new([])
2429 gen.sum_type_variants['ast__HashStmtNode'] = ['IfExpr', 'HashStmt']
2430 gen.expr(ast.Expr(ast.PrefixExpr{
2431 op: .amp
2432 expr: ast.Expr(ast.CastExpr{
2433 typ: ast.Expr(ast.SelectorExpr{
2434 lhs: ast.Expr(ast.Ident{
2435 name: 'ast'
2436 })
2437 rhs: ast.Ident{
2438 name: 'HashStmtNode'
2439 }
2440 })
2441 expr: ast.Expr(ast.PrefixExpr{
2442 op: .amp
2443 expr: ast.Expr(ast.Ident{
2444 name: 'node'
2445 })
2446 })
2447 })
2448 }))
2449 out := gen.sb.str()
2450 assert out == '((ast__HashStmtNode*)(&node))'
2451 assert !out.contains('malloc(sizeof(ast__HashStmtNode))')
2452}
2453
2454fn test_cast_expr_from_sum_type_to_variant_extracts_payload() {
2455 mut gen := Gen.new([])
2456 gen.sum_type_variants['ast__Stmt'] = ['ExprStmt', 'ReturnStmt']
2457 gen.remember_runtime_local_type('stmt', 'ast__Stmt')
2458 cast_expr := ast.CastExpr{
2459 typ: ast.Expr(ast.SelectorExpr{
2460 lhs: ast.Expr(ast.Ident{
2461 name: 'ast'
2462 })
2463 rhs: ast.Ident{
2464 name: 'ExprStmt'
2465 }
2466 })
2467 expr: ast.Expr(ast.Ident{
2468 name: 'stmt'
2469 })
2470 }
2471 gen.expr(ast.Expr(cast_expr))
2472 out := gen.sb.str()
2473 assert out.contains('(stmt)._data._ExprStmt')
2474 assert !out.contains('((ast__ExprStmt)(stmt))')
2475}
2476
2477fn test_cast_expr_module_struct_from_pointer_source_emits_pointer_cast() {
2478 mut gen := Gen.new([])
2479 gen.remember_runtime_local_type('source_fn', 'void*')
2480 gen.expr(ast.Expr(ast.CastExpr{
2481 typ: ast.Expr(ast.SelectorExpr{
2482 lhs: ast.Expr(ast.Ident{
2483 name: 'ast'
2484 })
2485 rhs: ast.Ident{
2486 name: 'FnDecl'
2487 }
2488 })
2489 expr: ast.Expr(ast.Ident{
2490 name: 'source_fn'
2491 })
2492 }))
2493 out := gen.sb.str()
2494 assert out == '((ast__FnDecl*)(source_fn))'
2495 assert !out.contains('((ast__FnDecl)(source_fn))')
2496}
2497
2498fn test_cast_expr_from_sum_type_pointer_deref_to_variant_extracts_payload() {
2499 mut gen := Gen.new([])
2500 gen.cur_import_modules['ast'] = 'ast'
2501 gen.sum_type_variants['ast__Stmt'] = ['ExprStmt', 'ReturnStmt']
2502 stmt_ptr_typ := ast.Expr(ast.PrefixExpr{
2503 op: .amp
2504 expr: ast.Expr(ast.SelectorExpr{
2505 lhs: ast.Expr(ast.Ident{
2506 name: 'ast'
2507 })
2508 rhs: ast.Ident{
2509 name: 'Stmt'
2510 }
2511 })
2512 })
2513 assert gen.expr_type_to_c(stmt_ptr_typ).trim_space().trim_right('*') == 'ast__Stmt', gen.expr_type_to_c(stmt_ptr_typ)
2514
2515 cast_expr := ast.CastExpr{
2516 typ: ast.Expr(ast.SelectorExpr{
2517 lhs: ast.Expr(ast.Ident{
2518 name: 'ast'
2519 })
2520 rhs: ast.Ident{
2521 name: 'ExprStmt'
2522 }
2523 })
2524 expr: ast.Expr(ast.PrefixExpr{
2525 op: .mul
2526 expr: ast.Expr(ast.CastExpr{
2527 typ: stmt_ptr_typ
2528 expr: ast.Expr(ast.CallExpr{
2529 lhs: ast.Expr(ast.Ident{
2530 name: 'array__last'
2531 })
2532 args: [ast.Expr(ast.Ident{
2533 name: 'stmts'
2534 })]
2535 })
2536 })
2537 })
2538 }
2539 assert gen.cast_expr_is_sum_variant_extract(cast_expr, 'ast__ExprStmt'), gen.get_expr_type(cast_expr.expr)
2540 gen.expr(ast.Expr(cast_expr))
2541 out := gen.sb.str()
2542 assert out.contains('._data._ExprStmt')
2543 assert !out.contains('((ast__ExprStmt)((*')
2544}
2545
2546fn test_selector_on_cast_from_array_last_extracts_sum_payload() {
2547 mut gen := Gen.new([])
2548 gen.cur_import_modules['ast'] = 'ast'
2549 gen.sum_type_variants['ast__Stmt'] = ['ExprStmt', 'ReturnStmt']
2550 gen.struct_field_types['ast__IfBranch.stmts'] = 'Array_ast__Stmt'
2551 gen.struct_field_types['ast__ExprStmt.typ'] = 'ast__Type'
2552 gen.emitted_types['body_ast__ExprStmt'] = true
2553 gen.remember_runtime_local_type('branch', 'ast__IfBranch')
2554 stmts_expr := ast.Expr(ast.SelectorExpr{
2555 lhs: ast.Expr(ast.Ident{
2556 name: 'branch'
2557 })
2558 rhs: ast.Ident{
2559 name: 'stmts'
2560 }
2561 })
2562 cast_expr := ast.Expr(ast.CastExpr{
2563 typ: ast.Expr(ast.Ident{
2564 name: 'ast__ExprStmt'
2565 })
2566 expr: ast.Expr(ast.PrefixExpr{
2567 op: .mul
2568 expr: ast.Expr(ast.CastExpr{
2569 typ: ast.Expr(ast.Ident{
2570 name: 'ast__Stmtptr'
2571 })
2572 expr: ast.Expr(ast.CallExpr{
2573 lhs: ast.Expr(ast.Ident{
2574 name: 'array__last'
2575 })
2576 args: [stmts_expr]
2577 })
2578 })
2579 })
2580 })
2581 gen.expr(ast.Expr(ast.SelectorExpr{
2582 lhs: cast_expr
2583 rhs: ast.Ident{
2584 name: 'typ'
2585 }
2586 }))
2587 out := gen.sb.str()
2588 assert out.contains('._data._ExprStmt'), out
2589 assert out.contains('->typ') || out.contains('.typ'), out
2590 assert !out.contains('((ast__ExprStmt)((*'), out
2591}
2592
2593fn test_selector_on_call_or_cast_from_array_last_extracts_sum_payload() {
2594 mut gen := Gen.new([])
2595 gen.cur_import_modules['ast'] = 'ast'
2596 gen.sum_type_variants['ast__Stmt'] = ['ExprStmt', 'ReturnStmt']
2597 gen.struct_field_types['ast__IfBranch.stmts'] = 'Array_ast__Stmt'
2598 gen.struct_field_types['ast__ExprStmt.typ'] = 'ast__Type'
2599 gen.emitted_types['body_ast__ExprStmt'] = true
2600 gen.remember_runtime_local_type('branch', 'ast__IfBranch')
2601 stmts_expr := ast.Expr(ast.SelectorExpr{
2602 lhs: ast.Expr(ast.Ident{
2603 name: 'branch'
2604 })
2605 rhs: ast.Ident{
2606 name: 'stmts'
2607 }
2608 })
2609 sum_value := ast.Expr(ast.PrefixExpr{
2610 op: .mul
2611 expr: ast.Expr(ast.CastExpr{
2612 typ: ast.Expr(ast.Ident{
2613 name: 'ast__Stmtptr'
2614 })
2615 expr: ast.Expr(ast.CallExpr{
2616 lhs: ast.Expr(ast.Ident{
2617 name: 'array__last'
2618 })
2619 args: [stmts_expr]
2620 })
2621 })
2622 })
2623 gen.expr(ast.Expr(ast.SelectorExpr{
2624 lhs: ast.Expr(ast.CallOrCastExpr{
2625 lhs: ast.Expr(ast.Ident{
2626 name: 'ast__ExprStmt'
2627 })
2628 expr: sum_value
2629 })
2630 rhs: ast.Ident{
2631 name: 'typ'
2632 }
2633 }))
2634 out := gen.sb.str()
2635 assert out.contains('._data._ExprStmt'), out
2636 assert !out.contains('((ast__ExprStmt)((*'), out
2637}
2638
2639fn test_selector_on_as_cast_from_array_last_extracts_sum_payload() {
2640 mut gen := Gen.new([])
2641 gen.cur_import_modules['ast'] = 'ast'
2642 gen.sum_type_variants['ast__Stmt'] = ['ExprStmt', 'ReturnStmt']
2643 gen.struct_field_types['ast__IfBranch.stmts'] = 'Array_ast__Stmt'
2644 gen.struct_field_types['ast__ExprStmt.typ'] = 'ast__Type'
2645 gen.remember_runtime_local_type('branch', 'ast__IfBranch')
2646 stmts_expr := ast.Expr(ast.SelectorExpr{
2647 lhs: ast.Expr(ast.Ident{
2648 name: 'branch'
2649 })
2650 rhs: ast.Ident{
2651 name: 'stmts'
2652 }
2653 })
2654 sum_value := ast.Expr(ast.PrefixExpr{
2655 op: .mul
2656 expr: ast.Expr(ast.CastExpr{
2657 typ: ast.Expr(ast.Ident{
2658 name: 'ast__Stmtptr'
2659 })
2660 expr: ast.Expr(ast.CallExpr{
2661 lhs: ast.Expr(ast.Ident{
2662 name: 'array__last'
2663 })
2664 args: [stmts_expr]
2665 })
2666 })
2667 })
2668 gen.expr(ast.Expr(ast.SelectorExpr{
2669 lhs: ast.Expr(ast.AsCastExpr{
2670 expr: sum_value
2671 typ: ast.Expr(ast.Ident{
2672 name: 'ast__ExprStmt'
2673 })
2674 })
2675 rhs: ast.Ident{
2676 name: 'typ'
2677 }
2678 }))
2679 out := gen.sb.str()
2680 assert out.contains('._data._ExprStmt'), out
2681 assert out.contains('->typ') || out.contains('.typ'), out
2682 assert !out.contains('((ast__ExprStmt)((*'), out
2683}
2684
2685fn test_as_cast_selector_from_array_last_resolves_source_sum_type() {
2686 mut gen := Gen.new([])
2687 gen.cur_import_modules['ast'] = 'ast'
2688 gen.sum_type_variants['ast__Expr'] = ['ArrayDecompose', 'Ident']
2689 gen.struct_field_types['ast__CallArg.expr'] = 'ast__Expr'
2690 gen.remember_runtime_local_type('args', 'Array_ast__CallArg')
2691 arg_expr := ast.Expr(ast.SelectorExpr{
2692 lhs: ast.Expr(ast.CallExpr{
2693 lhs: ast.Expr(ast.Ident{
2694 name: 'array__last'
2695 })
2696 args: [ast.Expr(ast.Ident{
2697 name: 'args'
2698 })]
2699 })
2700 rhs: ast.Ident{
2701 name: 'expr'
2702 }
2703 })
2704 gen.expr(ast.Expr(ast.AsCastExpr{
2705 expr: arg_expr
2706 typ: ast.Expr(ast.Ident{
2707 name: 'ast__ArrayDecompose'
2708 })
2709 }))
2710 out := gen.sb.str()
2711 assert out.contains('._data._ArrayDecompose'), out
2712 assert !out.contains('((ast__ArrayDecompose)((*'), out
2713}
2714
2715fn test_sum_pointer_arg_uses_declared_selector_storage_despite_narrowed_env_type() {
2716 mut gen := Gen.new([])
2717 gen.sum_type_variants['ast__Expr'] = ['Ident', 'SelectorExpr']
2718 gen.struct_field_types['ast__MatchExpr.cond'] = 'ast__Expr'
2719 gen.remember_runtime_local_type('node', 'ast__MatchExpr*')
2720 gen.fn_param_types['checker__Checker__fail_if_immutable'] = ['ast__Expr*']
2721 gen.fn_param_is_ptr['checker__Checker__fail_if_immutable'] = [true]
2722 cond := ast.SelectorExpr{
2723 lhs: ast.Expr(ast.Ident{
2724 name: 'node'
2725 })
2726 rhs: ast.Ident{
2727 name: 'cond'
2728 }
2729 pos: token.Pos{
2730 id: 81
2731 }
2732 }
2733 gen.selector_field_type_cache[selector_field_type_cache_key(cond)] = 'ast__Ident'
2734 gen.gen_call_arg('checker__Checker__fail_if_immutable', 0, ast.Expr(cond))
2735 out := gen.sb.str()
2736 assert out == '&node->cond', out
2737 assert !out.contains('._tag ='), out
2738 assert !out.contains('._data._Ident'), out
2739}
2740
2741fn test_as_cast_from_sum_type_pointer_deref_keeps_deref_for_payload_access() {
2742 mut gen := Gen.new([])
2743 gen.cur_import_modules['ast'] = 'ast'
2744 gen.sum_type_variants['ast__Stmt'] = ['GlobalDecl', 'FnDecl']
2745 gen.remember_runtime_local_type('stmt_ptr', 'ast__Stmt*')
2746 gen.expr(ast.Expr(ast.AsCastExpr{
2747 expr: ast.Expr(ast.PrefixExpr{
2748 op: .mul
2749 expr: ast.Expr(ast.Ident{
2750 name: 'stmt_ptr'
2751 })
2752 })
2753 typ: ast.Expr(ast.SelectorExpr{
2754 lhs: ast.Expr(ast.Ident{
2755 name: 'ast'
2756 })
2757 rhs: ast.Ident{
2758 name: 'GlobalDecl'
2759 }
2760 })
2761 }))
2762 out := gen.sb.str()
2763 assert out.contains('(*stmt_ptr))._data._GlobalDecl'), out
2764 assert !out.contains('stmt_ptr._data._GlobalDecl'), out
2765}
2766
2767fn test_pipeline_as_cast_from_for_in_addressed_array_element_uses_pointer_payload_access() {
2768 csrc := cleanc_csrc_for_test_source('for_in_as_cast', 'module main
2769
2770struct File {
2771 stmts []Stmt
2772}
2773
2774type Stmt = GlobalDecl | FnDecl
2775
2776struct GlobalDecl {}
2777struct FnDecl {}
2778
2779fn use_global(_ GlobalDecl) {}
2780
2781fn gen_file(file File) {
2782 mut global_indices := []int{}
2783 for gi in global_indices {
2784 stmt_ptr := &file.stmts[gi]
2785 use_global((*stmt_ptr) as GlobalDecl)
2786 }
2787}
2788')
2789 assert !csrc.contains('stmt_ptr._data._GlobalDecl'), csrc
2790 assert csrc.contains('((*stmt_ptr))._data._GlobalDecl')
2791 || csrc.contains('stmt_ptr->_data._GlobalDecl'), csrc
2792}
2793
2794fn test_for_in_over_cloned_u8_array_preserves_element_type() {
2795 mut g := Gen.new([])
2796 g.fn_return_types['array__clone'] = 'array'
2797 g.remember_runtime_local_type('src', 'Array_u8')
2798 g.remember_runtime_local_type('i', 'int')
2799 g.remember_runtime_local_type('sep', 'u8')
2800 g.gen_assign_stmt(ast.AssignStmt{
2801 op: .decl_assign
2802 lhs: [ast.Expr(ast.Ident{
2803 name: 'bytes'
2804 })]
2805 rhs: [
2806 ast.Expr(ast.CallExpr{
2807 lhs: ast.Expr(ast.Ident{
2808 name: 'array__clone'
2809 })
2810 args: [
2811 ast.Expr(ast.PrefixExpr{
2812 op: .amp
2813 expr: ast.Expr(ast.Ident{
2814 name: 'src'
2815 })
2816 }),
2817 ]
2818 }),
2819 ]
2820 })
2821 g.gen_assign_stmt(ast.AssignStmt{
2822 op: .decl_assign
2823 lhs: [ast.Expr(ast.Ident{
2824 name: 'byte'
2825 })]
2826 rhs: [
2827 ast.Expr(ast.IndexExpr{
2828 lhs: ast.Expr(ast.Ident{
2829 name: 'bytes'
2830 })
2831 expr: ast.Expr(ast.Ident{
2832 name: 'i'
2833 })
2834 }),
2835 ]
2836 })
2837 g.gen_assign_stmt(ast.AssignStmt{
2838 op: .assign
2839 lhs: [
2840 ast.Expr(ast.IndexExpr{
2841 lhs: ast.Expr(ast.Ident{
2842 name: 'bytes'
2843 })
2844 expr: ast.Expr(ast.Ident{
2845 name: 'i'
2846 })
2847 }),
2848 ]
2849 rhs: [ast.Expr(ast.Ident{
2850 name: 'sep'
2851 })]
2852 })
2853 csrc := g.sb.str()
2854 assert csrc.contains('Array_u8 bytes = array__clone_to_depth('), csrc
2855 assert csrc.contains('u8 byte = ((u8*)'), csrc
2856 assert csrc.contains('((u8*)bytes.data)[((int)(i))] = sep;'), csrc
2857 assert !csrc.contains('int byte = ((int*)'), csrc
2858 assert !csrc.contains('((int*)bytes.data)[((int)(i))] = sep;'), csrc
2859}
2860
2861fn test_pipeline_sum_variant_cast_from_array_last_call_extracts_payload() {
2862 csrc := cleanc_csrc_for_test_source('array_last_sum_cast', 'module main
2863
2864struct ExprStmt {
2865 typ int
2866}
2867
2868struct ReturnStmt {}
2869
2870type Stmt = ExprStmt | ReturnStmt
2871
2872struct Branch {
2873 stmts []Stmt
2874}
2875
2876fn (a []Stmt) last() Stmt {
2877 return a[0]
2878}
2879
2880fn f(branch Branch) int {
2881 if branch.stmts.len > 0 {
2882 if branch.stmts.last() is ExprStmt {
2883 return (branch.stmts.last() as ExprStmt).typ
2884 }
2885 }
2886 return 0
2887}
2888')
2889 assert csrc.contains('._data._ExprStmt'), csrc
2890 assert !csrc.contains('((ExprStmt)((*(Stmt*)array__last(branch.stmts))))'), csrc
2891 assert !csrc.contains('((main__ExprStmt)((*(main__Stmt*)array__last(branch.stmts))))'), csrc
2892}
2893
2894fn test_pipeline_sum_variant_cast_from_selector_field_extracts_payload() {
2895 csrc := cleanc_csrc_for_test_source('selector_field_sum_cast', 'module main
2896
2897struct ArrayDecompose {
2898 value int
2899}
2900
2901struct Other {}
2902
2903type Expr = ArrayDecompose | Other
2904
2905struct CallArg {
2906 expr Expr
2907}
2908
2909fn (a []CallArg) last() CallArg {
2910 return a[0]
2911}
2912
2913fn f(args []CallArg) int {
2914 if args.len > 0 && args.last().expr is ArrayDecompose {
2915 array_decompose := args.last().expr as ArrayDecompose
2916 return array_decompose.value
2917 }
2918 return 0
2919}
2920')
2921 assert csrc.contains('._data._ArrayDecompose'), csrc
2922 assert !csrc.contains('((ArrayDecompose)((*(CallArg*)array__last(args)).expr))'), csrc
2923 assert !csrc.contains('((main__ArrayDecompose)((*(main__CallArg*)array__last(args)).expr))'), csrc
2924}
2925
2926fn test_deref_expr_type_ignores_stale_pointer_env_type() {
2927 mut env := types.Environment.new()
2928 env.set_expr_type(61, types.Type(types.Pointer{
2929 base_type: types.Struct{
2930 name: 'ast__Stmt'
2931 }
2932 }))
2933 mut gen := Gen.new_with_env([], env)
2934 stmt_ptr_typ := ast.Expr(ast.PrefixExpr{
2935 op: .amp
2936 expr: ast.Expr(ast.SelectorExpr{
2937 lhs: ast.Expr(ast.Ident{
2938 name: 'ast'
2939 })
2940 rhs: ast.Ident{
2941 name: 'Stmt'
2942 }
2943 })
2944 })
2945 deref_expr := ast.Expr(ast.PrefixExpr{
2946 op: .mul
2947 expr: ast.Expr(ast.CastExpr{
2948 typ: stmt_ptr_typ
2949 expr: ast.Expr(ast.Ident{
2950 name: 'ptr'
2951 })
2952 })
2953 pos: token.Pos{
2954 id: 61
2955 }
2956 })
2957 assert gen.get_expr_type(deref_expr) == 'ast__Stmt'
2958 assert !gen.expr_is_pointer(deref_expr)
2959
2960 gen.expr(ast.Expr(ast.SelectorExpr{
2961 lhs: deref_expr
2962 rhs: ast.Ident{
2963 name: '_tag'
2964 }
2965 }))
2966 out := gen.sb.str()
2967 assert out.contains(')._tag')
2968 assert !out.contains(')->_tag')
2969}
2970
2971fn test_sum_cast_from_array_last_uses_value_separator() {
2972 mut gen := Gen.new([])
2973 gen.cur_import_modules['ast'] = 'ast'
2974 gen.sum_type_variants['ast__Stmt'] = ['ExprStmt', 'ReturnStmt']
2975 gen.struct_field_types['ast__IfBranch.stmts'] = 'Array_ast__Stmt'
2976 gen.remember_runtime_local_type('branch', 'ast__IfBranch')
2977 stmts_expr := ast.Expr(ast.SelectorExpr{
2978 lhs: ast.Expr(ast.Ident{
2979 name: 'branch'
2980 })
2981 rhs: ast.Ident{
2982 name: 'stmts'
2983 }
2984 })
2985 last_expr := ast.Expr(ast.CallExpr{
2986 lhs: ast.Expr(ast.Ident{
2987 name: 'array__last'
2988 })
2989 args: [stmts_expr]
2990 })
2991 assert gen.get_expr_type(last_expr) == 'ast__Stmt'
2992 assert !gen.expr_is_pointer(last_expr)
2993
2994 gen.expr(ast.Expr(ast.AsCastExpr{
2995 expr: last_expr
2996 typ: ast.Expr(ast.SelectorExpr{
2997 lhs: ast.Expr(ast.Ident{
2998 name: 'ast'
2999 })
3000 rhs: ast.Ident{
3001 name: 'ExprStmt'
3002 }
3003 })
3004 }))
3005 out := gen.sb.str()
3006 assert out.contains(')._data._ExprStmt')
3007 assert !out.contains(')->_data._ExprStmt')
3008
3009 mut method_gen := Gen.new([])
3010 method_gen.cur_import_modules['ast'] = 'ast'
3011 method_gen.sum_type_variants['ast__Stmt'] = ['ExprStmt', 'ReturnStmt']
3012 method_gen.struct_field_types['ast__IfBranch.stmts'] = 'Array_ast__Stmt'
3013 method_gen.remember_runtime_local_type('branch', 'ast__IfBranch')
3014 method_last_expr := ast.Expr(ast.CallExpr{
3015 lhs: ast.Expr(ast.SelectorExpr{
3016 lhs: stmts_expr
3017 rhs: ast.Ident{
3018 name: 'last'
3019 }
3020 })
3021 })
3022 assert method_gen.get_expr_type(method_last_expr) == 'ast__Stmt'
3023 assert !method_gen.expr_is_pointer(method_last_expr)
3024 method_gen.expr(ast.Expr(ast.AsCastExpr{
3025 expr: method_last_expr
3026 typ: ast.Expr(ast.SelectorExpr{
3027 lhs: ast.Expr(ast.Ident{
3028 name: 'ast'
3029 })
3030 rhs: ast.Ident{
3031 name: 'ExprStmt'
3032 }
3033 })
3034 }))
3035 method_out := method_gen.sb.str()
3036 assert method_out.contains(')._data._ExprStmt')
3037 assert !method_out.contains(')->_data._ExprStmt')
3038
3039 mut env := types.Environment.new()
3040 env.set_expr_type(62, types.Type(types.Pointer{
3041 base_type: types.Struct{
3042 name: 'ast__Stmt'
3043 }
3044 }))
3045 mut env_gen := Gen.new_with_env([], env)
3046 env_gen.cur_import_modules['ast'] = 'ast'
3047 env_gen.sum_type_variants['ast__Stmt'] = ['ExprStmt', 'ReturnStmt']
3048 env_gen.struct_field_types['ast__IfBranch.stmts'] = 'Array_ast__Stmt'
3049 env_gen.remember_runtime_local_type('branch', 'ast__IfBranch')
3050 env_last_expr := ast.Expr(ast.CallExpr{
3051 lhs: ast.Expr(ast.SelectorExpr{
3052 lhs: stmts_expr
3053 rhs: ast.Ident{
3054 name: 'last'
3055 }
3056 })
3057 pos: token.Pos{
3058 id: 62
3059 }
3060 })
3061 assert env_gen.get_expr_type(env_last_expr) == 'ast__Stmt'
3062 assert !env_gen.expr_is_pointer(env_last_expr)
3063 env_gen.expr(ast.Expr(ast.SelectorExpr{
3064 lhs: ast.Expr(ast.AsCastExpr{
3065 expr: env_last_expr
3066 typ: ast.Expr(ast.SelectorExpr{
3067 lhs: ast.Expr(ast.Ident{
3068 name: 'ast'
3069 })
3070 rhs: ast.Ident{
3071 name: 'ExprStmt'
3072 }
3073 })
3074 })
3075 rhs: ast.Ident{
3076 name: 'typ'
3077 }
3078 }))
3079 env_out := env_gen.sb.str()
3080 assert env_out.contains(')._data._ExprStmt')
3081 assert !env_out.contains(')->_data._ExprStmt')
3082}
3083
3084fn test_selector_on_array_last_pointer_element_uses_arrow() {
3085 mut gen := Gen.new([])
3086 gen.struct_field_types['builder__Builder.parsed_files'] = 'Array_ast__Fileptr'
3087 gen.remember_runtime_local_type('v', 'builder__Builder*')
3088 parsed_files := ast.Expr(ast.SelectorExpr{
3089 lhs: ast.Expr(ast.Ident{
3090 name: 'v'
3091 })
3092 rhs: ast.Ident{
3093 name: 'parsed_files'
3094 }
3095 })
3096 last_expr := ast.Expr(ast.CallExpr{
3097 lhs: ast.Expr(ast.Ident{
3098 name: 'array__last'
3099 })
3100 args: [parsed_files]
3101 })
3102 assert gen.infer_array_method_elem_type(last_expr) == 'ast__File*'
3103 assert gen.expr_is_pointer(last_expr)
3104 gen.expr(ast.Expr(ast.SelectorExpr{
3105 lhs: last_expr
3106 rhs: ast.Ident{
3107 name: 'path'
3108 }
3109 }))
3110 out := gen.sb.str()
3111 assert out.contains('array__last(v->parsed_files))->path'), out
3112 assert !out.contains('array__last(v->parsed_files)).path'), out
3113}
3114
3115fn test_if_expr_type_prefers_matching_concrete_branch_types_over_voidptr_env_type() {
3116 mut env := types.Environment.new()
3117 env.set_expr_type(71, types.Type(types.Pointer{
3118 base_type: types.Type(types.void_)
3119 }))
3120 mut gen := Gen.new_with_env([], env)
3121 gen.fn_return_types['next_value_id'] = 'int'
3122 if_expr := ast.IfExpr{
3123 cond: ast.Expr(ast.BasicLiteral{
3124 kind: .key_true
3125 value: 'true'
3126 })
3127 stmts: [
3128 ast.Stmt(ast.ExprStmt{
3129 expr: ast.Expr(ast.CallExpr{
3130 lhs: ast.Expr(ast.Ident{
3131 name: 'next_value_id'
3132 })
3133 })
3134 }),
3135 ]
3136 else_expr: ast.Expr(ast.CallExpr{
3137 lhs: ast.Expr(ast.Ident{
3138 name: 'next_value_id'
3139 })
3140 })
3141 pos: token.Pos{
3142 id: 71
3143 }
3144 }
3145 assert gen.get_if_expr_type(&if_expr) == 'int'
3146}
3147
3148fn test_if_expr_value_wraps_concrete_branch_for_sum_type_temp() {
3149 mut env := types.Environment.new()
3150 env.set_expr_type(81, types.Type(types.SumType{
3151 name: 'ast__Expr'
3152 variants: [
3153 types.Type(types.Struct{
3154 name: 'ast__SqlExpr'
3155 }),
3156 ]
3157 }))
3158 mut gen := Gen.new_with_env([], env)
3159 gen.sum_type_variants['ast__Expr'] = ['ast__SqlExpr']
3160 gen.remember_runtime_local_type('sql_expr', 'ast__SqlExpr')
3161 gen.remember_runtime_local_type('empty_expr', 'ast__Expr')
3162 if_expr := ast.IfExpr{
3163 cond: ast.Expr(ast.BasicLiteral{
3164 kind: .key_true
3165 value: 'true'
3166 })
3167 stmts: [
3168 ast.Stmt(ast.ExprStmt{
3169 expr: ast.Expr(ast.Ident{
3170 name: 'sql_expr'
3171 })
3172 }),
3173 ]
3174 else_expr: ast.Expr(ast.Ident{
3175 name: 'empty_expr'
3176 })
3177 pos: token.Pos{
3178 id: 81
3179 }
3180 }
3181 gen.gen_if_expr_value(&if_expr)
3182 out := gen.sb.str()
3183 assert out.contains('ast__Expr _if_expr_t')
3184 assert out.contains('._tag = 0')
3185 assert !out.contains('_if_expr_t0 = sql_expr;')
3186}
3187
3188fn test_selector_field_smartcast_uses_declared_sum_type_payload() {
3189 mut env := types.Environment.new()
3190 env.set_expr_type(63, types.Type(types.Struct{
3191 name: 'ast__Ident'
3192 }))
3193 mut gen := Gen.new_with_env([], env)
3194 gen.sum_type_variants['ast__Expr'] = ['Ident', 'IntegerLiteral']
3195 gen.struct_field_types['ast__EnumField.expr'] = 'ast__Expr'
3196 gen.struct_field_types['ast__Ident.language'] = 'ast__Language'
3197 gen.struct_field_types['ast__Ident.kind'] = 'ast__IdentKind'
3198 gen.remember_runtime_local_type('field', 'ast__EnumField*')
3199 field_expr := ast.Expr(ast.SelectorExpr{
3200 lhs: ast.Expr(ast.Ident{
3201 name: 'field'
3202 })
3203 rhs: ast.Ident{
3204 name: 'expr'
3205 }
3206 pos: token.Pos{
3207 id: 63
3208 }
3209 })
3210 assert gen.selector_declared_field_type(field_expr as ast.SelectorExpr) == 'ast__Expr'
3211
3212 gen.expr(ast.Expr(ast.SelectorExpr{
3213 lhs: field_expr
3214 rhs: ast.Ident{
3215 name: 'language'
3216 }
3217 }))
3218 out := gen.sb.str()
3219 assert out.contains('(field->expr)._data._Ident')
3220 assert out.contains('->language')
3221 assert !out.contains('field->expr.language')
3222
3223 mut qualified_gen := Gen.new_with_env([], env)
3224 qualified_gen.sum_type_variants['ast__Expr'] = ['ast__Ident', 'ast__IntegerLiteral']
3225 qualified_gen.struct_field_types['ast__EnumField.expr'] = 'ast__Expr'
3226 qualified_gen.struct_field_types['ast__Ident.language'] = 'ast__Language'
3227 qualified_gen.remember_runtime_local_type('field', 'ast__EnumField*')
3228 qualified_gen.expr(ast.Expr(ast.SelectorExpr{
3229 lhs: field_expr
3230 rhs: ast.Ident{
3231 name: 'language'
3232 }
3233 }))
3234 qualified_out := qualified_gen.sb.str()
3235 assert qualified_out.contains('->language')
3236 assert !qualified_out.contains('field->expr.language')
3237
3238 mut no_env_gen := Gen.new([])
3239 no_env_gen.sum_type_variants['ast__Expr'] = ['Ident', 'IntegerLiteral']
3240 no_env_gen.struct_field_types['ast__EnumField.expr'] = 'ast__Expr'
3241 no_env_gen.struct_field_types['ast__Ident.language'] = 'ast__Language'
3242 no_env_gen.remember_runtime_local_type('field', 'ast__EnumField*')
3243 no_env_gen.expr(ast.Expr(ast.SelectorExpr{
3244 lhs: field_expr
3245 rhs: ast.Ident{
3246 name: 'language'
3247 }
3248 }))
3249 no_env_out := no_env_gen.sb.str()
3250 assert no_env_out.contains('->language')
3251 assert !no_env_out.contains('field->expr.language')
3252
3253 mut const_field_gen := Gen.new([])
3254 const_field_gen.sum_type_variants['ast__Expr'] = ['Ident', 'IntegerLiteral']
3255 const_field_gen.struct_field_types['ast__ConstField.expr'] = 'ast__Expr'
3256 const_field_gen.struct_field_types['ast__Ident.language'] = 'ast__Language'
3257 const_field_gen.remember_runtime_local_type('field', 'ast__ConstField*')
3258 const_field_gen.expr(ast.Expr(ast.SelectorExpr{
3259 lhs: field_expr
3260 rhs: ast.Ident{
3261 name: 'language'
3262 }
3263 }))
3264 const_field_out := const_field_gen.sb.str()
3265 assert const_field_out.contains('->language')
3266 assert !const_field_out.contains('field->expr.language')
3267}
3268
3269fn test_selector_method_receiver_prefers_declared_field_type_over_env_fn_type() {
3270 mut env := types.Environment.new()
3271 env.set_expr_type(91, types.Type(types.FnType{}))
3272 env.set_expr_type(92, types.Type(types.FnType{}))
3273 mut gen := Gen.new_with_env([], env)
3274 gen.struct_field_types['Holder.typ'] = 'ast__Type'
3275 gen.remember_runtime_local_type('holder', 'Holder')
3276 gen.fn_return_types['ast__Type__is_ptr'] = 'bool'
3277 gen.fn_param_types['ast__Type__is_ptr'] = ['ast__Type']
3278 gen.fn_param_is_ptr['ast__Type__is_ptr'] = [false]
3279 receiver := ast.SelectorExpr{
3280 lhs: ast.Expr(ast.Ident{
3281 name: 'holder'
3282 })
3283 rhs: ast.Ident{
3284 name: 'typ'
3285 }
3286 pos: token.Pos{
3287 id: 91
3288 }
3289 }
3290 assert gen.method_receiver_base_type(ast.Expr(receiver)) == 'ast__Type'
3291 gen.call_expr(ast.Expr(ast.SelectorExpr{
3292 lhs: ast.Expr(receiver)
3293 rhs: ast.Ident{
3294 name: 'is_ptr'
3295 }
3296 pos: token.Pos{
3297 id: 92
3298 }
3299 }), [])
3300 out := gen.sb.str()
3301 assert out == 'ast__Type__is_ptr(holder.typ)'
3302 assert !out.contains('((bool(*)())')
3303}
3304
3305fn test_selector_method_receiver_unwraps_address_of_local_before_env_fallback() {
3306 mut env := types.Environment.new()
3307 env.set_expr_type(99, types.Type(types.Pointer{
3308 base_type: types.Type(types.Struct{
3309 name: 'printer__JSONSink'
3310 })
3311 }))
3312 mut gen := Gen.new_with_env([], env)
3313 gen.remember_runtime_local_type('sink', 'printer__SummarySink')
3314 gen.fn_return_types['printer__SummarySink__stats'] = '_option_printer__Statsptr'
3315 gen.fn_param_types['printer__SummarySink__stats'] = ['printer__SummarySink*']
3316 gen.fn_param_is_ptr['printer__SummarySink__stats'] = [true]
3317 gen.fn_return_types['printer__JSONSink__stats'] = 'printer__Stats*'
3318 gen.fn_param_types['printer__JSONSink__stats'] = ['printer__JSONSink*']
3319 gen.fn_param_is_ptr['printer__JSONSink__stats'] = [true]
3320 receiver := ast.Expr(ast.ParenExpr{
3321 expr: ast.Expr(ast.PrefixExpr{
3322 op: token.Token.amp
3323 expr: ast.Expr(ast.Ident{
3324 name: 'sink'
3325 })
3326 })
3327 pos: token.Pos{
3328 id: 99
3329 }
3330 })
3331 assert gen.method_receiver_base_type(receiver) == 'printer__SummarySink'
3332 gen.call_expr(ast.Expr(ast.SelectorExpr{
3333 lhs: receiver
3334 rhs: ast.Ident{
3335 name: 'stats'
3336 }
3337 }), [])
3338 out := gen.sb.str()
3339 assert out.starts_with('printer__SummarySink__stats(')
3340 assert !out.contains('printer__JSONSink__stats')
3341}
3342
3343fn test_array_selector_clone_does_not_emit_fn_pointer_field_call() {
3344 mut env := types.Environment.new()
3345 env.set_expr_type(94, types.Type(types.FnType{}))
3346 mut gen := Gen.new_with_env([], env)
3347 gen.struct_field_types['Holder.types'] = 'Array_ast__Type'
3348 gen.remember_runtime_local_type('holder', 'Holder')
3349 receiver := ast.SelectorExpr{
3350 lhs: ast.Expr(ast.Ident{
3351 name: 'holder'
3352 })
3353 rhs: ast.Ident{
3354 name: 'types'
3355 }
3356 }
3357 gen.call_expr(ast.Expr(ast.SelectorExpr{
3358 lhs: ast.Expr(receiver)
3359 rhs: ast.Ident{
3360 name: 'clone'
3361 }
3362 pos: token.Pos{
3363 id: 94
3364 }
3365 }), [])
3366 out := gen.sb.str()
3367 assert out == 'array__clone_to_depth((array*)&(holder.types), 0)'
3368 assert !out.contains('.clone')
3369 assert !out.contains('((Array_ast__Type(*)())')
3370}
3371
3372fn test_map_selector_clone_uses_builtin_map_clone() {
3373 mut env := types.Environment.new()
3374 env.set_expr_type(98, types.Type(types.FnType{}))
3375 mut gen := Gen.new_with_env([], env)
3376 gen.struct_field_types['Holder.cache'] = 'Map_string_ssa__TypeID'
3377 gen.remember_runtime_local_type('holder', 'Holder')
3378 gen.fn_return_types['map__clone'] = 'map'
3379 gen.fn_param_types['map__clone'] = ['map*']
3380 gen.fn_param_is_ptr['map__clone'] = [true]
3381 receiver := ast.SelectorExpr{
3382 lhs: ast.Expr(ast.Ident{
3383 name: 'holder'
3384 })
3385 rhs: ast.Ident{
3386 name: 'cache'
3387 }
3388 }
3389 gen.call_expr(ast.Expr(ast.SelectorExpr{
3390 lhs: ast.Expr(receiver)
3391 rhs: ast.Ident{
3392 name: 'clone'
3393 }
3394 pos: token.Pos{
3395 id: 98
3396 }
3397 }), [])
3398 out := gen.sb.str()
3399 assert out.starts_with('map__clone(')
3400 assert out.contains('holder.cache')
3401 assert !out.contains('Map_string_ssa__TypeID__clone')
3402}
3403
3404fn test_lowered_array_push_single_array_arg_emits_push_many() {
3405 mut gen := Gen.new([])
3406 gen.remember_runtime_local_type('matches', 'Array_usize')
3407 gen.remember_runtime_local_type('hits', 'Array_usize')
3408 gen.fn_param_types['array__push'] = ['array*', 'voidptr']
3409 gen.fn_param_is_ptr['array__push'] = [true, true]
3410 gen.call_expr(ast.Expr(ast.Ident{
3411 name: 'array__push'
3412 }), [
3413 ast.Expr(ast.Ident{
3414 name: 'matches'
3415 }),
3416 ast.Expr(ast.ArrayInitExpr{
3417 typ: ast.Expr(ast.Type(ast.ArrayType{
3418 elem_type: ast.Expr(ast.Ident{
3419 name: 'usize'
3420 })
3421 }))
3422 exprs: [
3423 ast.Expr(ast.Ident{
3424 name: 'hits'
3425 }),
3426 ]
3427 }),
3428 ])
3429 out := gen.sb.str()
3430 assert out.contains('array__push_many(')
3431 assert out.contains('_arr_push_many_tmp_')
3432 assert out.contains('.data')
3433 assert !out.contains('&(usize[1]){hits}')
3434}
3435
3436fn test_array_selector_clone_on_rvalue_receiver_uses_temp() {
3437 mut env := types.Environment.new()
3438 env.set_expr_type(96, types.Type(types.FnType{}))
3439 mut gen := Gen.new_with_env([], env)
3440 gen.struct_field_types['Holder.types'] = 'Array_ast__Type'
3441 gen.sum_type_variants['Node'] = ['Holder']
3442 gen.remember_runtime_local_type('node', 'Node')
3443 receiver := ast.SelectorExpr{
3444 lhs: ast.Expr(ast.AsCastExpr{
3445 expr: ast.Expr(ast.Ident{
3446 name: 'node'
3447 })
3448 typ: ast.Expr(ast.Ident{
3449 name: 'Holder'
3450 })
3451 })
3452 rhs: ast.Ident{
3453 name: 'types'
3454 }
3455 }
3456 gen.call_expr(ast.Expr(ast.SelectorExpr{
3457 lhs: ast.Expr(receiver)
3458 rhs: ast.Ident{
3459 name: 'clone'
3460 }
3461 pos: token.Pos{
3462 id: 96
3463 }
3464 }), [])
3465 out := gen.sb.str()
3466 assert out.starts_with('({ Array_ast__Type _arr_clone_tmp_')
3467 assert out.contains('; array__clone_to_depth((array*)&_arr_clone_tmp_')
3468 assert !out.contains('&((((node)._data._Holder)')
3469 assert !out.contains('&(((node)._data._Holder)')
3470}
3471
3472fn test_array_selector_clone_on_map_index_receiver_uses_typed_map_get_temp() {
3473 mut env := types.Environment.new()
3474 env.set_expr_type(97, types.Type(types.FnType{}))
3475 mut gen := Gen.new_with_env([], env)
3476 gen.remember_runtime_local_type('m', 'Map_string_Array_Map_string_types__Type')
3477 gen.remember_runtime_local_type('key', 'string')
3478 receiver := ast.IndexExpr{
3479 lhs: ast.Expr(ast.Ident{
3480 name: 'm'
3481 })
3482 expr: ast.Expr(ast.Ident{
3483 name: 'key'
3484 })
3485 }
3486 gen.call_expr(ast.Expr(ast.SelectorExpr{
3487 lhs: ast.Expr(receiver)
3488 rhs: ast.Ident{
3489 name: 'clone'
3490 }
3491 pos: token.Pos{
3492 id: 97
3493 }
3494 }), [])
3495 out := gen.sb.str()
3496 assert out.starts_with('({ Array_Map_string_types__Type _arr_clone_tmp_')
3497 assert out.contains('map__get(&(m), (void*)&_map_key')
3498 assert out.contains('; array__clone_to_depth((array*)&_arr_clone_tmp_')
3499 assert !out.contains('cannot resolve map type for index expr')
3500 assert !out.contains('array__clone_to_depth((array*)&((')
3501}
3502
3503fn test_array_selector_contains_does_not_emit_fn_pointer_field_call() {
3504 mut env := types.Environment.new()
3505 env.set_expr_type(95, types.Type(types.FnType{}))
3506 mut gen := Gen.new_with_env([], env)
3507 gen.struct_field_types['Holder.types'] = 'Array_ast__Type'
3508 gen.remember_runtime_local_type('holder', 'Holder')
3509 gen.remember_runtime_local_type('typ', 'ast__Type')
3510 receiver := ast.SelectorExpr{
3511 lhs: ast.Expr(ast.Ident{
3512 name: 'holder'
3513 })
3514 rhs: ast.Ident{
3515 name: 'types'
3516 }
3517 }
3518 gen.call_expr(ast.Expr(ast.SelectorExpr{
3519 lhs: ast.Expr(receiver)
3520 rhs: ast.Ident{
3521 name: 'contains'
3522 }
3523 pos: token.Pos{
3524 id: 95
3525 }
3526 }), [
3527 ast.Expr(ast.Ident{
3528 name: 'typ'
3529 }),
3530 ])
3531 out := gen.sb.str()
3532 assert out == 'array__contains(holder.types, &typ)'
3533 assert !out.contains('.contains')
3534 assert !out.contains('((bool(*)())')
3535}
3536
3537fn test_fixed_array_contains_call_is_inlined() {
3538 mut gen := Gen.new([])
3539 gen.remember_runtime_local_type('chars', 'Array_fixed_rune_4')
3540 gen.remember_runtime_local_type('ch', 'rune')
3541 gen.call_expr(ast.Expr(ast.Ident{
3542 name: 'Array_fixed_rune_4_contains'
3543 }), [
3544 ast.Expr(ast.Ident{
3545 name: 'chars'
3546 }),
3547 ast.Expr(ast.Ident{
3548 name: 'ch'
3549 }),
3550 ])
3551 out := gen.sb.str()
3552 assert out.contains('for (int _i = 0; _i < 4; _i++)')
3553 assert out.contains('chars[_i] == ch')
3554 assert !out.contains('Array_fixed_rune_4_contains(')
3555}
3556
3557fn test_raw_array_for_in_stmt_uses_typed_runtime_local() {
3558 mut gen := Gen.new([])
3559 gen.remember_runtime_local_type('items', 'Array_int')
3560 gen.remember_runtime_local_type('total', 'int')
3561 gen.gen_for_stmt(ast.ForStmt{
3562 init: ast.ForInStmt{
3563 value: ast.Expr(ast.Ident{
3564 name: 'item'
3565 })
3566 expr: ast.Expr(ast.Ident{
3567 name: 'items'
3568 })
3569 }
3570 stmts: [
3571 ast.Stmt(ast.AssignStmt{
3572 op: .plus_assign
3573 lhs: [ast.Expr(ast.Ident{
3574 name: 'total'
3575 })]
3576 rhs: [ast.Expr(ast.Ident{
3577 name: 'item'
3578 })]
3579 }),
3580 ]
3581 })
3582 out := gen.sb.str()
3583 assert out.contains('Array_int _arr_iter_')
3584 assert out.contains('int item = ((int*)_arr_iter_')
3585 assert out.contains('total += item;')
3586 assert !out.contains('for (;;) {')
3587}
3588
3589fn test_local_map_or_lowers_without_result_temp() {
3590 csrc := cleanc_csrc_for_test_source('local_map_or', '
3591fn local_map_defaults() int {
3592 mut indegree := map[int]int{}
3593 mut dependents := map[int][]int{}
3594 dep := dependents[0] or { []int{} }
3595 indegree[0] = (indegree[0] or { 0 }) + 1
3596 return dep.len + (indegree[0] or { 0 })
3597}
3598')
3599 assert csrc.contains('map__get')
3600 assert !csrc.contains('Array_int _or_t')
3601 assert !csrc.contains('.is_error')
3602 assert !csrc.contains('IError err = _or_t')
3603}
3604
3605fn test_nested_array_for_in_from_map_value_lowers_without_infinite_loop() {
3606 csrc := cleanc_csrc_for_test_source('nested_map_array_for_in', '
3607fn sum_stores(alloca_stores map[int][]int) int {
3608 mut total := 0
3609 for _, store_ids in alloca_stores {
3610 for store_id in store_ids {
3611 total += store_id
3612 }
3613 }
3614 return total
3615}
3616')
3617 assert csrc.contains('Array_int store_ids')
3618 assert csrc.contains('int store_id')
3619 assert !csrc.contains('for (;;) {')
3620}
3621
3622fn test_new_array_from_c_array_elem_type_uses_sizeof_arg() {
3623 mut gen := Gen.new([])
3624 elem := gen.infer_array_elem_type_from_expr(ast.Expr(ast.CallExpr{
3625 lhs: ast.Expr(ast.Ident{
3626 name: 'new_array_from_c_array'
3627 })
3628 args: [
3629 ast.Expr(ast.BasicLiteral{
3630 value: '4'
3631 kind: .number
3632 }),
3633 ast.Expr(ast.BasicLiteral{
3634 value: '4'
3635 kind: .number
3636 }),
3637 ast.Expr(ast.KeywordOperator{
3638 op: .key_sizeof
3639 exprs: [
3640 ast.Expr(ast.Ident{
3641 name: 'u8'
3642 }),
3643 ]
3644 }),
3645 ast.Expr(ast.PrefixExpr{
3646 op: .amp
3647 expr: ast.Expr(ast.Ident{
3648 name: 'msg'
3649 })
3650 }),
3651 ]
3652 }))
3653 assert elem == 'u8'
3654}
3655
3656fn test_struct_fn_type_field_emits_callable_c_field() {
3657 mut env := types.Environment.new()
3658 mut http_scope := types.new_scope(unsafe { nil })
3659 http_scope.insert('Server', types.Type(types.Struct{
3660 name: 'http__Server'
3661 }))
3662 lock env.scopes {
3663 env.scopes['http'] = http_scope
3664 }
3665 mut gen := Gen.new_with_env([], env)
3666 gen.cur_module = 'http'
3667 gen.gen_struct_decl(ast.StructDecl{
3668 name: 'Server'
3669 fields: [
3670 ast.FieldDecl{
3671 name: 'on_closed'
3672 typ: ast.Expr(ast.Type(ast.FnType{
3673 params: [
3674 ast.Parameter{
3675 name: 's'
3676 typ: ast.Expr(ast.Ident{
3677 name: 'Server'
3678 })
3679 is_mut: true
3680 },
3681 ]
3682 }))
3683 },
3684 ]
3685 })
3686 out := gen.sb.str()
3687 assert out.contains('void (*on_closed)(http__Server*);')
3688 assert !out.contains('void* on_closed;')
3689}
3690
3691fn test_struct_decl_emits_late_dynamic_array_alias() {
3692 mut gen := Gen.new([])
3693 gen.gen_struct_decl(ast.StructDecl{
3694 name: 'Holder'
3695 fields: [
3696 ast.FieldDecl{
3697 name: 'items'
3698 typ: ast.Expr(ast.Type(ast.ArrayType{
3699 elem_type: ast.Expr(ast.Ident{
3700 name: 'Foo'
3701 })
3702 }))
3703 },
3704 ]
3705 })
3706 out := gen.sb.str()
3707 alias_pos := out.index('typedef array Array_Foo;') or {
3708 assert false, out
3709 return
3710 }
3711 struct_pos := out.index('struct Holder {') or {
3712 assert false, out
3713 return
3714 }
3715 assert alias_pos < struct_pos
3716 assert out.contains('\tArray_Foo items;')
3717}
3718
3719fn test_struct_dependency_order_uses_fixed_array_element_type() {
3720 csrc := cleanc_csrc_for_test_source('fixed_array_struct_order', 'module main
3721
3722struct Holder {
3723 matrix Matrix
3724}
3725
3726struct Cell {
3727 x f32
3728}
3729
3730struct Matrix {
3731 cols [4]Cell
3732}
3733
3734fn main() {}
3735')
3736 matrix_pos := csrc.index('struct Matrix {') or {
3737 assert false, csrc
3738 return
3739 }
3740 holder_pos := csrc.index('struct Holder {') or {
3741 assert false, csrc
3742 return
3743 }
3744 assert matrix_pos < holder_pos
3745 assert csrc.contains('\tCell cols[4];')
3746}
3747
3748fn test_selector_method_receiver_uses_sum_common_field_type() {
3749 mut env := types.Environment.new()
3750 env.set_expr_type(93, types.Type(types.FnType{}))
3751 mut gen := Gen.new_with_env([], env)
3752 gen.sum_type_variants['ast__ScopeObject'] = ['EmptyScopeObject', 'Var']
3753 gen.struct_field_types['ast__EmptyScopeObject.typ'] = 'ast__Type'
3754 gen.struct_field_types['EmptyScopeObject.typ'] = 'ast__Type'
3755 gen.struct_field_types['ast__Var.typ'] = 'ast__Type'
3756 gen.struct_field_types['Var.typ'] = 'ast__Type'
3757 gen.remember_runtime_local_type('obj', 'ast__ScopeObject')
3758 gen.fn_return_types['ast__Type__is_ptr'] = 'bool'
3759 gen.fn_param_types['ast__Type__is_ptr'] = ['ast__Type']
3760 gen.fn_param_is_ptr['ast__Type__is_ptr'] = [false]
3761 receiver := ast.SelectorExpr{
3762 lhs: ast.Expr(ast.Ident{
3763 name: 'obj'
3764 })
3765 rhs: ast.Ident{
3766 name: 'typ'
3767 }
3768 }
3769 assert gen.method_receiver_base_type(ast.Expr(receiver)) == 'ast__Type'
3770 gen.call_expr(ast.Expr(ast.SelectorExpr{
3771 lhs: ast.Expr(receiver)
3772 rhs: ast.Ident{
3773 name: 'is_ptr'
3774 }
3775 pos: token.Pos{
3776 id: 93
3777 }
3778 }), [])
3779 out := gen.sb.str()
3780 assert out.contains('ast__Type__is_ptr(')
3781 assert out.contains('switch (_sum_cf')
3782 assert !out.contains('((bool(*)())')
3783}
3784
3785fn test_call_arg_auto_derefs_runtime_local_pointer_for_value_param() {
3786 mut gen := Gen.new([])
3787 gen.fn_param_types['use_arg'] = ['ast__AsmArg']
3788 gen.remember_runtime_local_type('arg', 'ast__AsmArg*')
3789 gen.gen_call_arg('use_arg', 0, ast.Ident{
3790 name: 'arg'
3791 })
3792 assert gen.sb.str() == '(*arg)'
3793}
3794
3795fn test_selector_array_field_is_array_value() {
3796 mut gen := Gen.new([])
3797 gen.struct_field_types['Holder.values'] = 'Array_int'
3798 gen.remember_runtime_local_type('holder', 'Holder*')
3799 sel := ast.SelectorExpr{
3800 lhs: ast.Expr(ast.Ident{
3801 name: 'holder'
3802 })
3803 rhs: ast.Ident{
3804 name: 'values'
3805 }
3806 }
3807 assert gen.expr_is_array_value(ast.Expr(sel))
3808 assert gen.expr_array_runtime_type(ast.Expr(sel)) == 'Array_int'
3809}
3810
3811fn test_fixed_array_elem_type_ready_accepts_primitive_alias() {
3812 mut g := Gen.new([])
3813 g.primitive_type_aliases['sha3__Lane'] = true
3814 assert g.fixed_array_elem_type_ready('sha3__Lane')
3815}
3816
3817fn test_fixed_array_elem_type_ready_waits_for_alias_base() {
3818 mut g := Gen.new([])
3819 g.alias_base_types['foo__Alias'] = 'foo__Thing'
3820 assert !g.fixed_array_elem_type_ready('foo__Alias')
3821 g.emitted_types['body_foo__Thing'] = true
3822 assert g.fixed_array_elem_type_ready('foo__Alias')
3823}
3824
3825fn test_types_type_to_c_prefixes_declared_c_structs() {
3826 mut g := Gen.new([])
3827 g.c_struct_types['kevent'] = true
3828 assert g.types_type_to_c(types.Type(types.Struct{
3829 name: 'kevent'
3830 })) == 'struct kevent'
3831
3832 g.typedef_c_types['kevent'] = true
3833 assert g.types_type_to_c(types.Type(types.Struct{
3834 name: 'kevent'
3835 })) == 'kevent'
3836}
3837
3838fn test_specialized_generic_named_pointer_token_resolves_to_c_pointer_type() {
3839 mut g := Gen.new([
3840 ast.File{
3841 stmts: [
3842 ast.Stmt(ast.ModuleStmt{
3843 name: 'ast'
3844 }),
3845 ast.Stmt(ast.StructDecl{
3846 name: 'File'
3847 }),
3848 ]
3849 },
3850 ])
3851 assert g.types_type_to_c(types.Type(types.NamedType('ast_Fileptr'))) == 'ast__File*'
3852 assert g.types_type_to_c(types.Type(types.Struct{
3853 name: 'ast_Fileptr'
3854 })) == 'ast__File*'
3855
3856 get_item := ast.FnDecl{
3857 name: 'get_item'
3858 is_method: true
3859 receiver: ast.Parameter{
3860 name: 'pool'
3861 typ: ast.Expr(ast.Ident{
3862 name: 'PoolProcessor'
3863 })
3864 }
3865 typ: ast.FnType{
3866 generic_params: [
3867 ast.Expr(ast.Ident{
3868 name: 'T'
3869 }),
3870 ]
3871 params: [
3872 ast.Parameter{
3873 name: 'idx'
3874 typ: ast.Expr(ast.Ident{
3875 name: 'int'
3876 })
3877 },
3878 ]
3879 return_type: ast.Expr(ast.Ident{
3880 name: 'T'
3881 })
3882 }
3883 language: .v
3884 }
3885 g.active_generic_types['T'] = types.Type(types.NamedType('ast_Fileptr'))
3886 g.register_fn_signature(get_item, 'pool__PoolProcessor__get_item_T_ast_Fileptr')
3887 assert g.fn_return_types['pool__PoolProcessor__get_item_T_ast_Fileptr'] == 'ast__File*'
3888}
3889
3890fn test_generic_struct_instance_lookup_mangles_c_struct_type_keys() {
3891 mut g := Gen.new([])
3892 g.generic_struct_instances['Box'] = [
3893 GenericStructInstance{
3894 params_key: 'int'
3895 c_name: 'Box'
3896 },
3897 GenericStructInstance{
3898 params_key: 'struct_cJSON'
3899 c_name: 'Box_T_struct_cJSON'
3900 },
3901 ]
3902
3903 resolved := g.resolve_generic_struct_c_name('Box', [
3904 ast.Expr(ast.SelectorExpr{
3905 lhs: ast.Expr(ast.Ident{
3906 name: 'C'
3907 })
3908 rhs: ast.Ident{
3909 name: 'cJSON'
3910 }
3911 }),
3912 ])
3913 assert resolved == 'Box_T_struct_cJSON'
3914}
3915
3916fn test_cache_generic_concrete_origin_accepts_unqualified_declared_type() {
3917 mut g := Gen.new([
3918 ast.File{
3919 stmts: [
3920 ast.Stmt(ast.ModuleStmt{
3921 name: 'main'
3922 }),
3923 ast.Stmt(ast.StructDecl{
3924 name: 'FileInfo'
3925 }),
3926 ]
3927 },
3928 ])
3929 g.cache_bundle_name = 'virtuals'
3930 g.emit_modules['main'] = true
3931 g.type_modules['main'] = true
3932
3933 assert g.generic_concrete_c_name_belongs_to_emit_modules('FileInfo')
3934 assert g.generic_concrete_c_name_belongs_to_emit_modules('Array_FileInfo')
3935 assert g.generic_specialization_belongs_to_emit_modules({
3936 'T': types.Type(types.Array{
3937 elem_type: types.Type(types.Struct{
3938 name: 'FileInfo'
3939 })
3940 })
3941 })
3942}
3943
3944fn test_cache_generic_concrete_origin_rejects_unqualified_name_from_qualified_module() {
3945 mut env := types.Environment.new()
3946 mut veb_scope := types.new_scope(unsafe { nil })
3947 veb_scope.insert('Context', types.Type(types.Struct{
3948 name: 'veb__Context'
3949 }))
3950 lock env.scopes {
3951 env.scopes['veb'] = veb_scope
3952 }
3953 mut g := Gen.new_with_env([
3954 ast.File{
3955 stmts: [
3956 ast.Stmt(ast.ModuleStmt{
3957 name: 'veb'
3958 }),
3959 ast.Stmt(ast.StructDecl{
3960 name: 'Context'
3961 }),
3962 ]
3963 },
3964 ], env)
3965 g.cache_bundle_name = 'veb'
3966 g.emit_modules['veb'] = true
3967 g.type_modules['veb'] = true
3968
3969 assert !g.generic_concrete_c_name_belongs_to_emit_modules('Context')
3970 assert g.generic_concrete_c_name_belongs_to_emit_modules('veb__Context')
3971}
3972
3973fn test_cache_generic_concrete_origin_accepts_current_module_unqualified_type() {
3974 mut env := types.Environment.new()
3975 mut json2_scope := types.new_scope(unsafe { nil })
3976 json2_scope.insert('ValueInfo', types.Type(types.Struct{
3977 name: 'json2__ValueInfo'
3978 }))
3979 lock env.scopes {
3980 env.scopes['json2'] = json2_scope
3981 }
3982 mut g := Gen.new_with_env([], env)
3983 g.cache_bundle_name = 'imports'
3984 g.cur_module = 'json2'
3985 g.emit_modules['json2'] = true
3986 g.type_modules['json2'] = true
3987
3988 assert g.generic_concrete_c_name_belongs_to_emit_modules('ValueInfo')
3989 assert g.generic_concrete_c_name_belongs_to_emit_modules('Array_ValueInfo')
3990}
3991
3992fn test_cache_receiver_specialization_checks_generated_method_name() {
3993 mut g := Gen.new([])
3994 g.cache_bundle_name = 'printer'
3995 g.emit_modules['printer'] = true
3996 g.type_modules['printer'] = true
3997 decl := ast.FnDecl{
3998 name: 'count'
3999 is_method: true
4000 receiver: ast.Parameter{
4001 name: 'writer'
4002 typ: ast.Expr(ast.Ident{
4003 name: 'CounterWriter_T_main__UserCounter'
4004 })
4005 }
4006 }
4007
4008 assert !g.transformed_specialization_belongs_to_cache('printer', decl)
4009}
4010
4011fn test_cache_generic_struct_bindings_follow_emit_module_filter() {
4012 prefs := &vpref.Preferences{
4013 backend: .cleanc
4014 }
4015 mut env := types.Environment.new()
4016 mut arrays_scope := types.new_scope(unsafe { nil })
4017 arrays_scope.insert('ReverseIterator', types.Type(types.Struct{
4018 name: 'ReverseIterator'
4019 generic_params: ['T']
4020 }))
4021 arrays_scope.insert('Part', types.Type(types.Struct{
4022 name: 'arrays__Part'
4023 }))
4024 lock env.scopes {
4025 env.scopes['arrays'] = arrays_scope
4026 }
4027 main_file := ast.File{
4028 mod: 'main'
4029 stmts: [
4030 ast.Stmt(ast.StructDecl{
4031 name: 'GitHubRepoInfo'
4032 }),
4033 ]
4034 }
4035 mut g := Gen.new_with_env_and_pref([main_file], env, prefs)
4036 g.cache_bundle_name = 'veb'
4037 g.cur_module = 'arrays'
4038 g.emit_modules['arrays'] = true
4039 g.type_modules['arrays'] = true
4040 g.record_generic_struct_bindings('ReverseIterator', 'arrays__ReverseIterator', [
4041 ast.Expr(ast.Ident{
4042 name: 'GitHubRepoInfo'
4043 }),
4044 ])
4045 assert 'arrays__ReverseIterator' !in g.generic_struct_instances
4046 g.record_generic_struct_bindings('ReverseIterator', 'arrays__ReverseIterator', [
4047 ast.Expr(ast.Ident{
4048 name: 'Part'
4049 }),
4050 ])
4051 assert g.generic_struct_instances['arrays__ReverseIterator'].len == 1
4052 g.generic_struct_instances.clear()
4053 env.generic_types['reverse_iterator'] = [
4054 {
4055 'T': types.Type(types.Struct{
4056 name: 'GitHubRepoInfo'
4057 })
4058 },
4059 {
4060 'T': types.Type(types.Struct{
4061 name: 'arrays__Part'
4062 })
4063 },
4064 ]
4065 fallback := g.fallback_generic_bindings_for_names(['T']) or { panic('expected fallback') }
4066 fallback_t := fallback['T'] or { panic('expected T fallback') }
4067 assert fallback_t.name() == 'arrays__Part'
4068}
4069
4070fn test_cache_generic_struct_bindings_propagate_current_module_nested_generic() {
4071 tmp_file := '/tmp/v2_cleanc_json2_nested_generic_${os.getpid()}.v'
4072 os.write_file(tmp_file, 'module json2
4073
4074struct Node[T] {
4075mut:
4076 value T
4077 next &Node[T] = unsafe { nil }
4078}
4079
4080struct ValueInfo {
4081 value int
4082}
4083
4084struct LinkedList[T] {
4085mut:
4086 head &Node[T] = unsafe { nil }
4087 tail &Node[T] = unsafe { nil }
4088}
4089
4090struct Decoder {
4091mut:
4092 values_info LinkedList[ValueInfo]
4093 current_node &Node[ValueInfo] = unsafe { nil }
4094}
4095') or {
4096 panic('failed to write temp file')
4097 }
4098 defer {
4099 os.rm(tmp_file) or {}
4100 }
4101 prefs := &vpref.Preferences{
4102 backend: .cleanc
4103 no_parallel: true
4104 }
4105 mut file_set := token.FileSet.new()
4106 mut par := parser.Parser.new(prefs)
4107 files := par.parse_files([tmp_file], mut file_set)
4108 mut env := types.Environment.new()
4109 mut checker := types.Checker.new(prefs, file_set, env)
4110 checker.check_files(files)
4111 mut trans := transformer.Transformer.new_with_pref(env, prefs)
4112 trans.set_file_set(file_set)
4113 transformed_files := trans.transform_files(files)
4114 mut g := Gen.new_with_env_and_pref(transformed_files, env, prefs)
4115 g.cache_bundle_name = 'imports'
4116 g.emit_modules['json2'] = true
4117 g.type_modules['json2'] = true
4118 g.collect_generic_struct_bindings()
4119
4120 assert g.generic_struct_instances['json2__LinkedList'].len == 1
4121 assert g.generic_struct_instances['json2__Node'].len == 1
4122 mut gen := Gen.new_with_env_and_pref(transformed_files, env, prefs)
4123 gen.cache_bundle_name = 'imports'
4124 gen.emit_modules['json2'] = true
4125 gen.type_modules['json2'] = true
4126 csrc := gen.gen()
4127 assert csrc.contains('struct json2__LinkedList_T_json2_ValueInfo {'), csrc
4128 assert csrc.contains('struct json2__Node_T_json2_ValueInfo {'), csrc
4129 assert csrc.contains('json2__Node_T_json2_ValueInfo* next;'), csrc
4130 assert csrc.contains('json2__ValueInfo value;'), csrc
4131}
4132
4133fn test_generic_fallback_requires_selected_fields_on_generic_param() {
4134 mut g := Gen.new([])
4135 node := ast.FnDecl{
4136 name: 'needs_req'
4137 typ: ast.FnType{
4138 generic_params: [
4139 ast.Expr(ast.Ident{
4140 name: 'X'
4141 }),
4142 ]
4143 params: [
4144 ast.Parameter{
4145 name: 'ctx'
4146 typ: ast.Expr(ast.Ident{
4147 name: 'X'
4148 })
4149 },
4150 ]
4151 }
4152 stmts: [
4153 ast.Stmt(ast.ExprStmt{
4154 expr: ast.Expr(ast.SelectorExpr{
4155 lhs: ast.Expr(ast.Ident{
4156 name: 'ctx'
4157 })
4158 rhs: ast.Ident{
4159 name: 'req'
4160 }
4161 })
4162 }),
4163 ]
4164 }
4165 assert !g.accepts_broad_generic_fallback_type(node, types.Type(types.i64_))
4166 assert g.accepts_broad_generic_fallback_type(node, types.Type(types.Struct{
4167 name: 'Context'
4168 fields: [
4169 types.Field{
4170 name: 'req'
4171 typ: types.Type(types.string_)
4172 },
4173 ]
4174 }))
4175}
4176
4177fn test_generic_fallback_ignores_comptime_field_selectors() {
4178 mut g := Gen.new([])
4179 node := ast.FnDecl{
4180 name: 'encode_struct_fields'
4181 typ: ast.FnType{
4182 generic_params: [
4183 ast.Expr(ast.Ident{
4184 name: 'T'
4185 }),
4186 ]
4187 params: [
4188 ast.Parameter{
4189 name: 'val'
4190 typ: ast.Expr(ast.Ident{
4191 name: 'T'
4192 })
4193 },
4194 ]
4195 }
4196 stmts: [
4197 ast.Stmt(ast.ExprStmt{
4198 expr: ast.Expr(ast.SelectorExpr{
4199 lhs: ast.Expr(ast.Ident{
4200 name: 'val'
4201 })
4202 rhs: ast.Ident{
4203 name: '__comptime_selector__'
4204 }
4205 })
4206 }),
4207 ]
4208 }
4209 assert g.accepts_broad_generic_fallback_type(node, types.Type(types.Struct{
4210 name: 'Config'
4211 }))
4212}
4213
4214fn test_option_result_payload_invalid_rejects_qualified_generic_pointer() {
4215 mut g := Gen.new([])
4216 assert g.option_result_payload_invalid('arrays__T*')
4217 assert g.option_result_payload_invalid('arrays__Tptr')
4218 assert !g.option_result_payload_invalid('arrays__Part*')
4219}
4220
4221fn test_option_result_payload_invalid_rejects_unbound_generic_struct() {
4222 mut env := types.Environment.new()
4223 mut sync_scope := types.new_scope(unsafe { nil })
4224 sync_scope.insert('ThreadLocalStorage', types.Type(types.Struct{
4225 name: 'sync__ThreadLocalStorage'
4226 generic_params: ['T']
4227 fields: [
4228 types.Field{
4229 name: 'key'
4230 typ: types.Type(types.u64_)
4231 },
4232 ]
4233 }))
4234