From 07ce904431d3668595730812047df69f91fe8b78 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Sun, 3 Aug 2025 04:10:21 -0300 Subject: [PATCH] markused: improve array tracking (range, gated) (#25023) --- vlib/v/ast/table.v | 1 - vlib/v/checker/checker.v | 20 ++-- vlib/v/gen/c/cgen.v | 7 +- vlib/v/markused/markused.v | 9 -- vlib/v/markused/walker.v | 187 +++++++++++++++++++++++-------------- 5 files changed, 129 insertions(+), 95 deletions(-) diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 4b63800807..9ec37ca587 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -11,7 +11,6 @@ import v.util pub struct UsedFeatures { pub mut: dump bool // filled in by markused - range_index bool // string[0..1] anon_fn bool // fn () { } auto_str bool // auto str fns auto_str_ptr bool // auto str fns for ptr type diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index bda2d44f55..83defeb0ec 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -5002,18 +5002,18 @@ fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type { typ_sym = unsafe { unwrapped_sym } } } - .string { - if node.is_gated && c.mod != 'strings' { - c.table.used_features.range_index = true - } - } + // .string { + // if node.is_gated && c.mod != 'strings' { + // c.table.used_features.range_index = true + // } + // } else {} } - if !c.is_builtin_mod && c.mod !in ['strings', 'math.bits'] { - if node.index is ast.RangeExpr { - c.table.used_features.range_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 + // } + // } 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] diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index e132c0ad98..a81af0e02d 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -7783,13 +7783,12 @@ fn (mut g Gen) register_iface_return_types() { if inter_info.is_generic { continue } + if g.pref.skip_unused && isym.idx !in g.table.used_features.used_syms { + continue + } for _, method_name in inter_info.get_methods() { method := isym.find_method_with_generic_parent(method_name) or { continue } if method.return_type.has_flag(.result) { - if g.pref.skip_unused - && g.table.sym(method.return_type).idx !in g.table.used_features.used_syms { - continue - } g.register_result(method.return_type) } } diff --git a/vlib/v/markused/markused.v b/vlib/v/markused/markused.v index 33c9477105..04c4d02897 100644 --- a/vlib/v/markused/markused.v +++ b/vlib/v/markused/markused.v @@ -85,15 +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.range_index || pref_.is_shared { - core_fns << string_idx_str + '.substr_with_check' - core_fns << string_idx_str + '.substr_ni' - core_fns << string_idx_str + '.substr' - core_fns << array_idx_str + '.slice_ni' - core_fns << array_idx_str + '.get_with_check' // used for `x := a[i] or {}` - core_fns << array_idx_str + '.clone_static_to_depth' - core_fns << array_idx_str + '.clone_to_depth' - } if table.used_features.arr_prepend { core_fns << ref_array_idx_str + '.prepend_many' } diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index 748c0a8293..d8e1dc2d2d 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -36,39 +36,50 @@ mut: is_direct_array_access bool // dependencies finding flags - uses_atomic bool // has atomic - uses_array bool // has array - uses_channel bool // has chan dep - uses_lock bool // has mutex dep - uses_ct_fields bool // $for .fields - uses_ct_methods bool // $for .methods - uses_ct_params bool // $for .params - uses_ct_values bool // $for .values - uses_ct_variants bool // $for .variants - uses_ct_attribute bool // $for .attributes - uses_external_type bool - uses_err bool // err var - uses_asserts bool // assert - uses_map_update bool // has {...expr} - uses_debugger bool // has debugger; - uses_mem_align bool // @[aligned:N] for structs - uses_eq bool // has == op - uses_interp bool // string interpolation - uses_guard bool - uses_orm bool - uses_str map[ast.Type]bool // has .str() calls, and for which types - uses_free map[ast.Type]bool // has .free() calls, and for which types - uses_spawn bool - 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_str_index_check bool // string[k] or { } - uses_str_range bool // string[a..b] - uses_fixed_arr_int bool // fixed_arr[k] - uses_check_index bool // arr[key] or { } - uses_append bool // var << item + uses_atomic bool // has atomic + uses_array bool // has array + uses_channel bool // has chan dep + uses_lock bool // has mutex dep + uses_ct_fields bool // $for .fields + uses_ct_methods bool // $for .methods + uses_ct_params bool // $for .params + uses_ct_values bool // $for .values + uses_ct_variants bool // $for .variants + uses_ct_attribute bool // $for .attributes + uses_external_type bool + uses_err bool // err var + uses_asserts bool // assert + uses_map_update bool // has {...expr} + uses_debugger bool // has debugger; + uses_mem_align bool // @[aligned:N] for structs + uses_eq bool // has == op + uses_interp bool // string interpolation + uses_guard bool + uses_orm bool + uses_str map[ast.Type]bool // has .str() calls, and for which types + uses_free map[ast.Type]bool // has .free() calls, and for which types + uses_spawn bool + uses_dump bool + uses_memdup bool // sumtype cast and &Struct{} + uses_arr_void bool // auto arr methods + uses_index bool // var[k] + uses_index_check bool // var[k] or { } + uses_arr_range_index bool // arr[i..j] + uses_str_range_index bool // str[i..j] + uses_range_index_check bool // var[i..j] or { } + uses_arr_range_index_gated bool + uses_str_range_index_gated bool + uses_str_index bool // string[k] + uses_str_index_check bool // string[k] or { } + uses_str_range bool // string[a..b] + uses_fixed_arr_int bool // fixed_arr[k] + uses_append bool // var << item + uses_map_setter bool + uses_map_getter bool + uses_arr_setter bool + uses_arr_getter bool + uses_arr_clone bool + uses_arr_sorted bool } pub fn Walker.new(params Walker) &Walker { @@ -440,6 +451,12 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.uses_str[node.left_type] = true } else if node.is_method && node.name == 'free' { w.uses_free[node.left_type] = true + } else if node.is_method && node.name == 'clone' && !w.uses_arr_clone + && node.left_type != 0 && w.table.final_sym(node.left_type).kind == .array { + w.uses_arr_clone = true + } else if node.is_method && node.name == 'sorted' && !w.uses_arr_sorted + && node.left_type != 0 && w.table.final_sym(node.left_type).kind == .array { + w.uses_arr_sorted = true } if !w.is_builtin_mod && !w.uses_external_type { if node.is_method { @@ -520,7 +537,7 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.expr(node.left) w.expr(node.index) if node.or_expr.kind == .block { - w.uses_check_index = true + w.uses_index_check = true } w.mark_by_type(node.typ) w.or_block(node.or_expr) @@ -529,9 +546,9 @@ fn (mut w Walker) expr(node_ ast.Expr) { } sym := w.table.final_sym(node.left_type) if sym.info is ast.Map { - if node.is_setter { + if node.is_setter && !w.uses_map_setter { w.mark_builtin_map_method_as_used('set') - } else { + } else if !node.is_setter && !w.uses_map_getter { w.mark_builtin_map_method_as_used('get') } w.mark_by_sym(w.table.sym(sym.info.key_type)) @@ -539,10 +556,12 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.features.used_maps++ } else if sym.kind in [.array, .array_fixed, .any] { if !w.is_direct_array_access || w.features.auto_str_arr { - if node.is_setter { + if node.is_setter && !w.uses_arr_setter { w.mark_builtin_array_method_as_used('set') - } else { + w.uses_arr_setter = true + } else if !node.is_setter && !w.uses_arr_getter { w.mark_builtin_array_method_as_used('get') + w.uses_arr_getter = true } } if sym.info is ast.Array { @@ -550,23 +569,37 @@ fn (mut w Walker) expr(node_ ast.Expr) { } else if sym.info is ast.ArrayFixed { w.mark_by_sym(w.table.sym(sym.info.elem_type)) } + if !w.uses_arr_range_index { + w.uses_arr_range_index = true + } if !w.uses_fixed_arr_int && sym.kind == .array_fixed { w.uses_fixed_arr_int = true } if !w.uses_index && !w.is_direct_array_access { w.uses_index = true } + if !w.uses_arr_range_index_gated { + w.uses_arr_range_index_gated = node.is_gated + } } 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 - if !w.uses_str_index_check { - w.uses_str_index_check = node.or_expr.kind == .block - } - if !w.uses_str_range { - w.uses_str_range = node.index is ast.RangeExpr + if node.index is ast.RangeExpr { + if !w.uses_str_range_index { + w.uses_str_range_index = true + } + if !w.uses_range_index_check { + w.uses_range_index_check = node.or_expr.kind == .block + } + if !w.uses_str_range_index_gated { + w.uses_str_range_index_gated = node.is_gated + } + } else { + if !w.uses_str_index_check { + w.uses_str_index_check = node.or_expr.kind == .block + } + if !w.uses_str_range { + w.uses_str_range = node.index is ast.RangeExpr + } } } else if sym.info is ast.Struct { w.mark_by_sym(sym) @@ -574,9 +607,9 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.mark_by_sym(sym) } else if sym.kind == .any { if !w.is_direct_array_access { - if node.is_setter { + if node.is_setter && !w.uses_arr_setter { w.mark_builtin_array_method_as_used('set') - } else { + } else if !node.is_setter && !w.uses_arr_getter { w.mark_builtin_array_method_as_used('get') } } @@ -628,7 +661,7 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.expr(node.expr) w.mark_by_type(node.expr_type) w.uses_guard = true - if node.expr_type != 0 && !w.uses_str_index_check && node.expr is ast.IndexExpr + if !w.uses_str_index_check && node.expr is ast.IndexExpr && node.expr_type != 0 && w.table.final_sym(node.expr_type).kind in [.u8, .string] { w.uses_str_index_check = true } @@ -1233,6 +1266,9 @@ fn (mut w Walker) remove_unused_fn_generic_types() { } fn (mut w Walker) mark_resource_dependencies() { + string_idx_str := ast.string_type_idx.str() + array_idx_str := ast.array_type_idx.str() + if w.trace_enabled { eprintln('>>>>>>>>>> DEPS USAGE') } @@ -1247,9 +1283,6 @@ fn (mut w Walker) mark_resource_dependencies() { w.fn_by_name('strings.new_builder') w.uses_free[ast.string_type] = true } - // if w.uses_err { - // w.fn_by_name('${int(ast.error_type)}.str') - // } if w.uses_eq { w.fn_by_name('fast_string_eq') } @@ -1314,15 +1347,10 @@ fn (mut w Walker) mark_resource_dependencies() { w.fn_by_name('tos3') } 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') if w.uses_str_index_check { w.fn_by_name(string_idx_str + '.at_with_check') @@ -1335,16 +1363,12 @@ fn (mut w Walker) mark_resource_dependencies() { w.mark_by_type(typ) } if w.trace_enabled { - types := w.table.used_features.print_types.keys().map(w.table.type_to_str(it)) - eprintln('>>>>>>>>>> PRINT TYPES ${types}') - } - if w.trace_enabled { - types := w.uses_str.keys().map(w.table.type_to_str(it)) - eprintln('>>>>>>>>>> USES .str() CALLS ON TYPES ${types}') - } - if w.trace_enabled { - types := w.uses_free.keys().map(w.table.type_to_str(it)) - eprintln('>>>>>>>>>> USES .free() CALLS ON TYPES ${types}') + ptypes := w.table.used_features.print_types.keys().map(w.table.type_to_str(it)) + eprintln('>>>>>>>>>> PRINT TYPES ${ptypes}') + stypes := w.uses_str.keys().map(w.table.type_to_str(it)) + eprintln('>>>>>>>>>> USES .str() CALLS ON TYPES ${stypes}') + ftypes := w.uses_free.keys().map(w.table.type_to_str(it)) + eprintln('>>>>>>>>>> USES .free() CALLS ON TYPES ${ftypes}') } if w.trace_enabled { eprintln('>>>>>>>>>> ALL_FNS LOOP') @@ -1386,15 +1410,15 @@ fn (mut w Walker) mark_resource_dependencies() { if func.receiver.typ.set_nr_muls(0) in w.table.used_features.comptime_syms || func.receiver.typ in w.table.used_features.comptime_syms { w.fn_by_name(k) - continue } + continue } if func.is_method && !func.receiver.typ.has_flag(.generic) && func.receiver.typ.is_ptr() { 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 } + continue } else if k.starts_with('map_') { map_fns[k] = func continue @@ -1404,8 +1428,9 @@ fn (mut w Walker) mark_resource_dependencies() { continue } } - if w.uses_guard || w.uses_check_index { + if w.uses_guard || w.uses_index_check { w.fn_by_name('error') + w.fn_by_name(array_idx_str + '.get_with_check') } if w.uses_append { ref_array_idx_str := int(ast.array_type.ref()).str() @@ -1428,10 +1453,30 @@ fn (mut w Walker) mark_resource_dependencies() { w.fn_by_name('__new_array_with_default') w.fn_by_name('__new_array_with_default_noscan') w.fn_by_name(int(ast.array_type.ref()).str() + '.set') + w.fn_by_name('clone_static_to_depth') } if w.uses_fixed_arr_int { w.fn_by_name('v_fixed_index') } + if w.uses_str_range_index { + w.fn_by_name(string_idx_str + '.substr') + } + if w.uses_arr_range_index { + w.fn_by_name(array_idx_str + '.slice') + } + if w.uses_range_index_check { + w.fn_by_name(string_idx_str + '.substr_with_check') + w.fn_by_name(array_idx_str + '.get_with_check') + } + if w.uses_str_range_index_gated { + w.fn_by_name(string_idx_str + '.substr_ni') + } + if w.uses_arr_range_index_gated { + w.fn_by_name(array_idx_str + '.slice_ni') + } + if w.uses_array || w.uses_arr_clone || w.uses_arr_sorted { + w.fn_by_name(array_idx_str + '.clone_static_to_depth') + } // handle ORM drivers: if orm_impls.len > 0 { for orm_type in orm_impls {