@@ -227,34 +227,11 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl) {
227227 if node.language == .c && node.stmts.len == 0 {
228228 return
229229 }
230- // Generic functions: emit a stub that returns default value
230+ // Generic functions: handled via emit_generic_fn_macro in the forward declaration pass
231231 if node.typ.generic_params.len > 0 {
232- fn_name := g.get_fn_name (node)
233- if fn_name != '' {
234- // Emit a forward declaration and stub body
235- g.sb.write_string ('/* generic stub */ array ${fn_name} (' )
236- for i, param in node.typ.params {
237- if i > 0 {
238- g.sb.write_string (', ' )
239- }
240- g.sb.write_string ('array' )
241- if param.is_mut {
242- g.sb.write_string ('*' )
243- }
244- // Avoid C name clash: parameter named 'array' hides struct array
245- pname := if param.name == 'array' { '_v_array' } else { param.name }
246- g.sb.write_string (' ${pname} ' )
247- }
248- if node.typ.params.len == 0 {
249- g.sb.write_string ('void' )
250- }
251- g.sb.writeln (') {' )
252- g.sb.writeln ('\t return (array){0};' )
253- g.sb.writeln ('}' )
254- g.sb.writeln ('' )
255- }
256232 return
257233 }
234+
258235 fn_name := g.get_fn_name (node)
259236 if fn_name == '' {
260237 return
@@ -499,12 +476,22 @@ fn (g &Gen) contains_call_expr(e ast.Expr) bool {
499476 if e is ast.CallExpr {
500477 return true
501478 }
479+ if e is ast.CallOrCastExpr {
480+ return true
481+ }
502482 if e is ast.CastExpr {
503483 return g.contains_call_expr (e.expr)
504484 }
505485 if e is ast.ParenExpr {
506486 return g.contains_call_expr (e.expr)
507487 }
488+ if e is ast.ArrayInitExpr {
489+ for elem in e.exprs {
490+ if g.contains_call_expr (elem) {
491+ return true
492+ }
493+ }
494+ }
508495 return false
509496}
510497
@@ -1809,3 +1796,92 @@ fn (g &Gen) get_str_fn_for_type(expr_type string) ?string {
18091796 }
18101797 return none
18111798}
1799+
1800+ // emit_generic_fn_macro emits a C macro definition for known simple generic functions
1801+ // (abs, min, max, clamp). For other generic functions, emits a stub.
1802+ // Also emits a typedef for the generic type parameter T.
1803+ fn (mut g Gen) emit_generic_fn_macro (fn_name string , node ast.FnDecl) {
1804+ macro_key := 'generic_macro_${fn_name} '
1805+ if macro_key in g.emitted_types {
1806+ return
1807+ }
1808+ g.emitted_types[macro_key] = true
1809+ if node.typ.params.len == 1 && node.name == 'abs' {
1810+ pname := node.typ.params[0 ].name
1811+ g.sb.writeln ('#define ${fn_name} (${pname} ) ((${pname} ) < 0 ? -(${pname} ) : (${pname} ))' )
1812+ } else if node.typ.params.len == 2 && node.name == 'min' {
1813+ p0 := node.typ.params[0 ].name
1814+ p1 := node.typ.params[1 ].name
1815+ g.sb.writeln ('#define ${fn_name} (${p0} , ${p1} ) ((${p0} ) < (${p1} ) ? (${p0} ) : (${p1} ))' )
1816+ } else if node.typ.params.len == 2 && node.name == 'max' {
1817+ p0 := node.typ.params[0 ].name
1818+ p1 := node.typ.params[1 ].name
1819+ g.sb.writeln ('#define ${fn_name} (${p0} , ${p1} ) ((${p0} ) > (${p1} ) ? (${p0} ) : (${p1} ))' )
1820+ } else if node.typ.params.len == 3 && node.name == 'clamp' {
1821+ p0 := node.typ.params[0 ].name
1822+ p1 := node.typ.params[1 ].name
1823+ p2 := node.typ.params[2 ].name
1824+ g.sb.writeln ('#define ${fn_name} (${p0} , ${p1} , ${p2} ) ((${p0} ) < (${p1} ) ? (${p1} ) : ((${p0} ) > (${p2} ) ? (${p2} ) : (${p0} )))' )
1825+ } else if node.typ.params.len == 0 && node.name in ['maxof' , 'minof' ] {
1826+ // Emit specialized macros for maxof[T]()/minof[T]() - comptime functions
1827+ // that return the min/max value for each numeric type.
1828+ prefix := if fn_name.contains ('__' ) { fn_name.all_before_last ('__' ) + '__' } else { '' }
1829+ is_max := node.name == 'maxof'
1830+ // max_f32/max_f64 are in the math module and emitted as math__max_f32/math__max_f64
1831+ type_const_pairs := if is_max {
1832+ [['i8' , 'max_i8' ], ['i16' , 'max_i16' ], ['i32' , 'max_i32' ],
1833+ ['i64' , 'max_i64' ], ['int' , 'max_int' ], ['u8' , 'max_u8' ],
1834+ ['u16' , 'max_u16' ], ['u32' , 'max_u32' ], ['u64' , 'max_u64' ],
1835+ ['f32' , 'math__max_f32' ], ['f64' , 'math__max_f64' ]]
1836+ } else {
1837+ [['i8' , 'min_i8' ], ['i16' , 'min_i16' ], ['i32' , 'min_i32' ],
1838+ ['i64' , 'min_i64' ], ['int' , 'min_int' ], ['u8' , '((u8)(0))' ],
1839+ ['u16' , '((u16)(0))' ], ['u32' , '((u32)(0))' ],
1840+ ['u64' , '((u64)(0))' ], ['f32' , '(-math__max_f32)' ],
1841+ ['f64' , '(-math__max_f64)' ]]
1842+ }
1843+ for pair in type_const_pairs {
1844+ g.sb.writeln ('#define ${prefix}${node.name} _${pair[0]} () (${pair[1]} )' )
1845+ }
1846+ } else {
1847+ // Fallback: emit a stub with array types.
1848+ // Only emit stub bodies for modules that should be emitted in the current
1849+ // cache bundle. Otherwise the same stub appears in both builtin.o and vlib.o.
1850+ if ! g.should_emit_module (g.cur_module) {
1851+ return
1852+ }
1853+ g.sb.write_string ('/* generic stub */ array ${fn_name} (' )
1854+ for i, param in node.typ.params {
1855+ if i > 0 {
1856+ g.sb.write_string (', ' )
1857+ }
1858+ g.sb.write_string ('array' )
1859+ if param.is_mut {
1860+ g.sb.write_string ('*' )
1861+ }
1862+ pname := if param.name == 'array' { '_v_array' } else { param.name }
1863+ g.sb.write_string (' ${pname} ' )
1864+ }
1865+ if node.typ.params.len == 0 {
1866+ g.sb.write_string ('void' )
1867+ }
1868+ g.sb.writeln (') {' )
1869+ g.sb.writeln ('\t return (array){0};' )
1870+ g.sb.writeln ('}' )
1871+ }
1872+ // Emit typedef for generic type parameter T as f64 (default numeric type)
1873+ for gp in node.typ.generic_params {
1874+ if gp is ast.Ident {
1875+ qualified := if fn_name.contains ('__' ) {
1876+ '${fn_name.all_before_last('__')} __${gp.name} '
1877+ } else {
1878+ gp.name
1879+ }
1880+ tdef_key := 'typedef_${qualified} '
1881+ if tdef_key ! in g.emitted_types {
1882+ g.emitted_types[tdef_key] = true
1883+ g.sb.writeln ('typedef f64 ${qualified} ;' )
1884+ }
1885+ }
1886+ }
1887+ }
0 commit comments