markused: clean up features tracking from checker - remove 1 all_fns loop (#24999)

This commit is contained in:
Felipe Pena 2025-07-31 02:18:40 -03:00 committed by GitHub
parent bb13e6601e
commit 844b7007a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 149 additions and 114 deletions

View File

@ -10,13 +10,12 @@ import v.util
@[heap; minify]
pub struct UsedFeatures {
pub mut:
dump bool // dump()
index bool // string[0]
dump bool // filled in by markused
range_index bool // string[0..1]
cast_ptr bool // &u8(...)
anon_fn bool // fn () { }
auto_str bool // auto str fns
auto_str_ptr bool // auto str fns for ptr type
auto_str_arr bool // auto str fns for array
arr_prepend bool // arr.prepend()
arr_insert bool // arr.insert()
arr_first bool // arr.first()

View File

@ -3069,7 +3069,6 @@ 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)
c.markused_dumpexpr(mut node)
@ -5014,7 +5013,6 @@ fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type {
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

View File

@ -1441,7 +1441,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
if args_len > 0 && fn_name in print_everything_fns {
node.args[0].ct_expr = c.comptime.is_comptime(node.args[0].expr)
c.builtin_args(mut node, fn_name, func)
c.markused_fn_call(mut node)
c.markused_print_call(mut node)
return func.return_type
}
// `return error(err)` -> `return err`

View File

@ -60,7 +60,6 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
if left_type.has_flag(.option) {
c.error('cannot push to Option array that was not unwrapped first', node.left.pos())
}
c.markused_infixexpr(!c.is_builtin_mod && c.mod != 'strings')
if mut node.right is ast.IfExpr {
if node.right.is_expr && node.right.branches.len > 0 {
mut last_stmt := node.right.branches[0].stmts.last()

View File

@ -36,6 +36,9 @@ fn (mut c Checker) markused_dumpexpr(mut node ast.DumpExpr) {
if node.expr_type.is_ptr() {
c.table.used_features.auto_str_ptr = true
}
if !c.table.used_features.auto_str_arr {
c.table.used_features.auto_str_arr = c.table.final_sym(unwrapped_type).kind == .array
}
} else {
c.table.used_features.print_types[node.expr_type.idx()] = true
}
@ -58,9 +61,6 @@ fn (mut c Checker) markused_castexpr(mut node ast.CastExpr, to_type ast.Type, mu
c.table.used_features.used_maps++
}
}
if c.mod !in ['strings', 'math.bits'] && to_type.is_ptr() {
c.table.used_features.cast_ptr = true
}
}
fn (mut c Checker) markused_comptimecall(mut node ast.ComptimeCall) {
@ -91,7 +91,6 @@ fn (mut c Checker) markused_comptimecall(mut node ast.ComptimeCall) {
}
fn (mut c Checker) markused_comptimefor(mut node ast.ComptimeFor, unwrapped_expr_type ast.Type) {
c.table.used_features.dump = true
if c.table.used_features.used_maps == 0 {
final_sym := c.table.final_sym(unwrapped_expr_type)
if final_sym.info is ast.Map {
@ -108,34 +107,45 @@ fn (mut c Checker) markused_call_expr(left_type ast.Type, mut node ast.CallExpr)
if left_type != 0 && left_type.is_ptr() && !c.table.used_features.auto_str_ptr
&& node.name == 'str' {
c.table.used_features.auto_str_ptr = true
if !c.table.used_features.auto_str_arr {
c.table.used_features.auto_str_arr = c.table.final_sym(left_type).kind == .array
}
}
}
fn (mut c Checker) markused_fn_call(mut node ast.CallExpr) {
fn (mut c Checker) markused_print_call(mut node ast.CallExpr) {
if !c.is_builtin_mod && c.mod != 'math.bits' && node.args[0].expr !is ast.StringLiteral {
arg_typ := c.unwrap_generic(node.args[0].typ)
if (node.args[0].expr is ast.CallExpr && node.args[0].expr.is_method
&& node.args[0].expr.name == 'str')
|| !c.table.sym(c.unwrap_generic(node.args[0].typ)).has_method('str') {
|| !c.table.sym(arg_typ).has_method('str') {
c.table.used_features.auto_str = true
} else {
if node.args[0].typ.has_option_or_result() {
if arg_typ.has_option_or_result() {
c.table.used_features.print_options = true
}
c.table.used_features.print_types[node.args[0].typ.idx()] = true
c.table.used_features.print_types[arg_typ.idx()] = true
if !c.table.used_features.auto_str_ptr && node.args[0].expr is ast.Ident {
var_obj := node.args[0].expr.obj
if var_obj is ast.Var {
if var_obj.orig_type != 0
&& c.table.final_sym(var_obj.orig_type).kind == .interface {
c.table.used_features.auto_str_ptr = true
if var_obj.orig_type != 0 {
fsym := c.table.final_sym(var_obj.orig_type)
if fsym.kind == .interface {
c.table.used_features.auto_str_ptr = true
} else if fsym.kind == .array {
c.table.used_features.auto_str_arr = true
}
return
}
}
}
}
if node.args[0].typ.is_ptr() {
if arg_typ.is_ptr() {
c.table.used_features.auto_str_ptr = true
}
if !c.table.used_features.auto_str_arr {
c.table.used_features.auto_str_arr = c.table.final_sym(arg_typ).kind == .array
}
}
}
@ -165,18 +175,14 @@ fn (mut c Checker) markused_string_inter_lit(mut node ast.StringInterLiteral, ft
if ftyp.is_ptr() {
c.table.used_features.auto_str_ptr = true
}
if !c.table.used_features.auto_str_arr {
c.table.used_features.auto_str_arr = c.table.final_sym(ftyp).kind == .array
}
if ftyp.has_option_or_result() {
c.table.used_features.print_options = true
}
}
fn (mut c Checker) markused_infixexpr(check bool) {
if !check {
return
}
c.table.used_features.index = true
}
fn (mut c Checker) markused_array_method(check bool, method_name string) {
if !check {
return

View File

@ -107,6 +107,10 @@ fn (mut g Gen) dump_expr_definitions() {
}
_, str_method_expects_ptr, _ := dump_sym.str_method_info()
typ := ast.idx_to_type(dump_type)
if g.pref.skip_unused
&& (!g.table.used_features.dump || typ.idx() !in g.table.used_features.used_syms) {
continue
}
is_ptr := typ.is_ptr()
deref, _ := deref_kind(str_method_expects_ptr, is_ptr, typ)
to_string_fn_name := g.get_str_fn(typ.clear_flags(.shared_f, .result))

View File

@ -85,21 +85,6 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
core_fns << '__new_array_with_array_default_noscan'
core_fns << 'new_array_from_c_array'
}
if table.used_features.index || pref_.is_shared {
include_panic_deps = true
core_fns << string_idx_str + '.at_with_check'
core_fns << string_idx_str + '.clone'
core_fns << string_idx_str + '.clone_static'
core_fns << string_idx_str + '.at'
core_fns << array_idx_str + '.set'
core_fns << array_idx_str + '.get_with_check' // used for `x := a[i] or {}`
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 || pref_.is_shared {
core_fns << string_idx_str + '.substr_with_check'
core_fns << string_idx_str + '.substr_ni'
@ -109,10 +94,6 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
core_fns << array_idx_str + '.clone_static_to_depth'
core_fns << array_idx_str + '.clone_to_depth'
}
if table.used_features.auto_str || table.used_features.dump {
core_fns << string_idx_str + '.repeat'
core_fns << 'tos3'
}
if table.used_features.arr_prepend {
core_fns << ref_array_idx_str + '.prepend_many'
}
@ -121,6 +102,7 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
}
if table.used_features.arr_pop {
core_fns << ref_array_idx_str + '.pop'
core_fns << ref_array_idx_str + '.pop_noscan'
}
if table.used_features.arr_first {
core_fns << array_idx_str + '.first'
@ -130,15 +112,7 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
}
if table.used_features.arr_insert {
core_fns << ref_array_idx_str + '.insert_many'
}
if table.used_features.dump {
include_panic_deps = true
builderptr_idx := int(table.find_type('strings.Builder').ref()).str()
core_fns << [
builderptr_idx + '.str',
builderptr_idx + '.free',
builderptr_idx + '.write_rune',
]
core_fns << ref_array_idx_str + '.insert_noscan'
}
if table.used_features.print_options {
include_panic_deps = true
@ -285,26 +259,6 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
handle_vweb(mut table, mut all_fn_root_names, 'vweb.Result', 'vweb.filter', 'vweb.Context')
handle_vweb(mut table, mut all_fn_root_names, 'x.vweb.Result', 'x.vweb.filter', 'x.vweb.Context')
// handle ORM drivers:
orm_connection_implementations := table.iface_types['orm.Connection'] or { []ast.Type{} }
if orm_connection_implementations.len > 0 {
for k, _ in all_fns {
if k.starts_with('orm.') {
all_fn_root_names << k
}
}
for orm_type in orm_connection_implementations {
typ := int(orm_type).str()
all_fn_root_names << typ + '.select'
all_fn_root_names << typ + '.insert'
all_fn_root_names << typ + '.update'
all_fn_root_names << typ + '.delete'
all_fn_root_names << typ + '.create'
all_fn_root_names << typ + '.drop'
all_fn_root_names << typ + '.last_id'
}
}
if 'debug_used_features' in pref_.compile_defines {
eprintln('> debug_used_features: ${table.used_features}')
}

View File

@ -30,8 +30,9 @@ mut:
all_decltypes map[string]ast.TypeDecl
all_structs map[string]ast.StructDecl
level int
is_builtin_mod bool
level int
is_builtin_mod bool
is_direct_array_access bool
// dependencies finding flags
uses_atomic bool // has atomic
@ -60,6 +61,11 @@ mut:
uses_dump bool
uses_memdup bool // sumtype cast and &Struct{}
uses_arr_void bool // auto arr methods
uses_index bool // var[k]
uses_str_index bool // string[k]
uses_fixed_arr_int bool // fixed_arr[k]
uses_check_index bool // arr[key] or { }
uses_append bool // var << item
}
pub fn Walker.new(params Walker) &Walker {
@ -71,7 +77,7 @@ pub fn Walker.new(params Walker) &Walker {
}
@[inline]
pub fn (mut w Walker) mark_fn_as_used(fkey string) {
fn (mut w Walker) mark_fn_as_used(fkey string) {
$if trace_skip_unused_marked ? {
eprintln(' fn > |${fkey}|')
}
@ -91,10 +97,8 @@ pub fn (mut w Walker) mark_builtin_map_method_as_used(method_name string) {
pub fn (mut w Walker) mark_builtin_type_method_as_used(k string, rk string) {
if mut cfn := w.all_fns[k] {
w.fn_decl(mut cfn)
w.mark_fn_as_used(k)
} else if mut cfn := w.all_fns[rk] {
w.fn_decl(mut cfn)
w.mark_fn_as_used(rk)
}
}
@ -482,7 +486,7 @@ fn (mut w Walker) expr(node_ ast.Expr) {
}
ast.DumpExpr {
w.expr(node.expr)
w.uses_dump = true
w.features.dump = true
w.mark_by_type(node.expr_type)
}
ast.SpawnExpr {
@ -508,10 +512,13 @@ fn (mut w Walker) expr(node_ ast.Expr) {
w.expr(node.call_expr)
}
ast.IndexExpr {
if !w.uses_index && !w.is_direct_array_access {
w.uses_index = true
}
w.expr(node.left)
w.expr(node.index)
if node.or_expr.kind == .block {
w.uses_guard = true
w.uses_check_index = true
}
w.mark_by_type(node.typ)
w.or_block(node.or_expr)
@ -529,17 +536,22 @@ fn (mut w Walker) expr(node_ ast.Expr) {
w.mark_by_sym(w.table.sym(sym.info.value_type))
w.features.used_maps++
} else if sym.info is ast.Array {
if node.is_setter {
w.mark_builtin_array_method_as_used('set')
} else {
w.mark_builtin_array_method_as_used('get')
if !w.is_direct_array_access || w.features.auto_str_arr {
if node.is_setter {
w.mark_builtin_array_method_as_used('set')
} else {
w.mark_builtin_array_method_as_used('get')
}
}
w.mark_by_sym(w.table.sym(sym.info.elem_type))
} else if sym.info is ast.ArrayFixed {
w.uses_fixed_arr_int = true
} else if sym.kind == .string {
if node.index is ast.RangeExpr {
w.mark_builtin_array_method_as_used('slice')
w.features.range_index = true
}
w.uses_str_index = true
} else if sym.info is ast.Struct {
w.mark_by_sym(sym)
} else if sym.info is ast.SumType {
@ -583,6 +595,9 @@ fn (mut w Walker) expr(node_ ast.Expr) {
if !w.uses_eq && node.op in [.eq, .ne] {
w.uses_eq = true
}
if !w.uses_append && node.op == .left_shift {
w.uses_append = true
}
}
ast.IfGuardExpr {
w.expr(node.expr)
@ -814,18 +829,25 @@ pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) {
if node.no_body {
return
}
if node.is_method {
w.mark_by_type(node.receiver.typ)
}
last_is_direct_array_access := w.is_direct_array_access
w.is_direct_array_access = node.is_direct_arr || w.pref.no_bounds_checking
defer {
w.is_direct_array_access = last_is_direct_array_access
}
if w.trace_enabled {
w.level++
defer { w.level-- }
defer {
w.level--
}
receiver_name := if node.is_method && node.receiver.typ != 0 {
w.table.type_to_str(node.receiver.typ) + '.'
} else {
''
}
eprintln('>>>${' '.repeat(w.level)}${receiver_name}${node.name} [decl]')
}
if node.is_method {
w.mark_by_type(node.receiver.typ)
eprintln('>>>${' '.repeat(w.level)}${receiver_name}${node.name} [${@FN}]')
}
w.mark_fn_ret_and_params(node.return_type, node.params)
w.mark_fn_as_used(fkey)
@ -936,17 +958,22 @@ pub fn (mut w Walker) call_expr(mut node ast.CallExpr) {
stmt := w.all_fns[fn_name] or { return }
if !stmt.should_be_skipped && stmt.name == node.name {
if !node.is_method || receiver_typ == stmt.receiver.typ {
last_is_direct_array_access := w.is_direct_array_access
w.is_direct_array_access = stmt.is_direct_arr || w.pref.no_bounds_checking
defer {
w.is_direct_array_access = last_is_direct_array_access
}
if w.trace_enabled {
w.level++
defer {
w.level--
}
receiver_name := if node.receiver_type != 0 {
w.table.type_to_str(node.receiver_type) + '.'
receiver_name := if stmt.is_method && stmt.receiver.typ != 0 {
w.table.type_to_str(stmt.receiver.typ) + '.'
} else {
''
}
eprintln('>>>${' '.repeat(w.level)}${receiver_name}${node.name} [call]')
eprintln('>>>${' '.repeat(w.level)}${receiver_name}${node.name} [${@FN}]')
}
w.mark_fn_ret_and_params(stmt.return_type, stmt.params)
w.stmts(stmt.stmts)
@ -964,17 +991,22 @@ pub fn (mut w Walker) fn_by_name(fn_name string) {
return
}
stmt := w.all_fns[fn_name] or { return }
last_is_direct_array_access := w.is_direct_array_access
w.is_direct_array_access = stmt.is_direct_arr || w.pref.no_bounds_checking
defer {
w.is_direct_array_access = last_is_direct_array_access
}
if w.trace_enabled {
w.level++
defer {
w.level--
}
receiver_name := if fn_name.contains('.') && fn_name.all_before_last('.').int() > 0 {
w.table.type_to_str(fn_name.all_before_last('.').int()) + '.'
receiver_name := if stmt.is_method && stmt.receiver.typ != 0 {
w.table.type_to_str(stmt.receiver.typ) + '.'
} else {
''
}
eprintln('>>>${' '.repeat(w.level)}${receiver_name}${fn_name.all_after_last('.')} [by_name]')
eprintln('>>>${' '.repeat(w.level)}${receiver_name}${stmt.name} [${@FN}]')
}
w.mark_fn_as_used(fn_name)
w.mark_fn_ret_and_params(stmt.return_type, stmt.params)
@ -1201,14 +1233,6 @@ fn (mut w Walker) remove_unused_fn_generic_types() {
}
}
fn (mut w Walker) remove_unused_dump_type() {
for typ, _ in w.table.dumps {
if ast.Type(u32(typ)).idx() !in w.used_syms {
w.table.dumps.delete(typ)
}
}
}
fn (mut w Walker) mark_resource_dependencies() {
if w.trace_enabled {
eprintln('>>>>>>>>>> DEPS USAGE')
@ -1274,12 +1298,16 @@ fn (mut w Walker) mark_resource_dependencies() {
if w.uses_mem_align {
w.fn_by_name('memdup_align')
}
if w.uses_guard {
if w.uses_guard || w.uses_check_index {
w.fn_by_name('error')
}
if w.uses_dump {
if w.features.dump {
w.fn_by_name('eprint')
w.fn_by_name('eprintln')
builderptr_idx := int(w.table.find_type('strings.Builder').ref()).str()
w.fn_by_name(builderptr_idx + '.str')
w.fn_by_name(builderptr_idx + '.free')
w.fn_by_name(builderptr_idx + '.write_rune')
}
if w.uses_spawn {
w.fn_by_name('malloc')
@ -1294,6 +1322,33 @@ fn (mut w Walker) mark_resource_dependencies() {
if w.uses_arr_void {
w.mark_by_type(w.table.find_or_register_array(ast.voidptr_type))
}
if w.features.auto_str || w.uses_dump {
w.fn_by_name(ast.string_type_idx.str() + '.repeat')
w.fn_by_name('tos3')
}
if w.uses_append {
ref_array_idx_str := int(ast.array_type.ref()).str()
w.fn_by_name(ref_array_idx_str + '.push')
w.fn_by_name(ref_array_idx_str + '.push_many_noscan')
w.fn_by_name(ref_array_idx_str + '.push_noscan')
}
if w.uses_index || w.pref.is_shared {
array_idx_str := ast.array_type_idx.str()
w.fn_by_name(array_idx_str + '.slice')
w.fn_by_name(array_idx_str + '.get')
if w.uses_guard || w.uses_check_index {
w.fn_by_name(array_idx_str + '.get_with_check')
}
}
if w.uses_str_index {
string_idx_str := ast.string_type_idx.str()
w.fn_by_name(string_idx_str + '.at')
w.fn_by_name(string_idx_str + '.at_with_check')
w.fn_by_name(string_idx_str + '.substr')
}
if w.uses_fixed_arr_int {
w.fn_by_name('v_fixed_index')
}
for typ, _ in w.table.used_features.print_types {
w.mark_by_type(typ)
}
@ -1316,6 +1371,8 @@ fn (mut w Walker) mark_resource_dependencies() {
mut map_fns := map[string]ast.FnDecl{}
has_str_call := w.uses_interp || w.uses_asserts || w.uses_str.len > 0
|| w.features.print_types.len > 0
orm_impls := w.table.iface_types['orm.Connection'] or { []ast.Type{} }
for k, mut func in w.all_fns {
if has_str_call && k.ends_with('.str') {
if func.receiver.typ.idx() in w.used_syms {
@ -1354,9 +1411,28 @@ fn (mut w Walker) mark_resource_dependencies() {
method_receiver_typename := w.table.type_to_str(func.receiver.typ)
if method_receiver_typename in ['&map', '&mapnode', '&SortedMap', '&DenseArray'] {
map_fns[k] = func
continue
}
} else if k.starts_with('map_') {
map_fns[k] = func
continue
}
if orm_impls.len > 0 && k.starts_with('orm.') {
w.fn_by_name(k)
continue
}
}
// handle ORM drivers:
if orm_impls.len > 0 {
for orm_type in orm_impls {
typ := int(orm_type).str()
w.fn_by_name(typ + '.select')
w.fn_by_name(typ + '.insert')
w.fn_by_name(typ + '.update')
w.fn_by_name(typ + '.delete')
w.fn_by_name(typ + '.create')
w.fn_by_name(typ + '.drop')
w.fn_by_name(typ + '.last_id')
}
}
if w.features.used_maps > 0 {
@ -1408,20 +1484,20 @@ pub fn (mut w Walker) finalize(include_panic_deps bool) {
w.mark_by_sym_name('VAssertMetaInfo')
}
if w.used_panic > 0 {
w.mark_fn_as_used('panic_option_not_set')
w.mark_fn_as_used('panic_result_not_set')
w.fn_by_name('panic_option_not_set')
w.fn_by_name('panic_result_not_set')
}
if w.used_none > 0 || w.table.used_features.auto_str {
w.mark_fn_as_used('_option_none')
w.fn_by_name('_option_none')
w.mark_by_sym_name('_option')
}
if w.used_option > 0 {
w.mark_fn_as_used('_option_clone')
w.mark_fn_as_used('_option_ok')
w.fn_by_name('_option_clone')
w.fn_by_name('_option_ok')
w.mark_by_sym_name('_option')
}
if w.used_result > 0 {
w.mark_fn_as_used('_result_ok')
w.fn_by_name('_result_ok')
w.mark_by_sym_name('_result')
}
if (w.used_option + w.used_result + w.used_none) > 0 {
@ -1430,7 +1506,7 @@ pub fn (mut w Walker) finalize(include_panic_deps bool) {
if include_panic_deps || w.uses_external_type || w.uses_asserts || w.uses_debugger
|| w.uses_interp {
if w.trace_enabled {
eprintln('>>>>> PANIC DEPS ${include_panic_deps} | external_type=${w.uses_external_type} | asserts=${w.uses_asserts} | dbg=${w.uses_debugger}')
eprintln('>>>>> PANIC DEPS ${include_panic_deps} | external_type=${w.uses_external_type} | asserts=${w.uses_asserts} | dbg=${w.uses_debugger} interp=${w.uses_interp}')
}
ref_array_idx_str := int(ast.array_type.ref()).str()
string_idx_str := ast.string_type_idx.str()
@ -1450,7 +1526,6 @@ pub fn (mut w Walker) finalize(include_panic_deps bool) {
// remove unused symbols
w.remove_unused_fn_generic_types()
w.remove_unused_dump_type()
if w.trace_enabled {
syms := w.used_syms.keys().map(w.table.type_to_str(it))