v: missing -skip-unused fixes (#22978)

This commit is contained in:
Felipe Pena 2024-11-27 20:03:03 -03:00 committed by GitHub
parent 066384ce8c
commit ae4a9047c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 233 additions and 98 deletions

View File

@ -88,7 +88,7 @@ mut:
values &u8 = unsafe { nil }
}
@[inline; markused]
@[inline]
fn new_dense_array(key_bytes int, value_bytes int) DenseArray {
cap := 8
return DenseArray{
@ -103,18 +103,18 @@ fn new_dense_array(key_bytes int, value_bytes int) DenseArray {
}
}
@[inline; markused]
@[inline]
fn (d &DenseArray) key(i int) voidptr {
return unsafe { voidptr(d.keys + i * d.key_bytes) }
}
// for cgen
@[inline; markused]
@[inline]
fn (d &DenseArray) value(i int) voidptr {
return unsafe { voidptr(d.values + i * d.value_bytes) }
}
@[inline; markused]
@[inline]
fn (d &DenseArray) has_index(i int) bool {
return d.deletes == 0 || unsafe { d.all_deleted[i] } == 0
}
@ -259,7 +259,6 @@ fn map_free_string(pkey voidptr) {
fn map_free_nop(_ voidptr) {
}
@[markused]
fn new_map(key_bytes int, value_bytes int, hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn) map {
metasize := int(sizeof(u32) * (init_capicity + extra_metas_inc))
// for now assume anything bigger than a pointer is a string

View File

@ -12,10 +12,10 @@ pub struct UsedFeatures {
pub mut:
interfaces bool // interface
dump bool // dump()
builtin_types bool // uses any builtin type
index bool // string[0]
range_index bool // string[0..1]
cast_ptr bool // &u8(...)
asserts bool // assert expr
as_cast bool // expr as Type
anon_fn bool // fn () { }
auto_str bool // auto str fns
@ -24,6 +24,11 @@ pub mut:
arr_first bool // arr.first()
arr_last bool // arr.last()
arr_pop bool // arr.pop()
arr_delete bool // arr.delete()
arr_init bool // [1, 2, 3]
arr_map bool // []map[key]value
map_update bool // {...foo}
interpolation bool // '${foo} ${bar}'
option_or_result bool // has panic call
print_types map[int]bool // print() idx types
used_fns map[string]bool // filled in by markused
@ -32,6 +37,7 @@ pub mut:
used_veb_types []Type // veb context types, filled in by checker
used_maps int // how many times maps were used, filled in by markused
used_arrays int // how many times arrays were used, filled in by markused
used_modules map[string]bool // filled in checker
// json bool // json is imported
debugger bool // debugger is used
comptime_calls map[string]bool // resolved $method() names

View File

@ -2302,11 +2302,20 @@ fn (mut c Checker) stmt(mut node ast.Stmt) {
fn (mut c Checker) assert_stmt(mut node ast.AssertStmt) {
if node.is_used {
c.table.used_features.auto_str = true
c.table.used_features.asserts = true
}
cur_exp_typ := c.expected_type
c.expected_type = ast.bool_type
assert_type := c.check_expr_option_or_result_call(node.expr, c.expr(mut node.expr))
if c.pref.skip_unused && !c.table.used_features.auto_str && !c.is_builtin_mod
&& mut node.expr is ast.InfixExpr {
if !c.table.sym(c.unwrap_generic(node.expr.left_type)).has_method('str') {
c.table.used_features.auto_str = true
}
if !c.table.sym(c.unwrap_generic(node.expr.right_type)).has_method('str') {
c.table.used_features.auto_str = true
}
}
if assert_type != ast.bool_type_idx {
atype_name := c.table.sym(assert_type).name
c.error('assert can be used only with `bool` expressions, but found `${atype_name}` instead',
@ -2949,6 +2958,7 @@ pub fn (mut c Checker) expr(mut node ast.Expr) ast.Type {
return c.concat_expr(mut node)
}
ast.DumpExpr {
c.table.used_features.dump = true
c.expected_type = ast.string_type
node.expr_type = c.expr(mut node.expr)
if c.pref.skip_unused && !c.is_builtin_mod {
@ -3268,7 +3278,8 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
to_type
}
final_to_is_ptr := to_type.is_ptr() || final_to_type.is_ptr()
if to_type.is_ptr() {
if c.pref.skip_unused && !c.is_builtin_mod && c.mod !in ['strings', 'math.bits']
&& to_type.is_ptr() {
c.table.used_features.cast_ptr = true
}
if to_type.has_flag(.result) {
@ -4763,10 +4774,12 @@ fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type {
}
else {}
}
c.table.used_features.index = true
if !c.is_builtin_mod && c.mod !in ['strings', 'math.bits'] {
if node.index is ast.RangeExpr {
c.table.used_features.range_index = true
}
c.table.used_features.index = true
}
is_aggregate_arr := typ_sym.kind == .aggregate
&& (typ_sym.info as ast.Aggregate).types.filter(c.table.type_kind(it) !in [.array, .array_fixed, .string, .map]).len == 0
if typ_sym.kind !in [.array, .array_fixed, .string, .map]

View File

@ -13,6 +13,9 @@ fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type {
}
// `x := []string{}` (the type was set in the parser)
if node.typ != ast.void_type {
if !c.is_builtin_mod && c.mod !in ['builtin', 'strings', 'strconv', 'math.bits'] {
c.table.used_features.arr_init = true
}
if node.elem_type != 0 {
elem_sym := c.table.sym(node.elem_type)
@ -61,6 +64,11 @@ fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type {
c.warn('byte is deprecated, use u8 instead', node.elem_type_pos)
}
}
ast.Map {
if c.pref.skip_unused && !c.is_builtin_mod {
c.table.used_features.arr_map = true
}
}
else {}
}
}
@ -143,6 +151,9 @@ fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type {
}
// `[1,2,3]`
if node.exprs.len > 0 && node.elem_type == ast.void_type {
if !c.is_builtin_mod && c.mod !in ['builtin', 'strings', 'strconv', 'math.bits'] {
c.table.used_features.arr_init = true
}
mut expected_value_type := ast.void_type
mut expecting_interface_array := false
mut expecting_sumtype_array := false
@ -495,6 +506,7 @@ fn (mut c Checker) map_init(mut node ast.MapInit) ast.Type {
}
if (node.keys.len > 0 && node.vals.len > 0) || node.has_update_expr {
c.table.used_features.map_update = true
mut map_type := ast.void_type
use_expected_type := c.expected_type != ast.void_type && !c.inside_const
&& c.table.sym(c.expected_type).kind == .map && !(c.inside_fn_arg

View File

@ -751,8 +751,23 @@ fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type {
c.inside_or_block_value = true
c.check_or_expr(node.or_block, typ, c.expected_or_type, node)
c.inside_or_block_value = old_inside_or_block_value
} else if node.or_block.kind == .propagate_option || node.or_block.kind == .propagate_result {
if c.pref.skip_unused && !c.is_builtin_mod && c.mod != 'strings' {
c.table.used_features.option_or_result = true
}
}
c.expected_or_type = old_expected_or_type
if c.pref.skip_unused && !c.is_builtin_mod && c.mod == 'main' {
if node.is_method {
type_str := c.table.type_to_str(node.left_type)
if c.table.sym(node.left_type).is_builtin()
&& type_str !in c.table.used_features.used_modules {
c.table.used_features.used_modules[type_str] = true
}
} else if node.name.contains('.') {
c.table.used_features.used_modules[node.name.all_before('.')] = true
}
}
if !c.inside_const && c.table.cur_fn != unsafe { nil } && !c.table.cur_fn.is_main
&& !c.table.cur_fn.is_test {
@ -1401,7 +1416,8 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
// println / eprintln / panic can print anything
if node.args.len > 0 && fn_name in print_everything_fns {
c.builtin_args(mut node, fn_name, func)
if c.pref.skip_unused && !c.is_builtin_mod && node.args[0].expr !is ast.StringLiteral {
if c.pref.skip_unused && !c.is_builtin_mod && c.mod != 'math.bits'
&& node.args[0].expr !is ast.StringLiteral {
if !c.table.sym(c.unwrap_generic(node.args[0].typ)).has_method('str') {
c.table.used_features.auto_str = true
if node.args[0].typ.is_ptr() {
@ -2162,9 +2178,6 @@ fn (mut c Checker) method_call(mut node ast.CallExpr, mut continue_check &bool)
continue_check = false
return ast.void_type
}
if c.pref.skip_unused && !c.is_builtin_mod && c.mod != 'strings' {
c.table.used_features.builtin_types = true
}
c.expected_type = left_type
mut is_generic := left_type.has_flag(.generic)
node.left_type = left_type
@ -2949,6 +2962,9 @@ fn (mut c Checker) check_expected_arg_count(mut node ast.CallExpr, f &ast.Fn) !
}
if f.is_variadic {
min_required_params--
if c.pref.skip_unused && !c.is_builtin_mod {
c.table.used_features.arr_init = true
}
} else {
has_decompose := node.args.any(it.expr is ast.ArrayDecompose)
if has_decompose {
@ -3565,7 +3581,7 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
}
node.return_type = ast.int_type
} else if method_name in ['first', 'last', 'pop'] {
if c.pref.skip_unused {
if c.pref.skip_unused && !c.is_builtin_mod {
if method_name == 'first' {
c.table.used_features.arr_first = true
}
@ -3587,6 +3603,9 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
node.receiver_type = node.left_type
}
} else if method_name == 'delete' {
if c.pref.skip_unused && !c.is_builtin_mod {
c.table.used_features.arr_delete = true
}
c.check_for_mut_receiver(mut node.left)
unwrapped_left_sym := c.table.sym(unwrapped_left_type)
if method := c.table.find_method(unwrapped_left_sym, method_name) {

View File

@ -190,6 +190,9 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
'declare a key and a value variable when ranging a map: `for key, val in map {`\n' +
'use `_` if you do not need the variable', node.pos)
}
if !c.is_builtin_mod && c.mod != 'strings' {
c.table.used_features.used_maps++
}
if node.key_var.len > 0 {
key_type := match sym.kind {
.map { sym.map_info().key_type }

View File

@ -62,6 +62,7 @@ fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) ast.Type {
} else {
c.table.used_features.print_types[ftyp.idx()] = true
}
c.table.used_features.interpolation = true
}
c.fail_if_unreadable(expr, ftyp, 'interpolation object')
node.expr_types << ftyp

View File

@ -6660,7 +6660,7 @@ fn (mut g Gen) write_init_function() {
g.write('\tas_cast_type_indexes = ')
g.writeln(g.as_cast_name_table())
}
if !g.pref.is_shared && (!g.pref.skip_unused || g.table.used_features.builtin_types) {
if !g.pref.is_shared && (!g.pref.skip_unused || g.table.used_features.used_modules.len > 0) {
// shared object does not need this
g.writeln('\tbuiltin_init();')
}

View File

@ -13,7 +13,6 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
defer {
util.timing_measure(@METHOD)
}
mut allow_noscan := true
// Functions that must be generated and can't be skipped
mut all_fn_root_names := []string{}
if pref_.backend == .native {
@ -25,17 +24,19 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
string_idx_str := '${ast.string_type_idx}'
array_idx_str := '${ast.array_type_idx}'
map_idx_str := '${ast.map_type_idx}'
ref_map_idx_str := '${int(ast.map_type.ref())}'
ref_densearray_idx_str := '${int(table.find_type('DenseArray').ref())}'
ref_array_idx_str := '${int(ast.array_type.ref())}'
mut core_fns := [
'main.main',
'init_global_allocator', // needed for linux_bare and wasm_bare
'v_realloc', // needed for _STR
'malloc',
'malloc_noscan',
'vcalloc',
'vcalloc_noscan',
//'malloc',
//'malloc_noscan',
//'vcalloc',
//'vcalloc_noscan',
'memdup',
'vstrlen',
//'vstrlen',
'tos',
'tos2',
'error',
@ -45,26 +46,35 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
'main.vtest_init',
'main.vtest_new_metainfo',
'main.vtest_new_filemetainfo',
'println',
]
$if debug_used_features ? {
dump(table.used_features)
}
panic_deps := [
'__new_array_with_default',
'__new_array_with_default_noscan',
'str_intp',
ref_array_idx_str + '.push',
ref_array_idx_str + '.push_noscan',
string_idx_str + '.substr',
array_idx_str + '.slice',
array_idx_str + '.get',
'v_fixed_index',
charptr_idx_str + '.vstring_literal',
]
// real world apps
if table.used_features.builtin_types || table.used_features.as_cast
|| table.used_features.auto_str {
if table.used_features.used_modules.len > 0 {
core_fns << panic_deps
}
if table.used_features.as_cast || table.used_features.auto_str || pref_.is_shared {
core_fns << panic_deps
core_fns << 'isnil'
core_fns << '__new_array'
core_fns << '__new_array_noscan'
core_fns << '__new_array_with_multi_default'
core_fns << '__new_array_with_multi_default_noscan'
core_fns << '__new_array_with_array_default'
core_fns << '__new_array_with_array_default_noscan'
core_fns << 'new_array_from_c_array'
// byteptr and charptr
core_fns << byteptr_idx_str + '.vstring'
@ -73,8 +83,8 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
core_fns << charptr_idx_str + '.vstring'
core_fns << charptr_idx_str + '.vstring_with_len'
core_fns << charptr_idx_str + '.vstring_literal'
if table.used_features.index {
}
if table.used_features.index || pref_.is_shared {
core_fns << string_idx_str + '.at_with_check'
core_fns << string_idx_str + '.clone'
core_fns << string_idx_str + '.clone_static'
@ -83,8 +93,11 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
core_fns << ref_array_idx_str + '.set'
core_fns << map_idx_str + '.get'
core_fns << map_idx_str + '.set'
core_fns << '__new_array_noscan'
core_fns << ref_array_idx_str + '.push_noscan'
core_fns << ref_array_idx_str + '.push_many_noscan'
}
if table.used_features.range_index {
if table.used_features.range_index || pref_.is_shared {
core_fns << string_idx_str + '.substr_with_check'
core_fns << string_idx_str + '.substr_ni'
core_fns << array_idx_str + '.slice_ni'
@ -114,24 +127,38 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
if table.used_features.arr_last {
core_fns << array_idx_str + '.last'
}
} else {
// TODO: this *should not* depend on the used compiler, which is brittle, but only on info in the AST...
// hello world apps
if table.used_features.arr_delete {
core_fns << panic_deps
}
if pref_.ccompiler_type != .tinyc && 'no_backtrace' !in pref_.compile_defines {
// with backtrace on gcc/clang more code needs be generated
allow_noscan = true
core_fns << panic_deps
} else {
allow_noscan = false
}
if table.used_features.interpolation {
core_fns << panic_deps
}
if table.used_features.dump {
core_fns << panic_deps
builderptr_idx := int(table.find_type('strings.Builder').ref())
core_fns << [
'${builderptr_idx}.str',
'${builderptr_idx}.free',
'${builderptr_idx}.write_rune',
]
}
if table.used_features.arr_init {
core_fns << '__new_array'
core_fns << 'new_array_from_c_array'
core_fns << 'new_array_from_c_array_noscan'
core_fns << '__new_array_with_multi_default'
core_fns << '__new_array_with_multi_default_noscan'
core_fns << '__new_array_with_array_default'
}
if table.used_features.option_or_result {
core_fns << '_option_ok'
core_fns << '_result_ok'
if !allow_noscan {
core_fns << charptr_idx_str + '.vstring_literal'
core_fns << panic_deps
allow_noscan = true
}
}
if table.used_features.as_cast {
core_fns << '__as_cast'
@ -139,9 +166,33 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
if table.used_features.anon_fn {
core_fns << 'memdup_uncollectable'
}
if table.used_features.arr_map {
core_fns << '__new_array_with_map_default'
core_fns << 'new_map_noscan_key'
core_fns << ref_map_idx_str + '.clone'
core_fns << ref_densearray_idx_str + '.clone'
core_fns << map_idx_str + '.clone'
table.used_features.used_maps++
}
if table.used_features.map_update {
core_fns << 'new_map_update_init'
table.used_features.used_maps++
}
if table.used_features.asserts {
core_fns << panic_deps
core_fns << '__print_assert_failure'
core_fns << 'isnil'
}
if pref_.trace_calls || pref_.trace_fns.len > 0 {
core_fns << panic_deps
core_fns << 'vgettid'
core_fns << 'C.gettid'
core_fns << 'v.trace_calls.on_c_main'
core_fns << 'v.trace_calls.current_time'
core_fns << 'v.trace_calls.on_call'
}
all_fn_root_names << core_fns
}
if pref_.is_bare {
all_fn_root_names << [
'strlen',
@ -154,16 +205,18 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
}
is_noscan_whitelisted := pref_.gc_mode in [.boehm_full_opt, .boehm_incr_opt]
has_noscan := all_fn_root_names.any(it.contains('noscan')
&& it !in ['vcalloc_noscan', 'malloc_noscan'])
for k, mut mfn in all_fns {
$if trace_skip_unused_all_fns ? {
println('k: ${k} | mfn: ${mfn.name}')
}
if k in table.used_features.comptime_calls {
all_fn_root_names << k
continue
}
// _noscan functions/methods are selected when the `-gc boehm` is on:
if allow_noscan && is_noscan_whitelisted && mfn.name.ends_with('_noscan') {
if has_noscan && is_noscan_whitelisted && mfn.name.ends_with('_noscan') {
all_fn_root_names << k
continue
}
@ -176,8 +229,7 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
all_fn_root_names << k
continue
}
if method_receiver_typename == '&strings.Builder'
&& (table.used_features.builtin_types || table.used_features.auto_str) {
if method_receiver_typename == '&strings.Builder' && table.used_features.auto_str {
// implicit string builders are generated in auto_eq_methods.v
all_fn_root_names << k
continue
@ -187,7 +239,7 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
if k.ends_with('.str') || k.ends_with('.auto_str') {
if table.used_features.auto_str
|| table.used_features.print_types[mfn.receiver.typ.idx()]
|| table.used_features.debugger {
|| table.used_features.debugger || table.used_features.used_modules.len > 0 {
all_fn_root_names << k
}
continue
@ -196,7 +248,7 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
all_fn_root_names << k
continue
}
if table.used_features.builtin_types && k.ends_with('.free') {
if table.used_features.used_modules.len > 0 && k.ends_with('.free') {
all_fn_root_names << k
continue
}
@ -356,19 +408,13 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
all_globals: all_globals
pref: pref_
)
// println( all_fns.keys() )
walker.mark_markused_fn_decls() // tagged with `@[markused]`
walker.mark_markused_consts() // tagged with `@[markused]`
walker.mark_markused_globals() // tagged with `@[markused]`
walker.mark_exported_fns()
walker.mark_root_fns(all_fn_root_names)
walker.mark_veb_actions()
if walker.n_asserts > 0 {
unsafe { walker.fn_decl(mut all_fns['__print_assert_failure']) }
}
if table.used_features.used_maps > 0 {
for k, mut mfn in all_fns {
mut method_receiver_typename := ''
@ -465,6 +511,13 @@ fn all_fn_const_and_global(ast_files []&ast.File) (map[string]ast.FnDecl, map[st
return all_fns, all_consts, all_globals
}
fn mark_all_methods_used(mut table ast.Table, mut all_fn_root_names []string, typ ast.Type) {
sym := table.sym(typ)
for method in sym.methods {
all_fn_root_names << '${int(typ)}.${method.name}'
}
}
fn handle_vweb(mut table ast.Table, mut all_fn_root_names []string, result_name string, filter_name string,
context_name string) {
// handle vweb magic router methods:
@ -472,7 +525,7 @@ fn handle_vweb(mut table ast.Table, mut all_fn_root_names []string, result_name
if result_type_idx != 0 {
all_fn_root_names << filter_name
typ_vweb_context := table.find_type(context_name).set_nr_muls(1)
all_fn_root_names << '${int(typ_vweb_context)}.html'
mark_all_methods_used(mut table, mut all_fn_root_names, typ_vweb_context)
for vgt in table.used_features.used_veb_types {
sym_app := table.sym(vgt)
for m in sym_app.methods {

View File

@ -299,6 +299,10 @@ fn (mut w Walker) expr(node_ ast.Expr) {
w.fn_decl(mut node.decl)
}
ast.ArrayInit {
sym := w.table.final_sym(node.elem_type)
if sym.info is ast.Struct {
w.a_struct_info(sym.name, sym.info)
}
w.expr(node.len_expr)
w.expr(node.cap_expr)
w.expr(node.init_expr)
@ -419,6 +423,7 @@ fn (mut w Walker) expr(node_ ast.Expr) {
// println('>>> else, ast.Ident kind: $node.kind')
}
}
w.or_block(node.or_expr)
}
ast.LambdaExpr {
w.expr(node.func)
@ -470,6 +475,7 @@ fn (mut w Walker) expr(node_ ast.Expr) {
w.fn_by_name(method.fkey())
}
}
w.or_block(node.or_block)
}
ast.SqlExpr {
w.expr(node.db_expr)
@ -511,7 +517,14 @@ fn (mut w Walker) expr(node_ ast.Expr) {
w.expr(node.orig)
}
ast.Comment {}
ast.EnumVal {}
ast.EnumVal {
if e := w.table.enum_decls[node.enum_name] {
filtered := e.fields.filter(it.name == node.val)
if filtered.len != 0 && filtered[0].expr !is ast.EmptyExpr {
w.expr(filtered[0].expr)
}
}
}
ast.LockExpr {
w.stmts(node.stmts)
}
@ -557,6 +570,7 @@ pub fn (mut w Walker) a_struct_info(sname string, info ast.Struct) {
pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) {
if node.language == .c {
w.mark_fn_as_used(node.fkey())
return
}
fkey := node.fkey()
@ -587,6 +601,17 @@ pub fn (mut w Walker) call_expr(mut node ast.CallExpr) {
w.mark_aggregate_call_used(fn_name, types)
}
}
} else if left_sym.info is ast.Interface {
for typ in left_sym.info.types {
sym := w.table.sym(typ)
_, embed_types := w.table.find_method_from_embeds(sym, node.name) or {
ast.Fn{}, []ast.Type{}
}
if embed_types.len != 0 {
fn_embed := '${int(embed_types.last())}.${node.name}'
w.fn_by_name(fn_embed)
}
}
}
}
w.expr(node.left)
@ -609,15 +634,19 @@ pub fn (mut w Walker) call_expr(mut node ast.CallExpr) {
else {}
}
}
} else if node.is_fn_a_const {
const_fn_name := '${node.mod}.${fn_name}'
if const_fn_name in w.all_consts {
w.mark_const_as_used(const_fn_name)
}
}
w.mark_fn_as_used(fn_name)
if node.is_method && node.receiver_type.has_flag(.generic) && node.receiver_concrete_type != 0
&& !node.receiver_concrete_type.has_flag(.generic) {
// if receiver is generic, then cgen requires `node.receiver_type` to be T.
// We therefore need to get the concrete type from `node.receiver_concrete_type`.
fkey := '${int(node.receiver_concrete_type)}.${node.name}'
w.used_fns[fkey] = true
w.mark_fn_as_used(fkey)
}
stmt := w.all_fns[fn_name] or { return }
if stmt.name == node.name {