7575 cur_sumtype_match_selector_lhs string // For SelectorExpr: LHS ident (e.g., "se" for se.lhs)
7676 cur_sumtype_match_selector_rhs string // For SelectorExpr: RHS field (e.g., "lhs" for se.lhs)
7777 cur_lambda_elem_type string // Element type for lambda `it` variable in filter/map/any
78+ // For -printfn support
79+ last_fn_c_name string // Current function's C name for -printfn
80+ fn_start_pos int // Start position of current function in output buffer
7881}
7982
8083pub fn Gen .new (files []ast.File) & Gen {
@@ -408,6 +411,34 @@ fn (g &Gen) get_struct_field_type_from_env(struct_name string, field_name string
408411 return none
409412}
410413
414+ // lookup_method_return_type_from_env looks up a method's return type from the environment
415+ fn (g &Gen) lookup_method_return_type_from_env (type_name string , method_name string ) ? string {
416+ if g.env == unsafe { nil } {
417+ return none
418+ }
419+ if fn_type := g.env.lookup_method (type_name, method_name) {
420+ if ret := fn_type.get_return_type () {
421+ return g.types_type_to_c (ret)
422+ }
423+ return 'void'
424+ }
425+ return none
426+ }
427+
428+ // lookup_fn_return_type_from_env looks up a function's return type from the environment
429+ fn (g &Gen) lookup_fn_return_type_from_env (module_name string , fn_name string ) ? string {
430+ if g.env == unsafe { nil } {
431+ return none
432+ }
433+ if fn_type := g.env.lookup_fn (module_name, fn_name) {
434+ if ret := fn_type.get_return_type () {
435+ return g.types_type_to_c (ret)
436+ }
437+ return 'void'
438+ }
439+ return none
440+ }
441+
411442// types_type_to_c converts a types.Type to a C type string
412443fn (g &Gen) types_type_to_c (t types.Type) string {
413444 match t {
@@ -442,7 +473,8 @@ fn (g &Gen) types_type_to_c(t types.Type) string {
442473 return '${base }*'
443474 }
444475 types.Array {
445- return 'Array' // Arrays use generic Array struct
476+ elem := g.types_type_to_c (t.elem_type)
477+ return 'Array_${elem }'
446478 }
447479 types.Struct {
448480 return t.name
@@ -1195,11 +1227,37 @@ pub fn (mut g Gen) gen() string {
11951227 // strings__Builder__free - Builder is a type alias for []u8, delegate to array__free
11961228 g.sb.writeln ('static inline void strings__Builder__free(strings__Builder* b) { array__free((array*)b); }' )
11971229 // array__contains_u8 - check if array contains element (for 'in' operator)
1198- g.sb.writeln ('static inline bool array__contains_u8(array* a, u8 v) { for (int i = 0; i < a-> len; i++) { if (((u8*)a-> data)[i] == v) return true; } return false; }' )
1230+ g.sb.writeln ('static inline bool array__contains_u8(array a, u8 v) { for (int i = 0; i < a. len; i++) { if (((u8*)a. data)[i] == v) return true; } return false; }' )
11991231 // Forward declaration for string__eq (used by array__contains_string)
12001232 g.sb.writeln ('bool string__eq(string s, string a);' )
12011233 // array__contains_string - check if array contains string element
1202- g.sb.writeln ('static inline bool array__contains_string(array* a, string v) { for (int i = 0; i < a->len; i++) { if (string__eq(((string*)a->data)[i], v)) return true; } return false; }' )
1234+ g.sb.writeln ('static inline bool array__contains_string(array a, string v) { for (int i = 0; i < a.len; i++) { if (string__eq(((string*)a.data)[i], v)) return true; } return false; }' )
1235+ // builtin__new_array_from_c_array_noscan - create array from C array literal
1236+ g.sb.writeln ('static inline array builtin__new_array_from_c_array_noscan(int len, int cap, int elem_size, void* c_array) {' )
1237+ g.sb.writeln (' array a = {0}; a.len = len; a.cap = cap; a.element_size = elem_size;' )
1238+ g.sb.writeln (' if (len > 0) { a.data = malloc(cap * elem_size); if (a.data) memcpy(a.data, c_array, len * elem_size); }' )
1239+ g.sb.writeln (' return a;' )
1240+ g.sb.writeln ('}' )
1241+ // builtin__array_push_noscan - push element to array
1242+ g.sb.writeln ('static inline void builtin__array_push_noscan(array* a, void* elem) {' )
1243+ g.sb.writeln (' if (a->len >= a->cap) { a->cap = a->cap == 0 ? 8 : a->cap * 2; a->data = realloc(a->data, a->cap * a->element_size); }' )
1244+ g.sb.writeln (' memcpy((char*)a->data + a->len * a->element_size, elem, a->element_size);' )
1245+ g.sb.writeln (' a->len++;' )
1246+ g.sb.writeln ('}' )
1247+ // builtin__array_push_many - push multiple elements from another array
1248+ g.sb.writeln ('static inline void builtin__array_push_many(array* a, array b) {' )
1249+ g.sb.writeln (' if (b.len == 0) return;' )
1250+ g.sb.writeln (' int new_len = a->len + b.len;' )
1251+ g.sb.writeln (' while (new_len > a->cap) { a->cap = a->cap == 0 ? 8 : a->cap * 2; }' )
1252+ g.sb.writeln (' a->data = realloc(a->data, a->cap * a->element_size);' )
1253+ g.sb.writeln (' memcpy((char*)a->data + a->len * a->element_size, b.data, b.len * a->element_size);' )
1254+ g.sb.writeln (' a->len = new_len;' )
1255+ g.sb.writeln ('}' )
1256+ // array__contains_* functions for common types (takes array by value)
1257+ g.sb.writeln ('static inline bool array__contains_int(array a, int v) { for (int i = 0; i < a.len; i++) { if (((int*)a.data)[i] == v) return true; } return false; }' )
1258+ g.sb.writeln ('static inline bool array__contains_i64(array a, i64 v) { for (int i = 0; i < a.len; i++) { if (((i64*)a.data)[i] == v) return true; } return false; }' )
1259+ g.sb.writeln ('static inline bool array__contains_u64(array a, u64 v) { for (int i = 0; i < a.len; i++) { if (((u64*)a.data)[i] == v) return true; } return false; }' )
1260+ g.sb.writeln ('static inline bool array__contains_rune(array a, rune v) { for (int i = 0; i < a.len; i++) { if (((rune*)a.data)[i] == v) return true; } return false; }' )
12031261 // strings__Builder__trim - trim builder to specified length
12041262 g.sb.writeln ('static inline void strings__Builder__trim(strings__Builder b, int n) { if (n < b.len) b.len = n; }' )
12051263 // ArrayFlags__has - check if flag is set
@@ -1208,13 +1266,6 @@ pub fn (mut g Gen) gen() string {
12081266 g.sb.writeln ('static inline bool array__eq(array a, array b) { if (a.len != b.len) return false; return memcmp(a.data, b.data, a.len * a.element_size) == 0; }' )
12091267 // array__clone - clone an array (returns a copy with same elements)
12101268 g.sb.writeln ('static inline array array__clone(array* a) { array c = {0}; if (!a || a->len == 0) return c; c.len = a->len; c.cap = a->len; c.element_size = a->element_size; c.data = malloc(a->len * a->element_size); if (c.data) memcpy(c.data, a->data, a->len * a->element_size); return c; }' )
1211- // Register builtin array method return types
1212- g.fn_types['array__clone' ] = 'array'
1213- g.fn_types['array__reverse' ] = 'array'
1214- g.fn_types['array__sorted' ] = 'array'
1215- g.fn_types['array__eq' ] = 'bool'
1216- g.fn_types['array__contains_u8' ] = 'bool'
1217- g.fn_types['array__contains_string' ] = 'bool'
12181269 // Register Map getter return types
12191270 g.fn_types['__Map_int_int_get' ] = 'int'
12201271 g.fn_types['__Map_string_int_get' ] = 'int'
@@ -1228,6 +1279,11 @@ pub fn (mut g Gen) gen() string {
12281279 g.fn_types['__Map_string_Array_string_get' ] = 'array'
12291280 g.fn_types['__Map_string_Map_string_bool_get' ] = 'map'
12301281 g.fn_types['__Map_string_Map_string_string_get' ] = 'map'
1282+ // Register transformer-synthesized function return types (not in type system)
1283+ g.fn_types['string__plus' ] = 'string'
1284+ g.fn_types['string__plus_two' ] = 'string'
1285+ g.fn_types['string__eq' ] = 'bool'
1286+ g.fn_types['string__lt' ] = 'bool'
12311287 // Map function stubs
12321288 g.sb.writeln ('static inline Map_int_int __new_Map_int_int() { return (Map_int_int){0}; }' )
12331289 g.sb.writeln ('static inline int __Map_int_int_get(Map_int_int* m, int key) { return 0; }' )
@@ -1789,6 +1845,10 @@ fn (mut g Gen) infer_type(node ast.Expr) string {
17891845 if t := g.fn_types[mangled] {
17901846 return t
17911847 }
1848+ // Look up method return type from environment
1849+ if ret := g.lookup_method_return_type_from_env (clean_type, name) {
1850+ return ret
1851+ }
17921852 // Array methods that return the element type
17931853 if clean_type.starts_with ('Array_' ) && name in ['first' , 'last' , 'pop' ] {
17941854 return clean_type['Array_' .len..]
@@ -1823,6 +1883,22 @@ fn (mut g Gen) infer_type(node ast.Expr) string {
18231883 return t
18241884 }
18251885 }
1886+ // Look up function return type from environment
1887+ if ret := g.lookup_fn_return_type_from_env (g.cur_module, name) {
1888+ return ret
1889+ }
1890+ if ret := g.lookup_fn_return_type_from_env ('builtin' , name) {
1891+ return ret
1892+ }
1893+ // For calls with ArrayInitExpr argument, infer array type from that argument
1894+ for arg in node.args {
1895+ if arg is ast.ArrayInitExpr {
1896+ arr_type := g.infer_type (arg)
1897+ if arr_type.starts_with ('Array_' ) {
1898+ return arr_type
1899+ }
1900+ }
1901+ }
18261902 return 'int'
18271903 }
18281904 ast.CallOrCastExpr {
@@ -3265,6 +3341,10 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl) {
32653341 g.mut_params = map [string ]bool {}
32663342 g.defer_stmts.clear ()
32673343
3344+ // Record start position and function name for -printfn support
3345+ g.fn_start_pos = g.sb.len
3346+ g.last_fn_c_name = g.get_fn_name (node)
3347+
32683348 // Set the current function's return type for proper return statement generation
32693349 ret_expr := node.typ.return_type
32703350 g.cur_fn_returns_result = false
@@ -3646,6 +3726,12 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl) {
36463726 }
36473727 g.indent--
36483728 g.sb.writeln ('}' )
3729+ // Print function source if in -printfn list and exit
3730+ if g.pref != unsafe { nil } && g.pref.printfn_list.len > 0
3731+ && g.last_fn_c_name in g.pref.printfn_list {
3732+ println (g.sb.after (g.fn_start_pos))
3733+ exit (0 )
3734+ }
36493735}
36503736
36513737fn (mut g Gen) gen_stmts (stmts []ast.Stmt) {
@@ -5142,13 +5228,13 @@ fn (mut g Gen) gen_expr(node ast.Expr) {
51425228 g.gen_expr (node.lhs)
51435229 g.sb.write_string (')' )
51445230 } else if rhs_type.starts_with ('Array_' ) {
5145- // x in arr => array__contains_u8(& arr, x) (for u8 arrays)
5146- // x !in arr => !array__contains_u8(& arr, x)
5231+ // x in arr => array__contains_T( arr, x) (for arrays)
5232+ // x !in arr => !array__contains_T( arr, x)
51475233 elem_type := rhs_type['Array_' .len..]
51485234 if node.op == .not_in {
51495235 g.sb.write_string ('!' )
51505236 }
5151- g.sb.write_string ('array__contains_${elem_type }(& ' )
5237+ g.sb.write_string ('array__contains_${elem_type }(' )
51525238 g.gen_expr (node.rhs)
51535239 g.sb.write_string (', ' )
51545240 g.gen_expr (node.lhs)
@@ -6916,6 +7002,22 @@ fn (mut g Gen) gen_expr(node ast.Expr) {
69167002 return
69177003 }
69187004 }
7005+ // Check for known enum values from stdlib
7006+ // ProcessState enum from os module
7007+ if node.rhs.name in ['not_started' , 'running' , 'stopped' , 'exited' , 'closed' ] {
7008+ g.sb.write_string ('os__ProcessState__${node .rhs .name }' )
7009+ return
7010+ }
7011+ // TrimMode enum from strings module
7012+ if node.rhs.name in ['trim_left' , 'trim_right' , 'trim_both' ] {
7013+ g.sb.write_string ('TrimMode__${node .rhs .name }' )
7014+ return
7015+ }
7016+ // CaseMode enum from unicode module
7017+ if node.rhs.name in ['to_upper' , 'to_lower' , 'to_title' ] {
7018+ g.sb.write_string ('CaseMode__${node .rhs .name }' )
7019+ return
7020+ }
69197021 // Last fallback: just output the field name (might not be valid C)
69207022 g.sb.write_string ('${node .rhs .name }' )
69217023 return
@@ -7084,30 +7186,21 @@ fn (mut g Gen) gen_expr(node ast.Expr) {
70847186 g.gen_expr (node.expr)
70857187 }
70867188 ast.ArrayInitExpr {
7087- // Check if this is a fixed-size array (typ is ArrayFixedType)
7189+ // After transformer, ArrayInitExpr is either:
7190+ // 1. Fixed-size array: generate {val1, val2, ...}
7191+ // 2. Dynamic array as arg to builtin call: generate _MOV((elem_type[len]){values})
70887192 mut is_fixed_array := false
7089- mut fixed_size := ''
7090- mut fixed_elem_type := ''
70917193 match node.typ {
70927194 ast.Type {
70937195 if node.typ is ast.ArrayFixedType {
70947196 is_fixed_array = true
7095- fixed_elem_type = g.expr_type_to_c (node.typ.elem_type)
7096- fixed_size = if node.typ.len is ast.BasicLiteral {
7097- node.typ.len.value
7098- } else if node.typ.len is ast.Ident {
7099- node.typ.len.name
7100- } else {
7101- '0'
7102- }
71037197 }
71047198 }
71057199 else {}
71067200 }
71077201 if is_fixed_array {
7108- // Generate C fixed -size array initializer: {val1, val2, ...}
7202+ // Fixed -size array: generate C array initializer
71097203 g.sb.write_string ('{' )
7110- // If no explicit elements, generate zeros
71117204 if node.exprs.len == 0 {
71127205 g.sb.write_string ('0' )
71137206 } else {
@@ -7120,36 +7213,30 @@ fn (mut g Gen) gen_expr(node ast.Expr) {
71207213 }
71217214 g.sb.write_string ('}' )
71227215 } else {
7123- // Generate array using new_array_from_c_array builtin
7216+ // Dynamic array values: generate _MOV((elem_type[len]){values})
71247217 arr_len := node.exprs.len
7125- mut c_elem_type := 'int' // Actual C type for sizeof and array literals
7126- if node.typ ! is ast.EmptyExpr {
7127- // node.typ is ArrayType{elem_type: T} for []T{} syntax
7128- // Get the actual C type for sizeof and array literals
7129- match node.typ {
7130- ast.Type {
7131- if node.typ is ast.ArrayType {
7132- c_elem_type = g.expr_type_to_c (node.typ.elem_type)
7133- } else {
7134- c_elem_type = g.expr_type_to_c (node.typ)
7135- }
7136- }
7137- else {
7138- c_elem_type = g.expr_type_to_c (node.typ)
7218+ mut c_elem_type := 'int'
7219+ match node.typ {
7220+ ast.Type {
7221+ if node.typ is ast.ArrayType {
7222+ c_elem_type = g.expr_type_to_c (node.typ.elem_type)
71397223 }
71407224 }
7141- } else if arr_len > 0 {
7142- c_elem_type = g.infer_type (node.exprs[0 ])
7225+ else {}
71437226 }
7144- // new_array_from_c_array(len, len, sizeof(elem), (elem_type[len]){values})
7145- g.sb.write_string ('new_array_from_c_array(${arr_len }, ${arr_len }, sizeof(${c_elem_type }), (${c_elem_type }[${arr_len }]){' )
7146- for i, expr in node.exprs {
7147- if i > 0 {
7148- g.sb.write_string (', ' )
7227+ // Handle empty arrays specially - generate empty struct initialization
7228+ if arr_len == 0 {
7229+ g.sb.write_string ('(array){0}' )
7230+ } else {
7231+ g.sb.write_string ('_MOV((${c_elem_type }[${arr_len }]){' )
7232+ for i, expr in node.exprs {
7233+ if i > 0 {
7234+ g.sb.write_string (', ' )
7235+ }
7236+ g.gen_expr (expr)
71497237 }
7150- g.gen_expr (expr )
7238+ g.sb. write_string ( '})' )
71517239 }
7152- g.sb.write_string ('})' )
71537240 }
71547241 }
71557242 ast.MapInitExpr {
0 commit comments