diff --git a/cmd/tools/vast/vast.v b/cmd/tools/vast/vast.v index c336dc2f3a..8d0db8d9bf 100644 --- a/cmd/tools/vast/vast.v +++ b/cmd/tools/vast/vast.v @@ -1468,6 +1468,7 @@ fn (t Tree) index_expr(node ast.IndexExpr) &Node { obj.add_terse('is_setter', t.bool_node(node.is_setter)) obj.add_terse('is_direct', t.bool_node(node.is_direct)) obj.add_terse('or_expr', t.or_expr(node.or_expr)) + obj.add_terse('typ', t.type_node(node.typ)) obj.add('pos', t.pos(node.pos)) return obj } diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index 0cc199f79d..54be8d021d 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -134,7 +134,7 @@ pub fn tos2(s &u8) string { // It will panic, when the pointer `s` is 0. // It is the same as `tos2`, but for &char pointers, avoiding callsite casts. // See also `tos_clone`. -@[markused; unsafe] +@[unsafe] pub fn tos3(s &char) string { if s == 0 { panic('tos3: nil string') @@ -151,7 +151,7 @@ pub fn tos3(s &char) string { // It returns '', when given a 0 pointer `s`, it does NOT panic. // It is the same as `tos5`, but for &u8 pointers, avoiding callsite casts. // See also `tos_clone`. -@[markused; unsafe] +@[unsafe] pub fn tos4(s &u8) string { if s == 0 { return '' @@ -168,7 +168,7 @@ pub fn tos4(s &u8) string { // It returns '', when given a 0 pointer `s`, it does NOT panic. // It is the same as `tos4`, but for &char pointers, avoiding callsite casts. // See also `tos_clone`. -@[markused; unsafe] +@[unsafe] pub fn tos5(s &char) string { if s == 0 { return '' diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index fdbe84f6b7..e1a6e9ebe6 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -1207,6 +1207,7 @@ pub mut: is_option bool // IfGuard is_direct bool // Set if the underlying memory can be safely accessed is_gated bool // #[] gated array + typ Type } @[minify] diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index be6096c86e..f962fcfcc2 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -14,7 +14,6 @@ pub mut: index bool // string[0] range_index bool // string[0..1] cast_ptr bool // &u8(...) - asserts bool // assert expr anon_fn bool // fn () { } auto_str bool // auto str fns auto_str_ptr bool // auto str fns for ptr type @@ -25,10 +24,8 @@ pub mut: arr_pop bool // arr.pop() arr_delete bool // arr.delete() arr_reverse bool // arr.reverse() - arr_init bool // [1, 2, 3] arr_map bool // []map[key]value type_name bool // var.type_name() - map_update bool // {...foo} print_options bool // print option type print_types map[int]bool // print() idx types used_fns map[string]bool // filled in by markused @@ -39,11 +36,8 @@ pub mut: used_maps int // how many times maps were used, filled in by markused used_none int // how many times `none` was used, filled in by markused // json bool // json is imported - debugger bool // debugger is used comptime_calls map[string]bool // resolved name to call on comptime - comptime_syms map[int]bool // resolved syms (generic) - comptime_for bool // uses $for - memory_align bool // @[aligned] for struct + comptime_syms map[Type]bool // resolved syms (generic) } @[unsafe] diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 3bd626ad6b..9e6e31e500 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2385,9 +2385,7 @@ fn (mut c Checker) stmt(mut node ast.Stmt) { } } ast.NodeError {} - ast.DebuggerStmt { - c.table.used_features.debugger = true - } + ast.DebuggerStmt {} ast.AsmStmt { c.asm_stmt(mut node) } @@ -2398,9 +2396,6 @@ 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.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)) @@ -5140,6 +5135,7 @@ fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type { } c.stmts_ending_with_expression(mut node.or_expr.stmts, c.expected_or_type) c.check_expr_option_or_result_call(node, typ) + node.typ = typ return typ } diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 43aad5175b..78a208b18c 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -277,7 +277,6 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { prev_inside_x_matches_type := c.inside_x_matches_type c.push_new_comptime_info() c.comptime.inside_comptime_for = true - c.table.used_features.comptime_for = true if c.field_data_type == 0 { c.field_data_type = c.table.find_type('FieldData') } @@ -322,7 +321,6 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { c.type_resolver.update_ct_type('${node.val_var}.typ', node.typ) c.stmts(mut node.stmts) c.pop_comptime_info() - c.table.used_features.comptime_for = true } else { c.error('iterating over .values is supported only for enums, and ${sym.name} is not an enum', node.typ_pos) @@ -340,7 +338,6 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { c.stmts(mut node.stmts) c.pop_comptime_info() } - c.table.used_features.comptime_for = true } else if node.kind == .params { if !(sym.kind == .function || sym.name == 'FunctionData') { c.error('iterating over `.params` is supported only for functions, and `${sym.name}` is not a function', diff --git a/vlib/v/checker/containers.v b/vlib/v/checker/containers.v index 3947033a84..5c44998a2b 100644 --- a/vlib/v/checker/containers.v +++ b/vlib/v/checker/containers.v @@ -8,14 +8,14 @@ import v.token fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { mut elem_type := ast.void_type unwrap_elem_type := c.unwrap_generic(node.elem_type) + if node.typ.has_flag(.generic) { + c.table.used_features.comptime_syms[c.unwrap_generic(node.typ)] = true + } if c.pref.warn_about_allocs { c.warn_alloc('array initialization', node.pos) } // `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) c.check_any_type(node.elem_type, elem_sym, node.pos) @@ -159,9 +159,6 @@ 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 @@ -502,6 +499,9 @@ fn (mut c Checker) map_init(mut node ast.MapInit) ast.Type { // `x := map[string]string` - set in parser if node.typ != 0 { info := c.table.sym(node.typ).map_info() + if node.typ.has_flag(.generic) { + c.table.used_features.comptime_syms[c.unwrap_generic(node.typ)] = true + } if info.value_type != 0 { if info.value_type.has_flag(.result) { c.error('cannot use Result type as map value type', node.pos) @@ -541,7 +541,6 @@ 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 diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 45a3ef8f4f..18996c05db 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -342,9 +342,15 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { if c.pref.skip_unused { if param.typ.has_flag(.generic) { c.table.used_features.comptime_syms[c.unwrap_generic(param.typ)] = true + c.table.used_features.comptime_syms[param.typ] = true } if node.return_type.has_flag(.generic) { c.table.used_features.comptime_syms[c.unwrap_generic(node.return_type)] = true + c.table.used_features.comptime_syms[node.return_type] = true + } + if node.receiver.typ.has_flag(.generic) { + c.table.used_features.comptime_syms[node.receiver.typ] = true + c.table.used_features.comptime_syms[c.unwrap_generic(node.receiver.typ)] = true } } if param.name == node.mod && param.name != 'main' { @@ -1895,6 +1901,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. c.register_trace_call(node, func) if func.return_type.has_flag(.generic) { c.table.used_features.comptime_syms[typ.clear_option_and_result()] = true + c.table.used_features.comptime_syms[func.return_type] = true } return typ } @@ -2028,6 +2035,10 @@ fn (mut c Checker) method_call(mut node ast.CallExpr, mut continue_check &bool) node.return_type = left_type node.receiver_type = left_type + if is_generic { + c.table.used_features.comptime_syms[c.unwrap_generic(left_type)] = true + } + if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.generic_names.len > 0 { c.table.unwrap_generic_type(left_type, c.table.cur_fn.generic_names, c.table.cur_concrete_types) } diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index b46cfb03f5..205330d45e 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -68,10 +68,6 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) { if node.language != .c && attr.name == 'typedef' { c.error('`typedef` attribute can only be used with C structs', node.pos) } - aligned := if attr.arg == '' { 0 } else { attr.arg.int() } - if aligned > 1 { - c.table.used_features.memory_align = true - } } // Evaluate the size of the unresolved fixed array @@ -533,6 +529,7 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini true) if c.pref.skip_unused && node.typ.has_flag(.generic) { c.table.used_features.comptime_syms[c.unwrap_generic(node.typ)] = true + c.table.used_features.comptime_syms[node.typ] = true } } if !is_field_zero_struct_init { @@ -865,8 +862,6 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.', mut info := first_sym.info as ast.Struct c.check_uninitialized_struct_fields_and_embeds(node, first_sym, mut info, mut inited_fields) - } else if first_sym.kind == .array { - c.table.used_features.arr_init = true } } .none { diff --git a/vlib/v/checker/used_features.v b/vlib/v/checker/used_features.v index 311e67ef06..43139777e9 100644 --- a/vlib/v/checker/used_features.v +++ b/vlib/v/checker/used_features.v @@ -175,7 +175,6 @@ fn (mut c Checker) markused_infixexpr(check bool) { return } c.table.used_features.index = true - c.table.used_features.arr_init = true } fn (mut c Checker) markused_array_method(check bool, method_name string) { @@ -184,7 +183,6 @@ fn (mut c Checker) markused_array_method(check bool, method_name string) { } match method_name { '' { // array init - c.table.used_features.arr_init = true } 'first' { c.table.used_features.arr_first = true diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 74db39f4fc..e132c0ad98 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -1702,6 +1702,9 @@ pub fn (mut g Gen) write_typedef_types() { type_symbols := g.table.type_symbols.filter(!it.is_builtin && it.kind in [.array, .array_fixed, .chan, .map]) for sym in type_symbols { + if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms { + continue + } match sym.kind { .array { info := sym.info as ast.Array @@ -1752,9 +1755,6 @@ pub fn (mut g Gen) write_typedef_types() { chan_inf := sym.chan_info() chan_elem_type := chan_inf.elem_type esym := g.table.sym(chan_elem_type) - if g.pref.skip_unused && esym.idx !in g.table.used_features.used_syms { - continue - } g.type_definitions.writeln('typedef chan ${sym.cname};') is_fixed_arr := esym.kind == .array_fixed if !chan_elem_type.has_flag(.generic) { @@ -1776,9 +1776,6 @@ static inline void __${sym.cname}_pushval(${sym.cname} ch, ${push_arg} val) { } } .map { - if g.pref.skip_unused && g.table.used_features.used_maps == 0 { - continue - } g.type_definitions.writeln('typedef map ${sym.cname};') } else { @@ -2614,7 +2611,8 @@ fn (mut g Gen) stmt(node ast.Stmt) { ast.InterfaceDecl { // definitions are sorted and added in write_types ts := g.table.sym(node.typ) - if !(ts.info as ast.Interface).is_generic { + if !(ts.info as ast.Interface).is_generic + && (!g.pref.skip_unused || ts.idx in g.table.used_features.used_syms) { for method in node.methods { if method.return_type.has_flag(.option) { // Register an option if it's not registered yet @@ -6711,7 +6709,8 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) { } match sym.info { ast.Struct { - if !struct_names[name] { + if !struct_names[name] + && (!g.pref.skip_unused || sym.idx in g.table.used_features.used_syms) { // generate field option types for fixed array of option struct before struct declaration opt_fields := sym.info.fields.filter(g.table.final_sym(it.typ).kind == .array_fixed) for opt_field in opt_fields { @@ -6733,15 +6732,13 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) { } } } - if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms { - continue - } g.struct_decl(sym.info, name, false, false) struct_names[name] = true } } ast.Thread { - if !g.pref.is_bare && !g.pref.no_builtin { + if !g.pref.is_bare && !g.pref.no_builtin + && (!g.pref.skip_unused || sym.idx in g.table.used_features.used_syms) { if g.pref.os == .windows { if name == '__v_thread' { g.thread_definitions.writeln('typedef HANDLE ${name};') @@ -6814,7 +6811,8 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) { ast.ArrayFixed { elem_sym := g.table.sym(sym.info.elem_type) if !elem_sym.is_builtin() && !sym.info.elem_type.has_flag(.generic) - && !sym.info.is_fn_ret { + && (!g.pref.skip_unused || (!sym.info.is_fn_ret + && sym.idx in g.table.used_features.used_syms)) { // .array_fixed { styp := sym.cname // array_fixed_char_300 => char x[300] @@ -6848,10 +6846,6 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) { } g.type_definitions.writeln('typedef ${fixed_elem_name} ${styp} [${len}];') } else if !(elem_sym.info is ast.ArrayFixed && elem_sym.info.is_fn_ret) { - if g.pref.skip_unused - && elem_sym.idx !in g.table.used_features.used_syms { - continue - } g.type_definitions.writeln('typedef ${fixed_elem_name} ${styp} [${len}];') } } diff --git a/vlib/v/gen/c/testdata/alias_c_parent_type_decl.vv b/vlib/v/gen/c/testdata/alias_c_parent_type_decl.vv index f65ae5f823..0f1d3c34a7 100644 --- a/vlib/v/gen/c/testdata/alias_c_parent_type_decl.vv +++ b/vlib/v/gen/c/testdata/alias_c_parent_type_decl.vv @@ -1,3 +1,4 @@ pub struct C.atype {} +@[markused] type BType = &C.atype diff --git a/vlib/v/gen/c/testdata/iface_method_order.vv b/vlib/v/gen/c/testdata/iface_method_order.vv index 615d5dbee6..46fa182a2e 100644 --- a/vlib/v/gen/c/testdata/iface_method_order.vv +++ b/vlib/v/gen/c/testdata/iface_method_order.vv @@ -10,6 +10,7 @@ fn (b Bar) b() {} fn (b Bar) a() {} +@[markused] struct Baz implements Foo { } diff --git a/vlib/v/markused/markused.v b/vlib/v/markused/markused.v index e696673cc2..7cb0252b17 100644 --- a/vlib/v/markused/markused.v +++ b/vlib/v/markused/markused.v @@ -8,7 +8,7 @@ import v.pref // mark_used walks the AST, starting at main() and marks all used fns transitively. pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&ast.File) { - mut all_fns, all_consts, all_globals, all_fields, all_decltypes, all_structs := all_global_decl(ast_files) + mut all_fns, all_consts, all_globals, all_decltypes, all_structs := all_global_decl(ast_files) util.timing_start('MARKUSED') defer { util.timing_measure('MARKUSED') @@ -17,11 +17,9 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a trace_skip_unused := pref_.compile_values['trace_skip_unused'] == 'true' trace_skip_unused_all_fns := pref_.compile_values['trace_skip_unused_all_fns'] == 'true' trace_skip_unused_fn_names := pref_.compile_values['trace_skip_unused_fn_names'] == 'true' - trace_skip_unused_interface_methods := pref_.compile_values['trace_skip_unused_interface_methods'] == 'true' trace_skip_unused_just_unused_fns := pref_.compile_values['trace_skip_unused_just_unused_fns'] == 'true' used_fns := pref_.compile_values['used_fns'] - byteptr_idx_str := ast.byteptr_type_idx.str() charptr_idx_str := ast.charptr_type_idx.str() string_idx_str := ast.string_type_idx.str() array_idx_str := ast.array_type_idx.str() @@ -58,23 +56,13 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a } else { mut core_fns := [ 'main.main', - 'init_global_allocator', // needed for linux_bare and wasm_bare - 'memdup', - 'tos', - 'tos2', - 'error', - 'fast_string_eq', - 'println', - 'ptr_str', ] + if pref_.is_bare { + core_fns << 'init_global_allocator' // needed for linux_bare and wasm_bare + } if ast_files[ast_files.len - 1].imports.len > 0 { core_fns << 'builtin_init' } - if ast.float_literal_type.idx() in table.used_features.print_types - || ast.f64_type_idx in table.used_features.print_types - || ast.f32_type_idx in table.used_features.print_types { - include_panic_deps = true - } if 'use_libbacktrace' in pref_.compile_defines { core_fns << 'print_libbacktrace' } @@ -96,13 +84,6 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a 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' - core_fns << byteptr_idx_str + '.vstring_with_len' - core_fns << byteptr_idx_str + '.vstring_literal' - 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 || pref_.is_shared { include_panic_deps = true @@ -132,10 +113,6 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a core_fns << string_idx_str + '.repeat' core_fns << 'tos3' } - if table.used_features.auto_str_ptr { - include_panic_deps = true - core_fns << 'isnil' - } if table.used_features.arr_prepend { core_fns << ref_array_idx_str + '.prepend_many' } @@ -163,35 +140,10 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a builderptr_idx + '.write_rune', ] } - if !table.used_features.arr_init { - table.used_features.arr_init = table.used_features.print_types.keys().any(table.type_to_str(it).contains('[]')) - } - 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_default' - core_fns << '__new_array_with_default_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 << ref_array_idx_str + '.set' - } - if table.used_features.comptime_for { - include_panic_deps = true - 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' - core_fns << ref_array_idx_str + '.set' - } if table.used_features.print_options { include_panic_deps = true core_fns << '_option_ok' core_fns << '_result_ok' - core_fns << charptr_idx_str + '.vstring_literal' } if table.used_features.anon_fn { core_fns << 'memdup_uncollectable' @@ -207,21 +159,9 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a core_fns << ref_densearray_idx_str + '.clone' core_fns << map_idx_str + '.clone' } - if table.used_features.map_update { - include_panic_deps = true - core_fns << 'new_map_update_init' - } - if table.used_features.asserts { - include_panic_deps = true - core_fns << '__print_assert_failure' - core_fns << 'isnil' - } if table.used_features.type_name { core_fns << charptr_idx_str + '.vstring_literal' } - if table.used_features.memory_align { - core_fns << 'memdup_align' - } if pref_.trace_calls || pref_.trace_fns.len > 0 { include_panic_deps = true core_fns << 'vgettid' @@ -234,6 +174,7 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a core_fns << '_result_ok' core_fns << 'tos5' core_fns << 'time.unix' // used by json + core_fns << 'error' include_panic_deps = true } if pref_.should_use_segfault_handler() { @@ -277,38 +218,12 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a } if mfn.is_method { method_receiver_typename := table.type_to_str(mfn.receiver.typ) - if method_receiver_typename == '&wyrand.WyRandRNG' { - // WyRandRNG is the default rand pseudo random generator - all_fn_root_names << k - continue - } - if table.used_features.auto_str && method_receiver_typename == '&strings.Builder' { - // implicit string builders are generated in auto_eq_methods.v - all_fn_root_names << k - continue - } if method_receiver_typename == '&sync.Channel' { all_fn_root_names << k continue } - if mfn.name in ['+', '-', '*', '%', '/', '<', '=='] { - // TODO: mark the used operators in the checker - all_fn_root_names << k - continue - } } has_dot := k.contains('.') - // auto generated string interpolation functions, may - // call .str or .auto_str methods for user types: - if table.used_features.auto_str || table.used_features.dump || table.used_features.asserts - || table.used_features.debugger - || table.used_features.print_types[mfn.receiver.typ.idx()] { - if (has_dot && (k.ends_with('.str') || k.ends_with('.auto_str'))) - || (k.starts_with('_Atomic_') && k.ends_with('_str')) { - all_fn_root_names << k - continue - } - } if has_dot { if k.ends_with('.init') || k.ends_with('.cleanup') { all_fn_root_names << k @@ -319,10 +234,6 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a all_fn_root_names << k continue } - if (pref_.autofree || include_panic_deps) && k.ends_with('.free') { - all_fn_root_names << k - continue - } if k.ends_with('.lock') || k.ends_with('.unlock') || k.ends_with('.rlock') || k.ends_with('.runlock') { all_fn_root_names << k @@ -347,12 +258,6 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a continue } } - if mfn.receiver.typ != ast.void_type && mfn.generic_names.len > 0 { - // generic methods may be used in cgen after specialisation :-| - // TODO: move generic method specialisation from cgen to before markused - all_fn_root_names << k - continue - } if pref_.prealloc && k.starts_with('prealloc_') { all_fn_root_names << k continue @@ -376,44 +281,6 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a } } - // handle interface implementation methods: - for isym in table.type_symbols { - if isym.kind != .interface { - continue - } - if isym.info !is ast.Interface { - // Do not remove this check, isym.info could be &IError. - continue - } - interface_info := isym.info as ast.Interface - if interface_info.methods.len == 0 { - continue - } - for itype in interface_info.types { - ptype := itype.set_nr_muls(1) - ntype := itype.set_nr_muls(0) - interface_types := [ptype, ntype] - for method in interface_info.methods { - for typ in interface_types { - interface_implementation_method_name := int(typ.clear_flags()).str() + '.' + - method.name - if trace_skip_unused_interface_methods { - eprintln('>> isym.name: ${isym.name} | interface_implementation_method_name: ${interface_implementation_method_name}') - } - all_fn_root_names << interface_implementation_method_name - } - } - for embed_method in table.get_embed_methods(table.sym(itype)) { - interface_implementation_method_name := - int(embed_method.params[0].typ.clear_flags()).str() + '.' + embed_method.name - if trace_skip_unused_interface_methods { - eprintln('>> isym.name: ${isym.name} | interface_implementation_method_name: ${interface_implementation_method_name} (embeded)') - } - all_fn_root_names << interface_implementation_method_name - } - } - } - handle_vweb(mut table, mut all_fn_root_names, 'veb.Result', 'veb.filter', 'veb.Context') 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') @@ -448,24 +315,20 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a all_fns: all_fns all_consts: all_consts all_globals: all_globals - all_fields: all_fields all_decltypes: all_decltypes all_structs: all_structs pref: pref_ + trace_enabled: 'trace_skip_unused_walker' in pref_.compile_defines ) walker.mark_markused_consts() // tagged with `@[markused]` walker.mark_markused_globals() // tagged with `@[markused]` walker.mark_markused_syms() // tagged with `@[markused]` walker.mark_markused_fns() // tagged with `@[markused]`, `@[export]` and veb actions walker.mark_markused_decltypes() // tagged with `@[markused]` - walker.mark_struct_field_default_expr() + walker.mark_generic_types() - for k, _ in table.used_features.comptime_calls { - walker.fn_by_name(k) - } - - for k, _ in table.used_features.comptime_syms { - walker.mark_by_sym(table.sym(k)) + if pref_.use_cache { + walker.mark_by_sym_name('IError') } walker.mark_root_fns(all_fn_root_names) @@ -473,6 +336,30 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a walker.mark_by_sym_name('vweb.RedirectParams') walker.mark_by_sym_name('vweb.RequestParams') + for kcon, con in all_consts { + if pref_.is_shared && con.is_pub { + walker.mark_const_as_used(kcon) + continue + } + if pref_.translated && con.attrs.any(it.name == 'export') { + walker.mark_const_as_used(kcon) + continue + } + } + + if trace_skip_unused_fn_names { + for key, _ in walker.used_fns { + println('> used fn key: ${key}') + } + } + + walker.finalize(include_panic_deps) + + table.used_features.used_none = walker.used_none + if walker.used_none == 0 { + walker.used_fns.delete('${int(ast.none_type)}.str') + } + if table.used_features.used_maps > 0 { for k, mut mfn in all_fns { mut method_receiver_typename := '' @@ -508,30 +395,6 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a } } - for kcon, con in all_consts { - if pref_.is_shared && con.is_pub { - walker.mark_const_as_used(kcon) - continue - } - if pref_.translated && con.attrs.any(it.name == 'export') { - walker.mark_const_as_used(kcon) - continue - } - } - - if trace_skip_unused_fn_names { - for key, _ in walker.used_fns { - println('> used fn key: ${key}') - } - } - - table.used_features.used_none = walker.used_none - if walker.used_none == 0 { - walker.used_fns.delete('${int(ast.none_type)}.str') - } - - walker.finalize(include_panic_deps) - table.used_features.used_fns = walker.used_fns.move() table.used_features.used_consts = walker.used_consts.move() table.used_features.used_globals = walker.used_globals.move() @@ -556,7 +419,7 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a } } -fn all_global_decl(ast_files []&ast.File) (map[string]ast.FnDecl, map[string]ast.ConstField, map[string]ast.GlobalField, map[string]ast.StructField, map[string]ast.Type, map[string]ast.StructDecl) { +fn all_global_decl(ast_files []&ast.File) (map[string]ast.FnDecl, map[string]ast.ConstField, map[string]ast.GlobalField, map[string]ast.TypeDecl, map[string]ast.StructDecl) { util.timing_start(@METHOD) defer { util.timing_measure(@METHOD) @@ -564,8 +427,7 @@ fn all_global_decl(ast_files []&ast.File) (map[string]ast.FnDecl, map[string]ast mut all_fns := map[string]ast.FnDecl{} mut all_consts := map[string]ast.ConstField{} mut all_globals := map[string]ast.GlobalField{} - mut all_fields := map[string]ast.StructField{} - mut all_decltypes := map[string]ast.Type{} + mut all_decltypes := map[string]ast.TypeDecl{} mut all_structs := map[string]ast.StructDecl{} for i in 0 .. ast_files.len { for node in ast_files[i].stmts { @@ -589,20 +451,18 @@ fn all_global_decl(ast_files []&ast.File) (map[string]ast.FnDecl, map[string]ast } } ast.StructDecl { - for sfield in node.fields { - sfkey := sfield.sfkey() - all_fields[sfkey] = sfield - } all_structs[node.name] = node } ast.TypeDecl { - all_decltypes[node.name] = node.typ + if node.is_markused { + all_decltypes[node.name] = node + } } else {} } } } - return all_fns, all_consts, all_globals, all_fields, all_decltypes, all_structs + return all_fns, all_consts, all_globals, all_decltypes, all_structs } fn mark_all_methods_used(mut table ast.Table, mut all_fn_root_names []string, typ ast.Type) { diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index ac6dbac153..aebc9fb789 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -9,32 +9,33 @@ import v.pref pub struct Walker { pub mut: - table &ast.Table = unsafe { nil } - features &ast.UsedFeatures = unsafe { nil } - used_fns map[string]bool // used_fns['println'] == true - used_consts map[string]bool // used_consts['os.args'] == true - used_globals map[string]bool - used_fields map[string]bool - used_syms map[int]bool - used_none int // _option_none - used_option int // _option_ok - used_result int // _result_ok - used_panic int // option/result propagation - used_interp int // str interpolation - n_asserts int - pref &pref.Preferences = unsafe { nil } + table &ast.Table = unsafe { nil } + features &ast.UsedFeatures = unsafe { nil } + used_fns map[string]bool // used_fns['println'] == true + trace_enabled bool + used_consts map[string]bool // used_consts['os.args'] == true + used_globals map[string]bool + used_fields map[string]bool + used_syms map[int]bool + used_none int // _option_none + used_option int // _option_ok + used_result int // _result_ok + used_panic int // option/result propagation + pref &pref.Preferences = unsafe { nil } mut: files []&ast.File all_fns map[string]ast.FnDecl all_consts map[string]ast.ConstField all_globals map[string]ast.GlobalField all_fields map[string]ast.StructField - all_decltypes map[string]ast.Type + all_decltypes map[string]ast.TypeDecl all_structs map[string]ast.StructDecl + level int is_builtin_mod 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 @@ -46,6 +47,20 @@ mut: 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 bool // has .str() call + uses_free bool // has .free() call + uses_spawn bool + uses_dump bool + uses_memdup bool // sumtype cast and &Struct{} + uses_arr_void bool // auto arr methods } pub fn Walker.new(params Walker) &Walker { @@ -92,6 +107,9 @@ pub fn (mut w Walker) mark_const_as_used(ckey string) { return } w.used_consts[ckey] = true + if ckey == 'c' { + println(ckey) + } cfield := w.all_consts[ckey] or { return } w.expr(cfield.expr) w.mark_by_type(cfield.typ) @@ -118,7 +136,6 @@ pub fn (mut w Walker) mark_struct_field_default_expr_as_used(sfkey string) { } w.used_fields[sfkey] = true sfield := w.all_fields[sfkey] or { return } - w.expr(sfield.default_expr) } @@ -149,6 +166,9 @@ pub fn (mut w Walker) mark_markused_fns() { } pub fn (mut w Walker) mark_root_fns(all_fn_root_names []string) { + if w.trace_enabled { + eprintln('>>>>>>> ROOT ${all_fn_root_names}') + } for fn_name in all_fn_root_names { if fn_name !in w.used_fns { $if trace_skip_unused_roots ? { @@ -192,15 +212,9 @@ pub fn (mut w Walker) mark_markused_syms() { } pub fn (mut w Walker) mark_markused_decltypes() { - for _, typ in w.all_decltypes { - w.mark_by_type(typ) - } -} - -pub fn (mut w Walker) mark_struct_field_default_expr() { - for sfkey, mut structfield in w.all_fields { - if structfield.has_default_expr { - w.mark_struct_field_default_expr_as_used(sfkey) + for _, decl in w.all_decltypes { + if decl.is_markused { + w.mark_by_type(decl.typ) } } } @@ -209,14 +223,16 @@ pub fn (mut w Walker) stmt(node_ ast.Stmt) { mut node := unsafe { node_ } match mut node { ast.EmptyStmt {} - ast.DebuggerStmt {} + ast.DebuggerStmt { + w.uses_debugger = true + } ast.AsmStmt { w.asm_io(node.output) w.asm_io(node.input) } ast.AssertStmt { if node.is_used { - w.n_asserts++ + w.uses_asserts = true w.expr(node.expr) if node.extra !is ast.EmptyExpr { w.expr(node.extra) @@ -314,12 +330,16 @@ pub fn (mut w Walker) stmt(node_ ast.Stmt) { w.expr(line.where_expr) w.exprs(line.update_exprs) } + w.uses_orm = true } ast.StructDecl { for typ in node.implements_types { w.mark_by_type(typ.typ) } w.struct_fields(node.fields) + if !w.uses_mem_align { + w.uses_mem_align = node.attrs.contains('aligned') + } } ast.DeferStmt { w.stmts(node.stmts) @@ -381,7 +401,12 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.fn_decl(mut node.decl) } ast.ArrayInit { - w.mark_by_type(node.elem_type) + sym := w.table.sym(node.elem_type) + w.mark_by_sym(sym) + if sym.info is ast.Thread { + w.mark_by_type(w.table.find_or_register_array(sym.info.return_type)) + } + w.mark_by_type(node.typ) w.expr(node.len_expr) w.expr(node.cap_expr) w.expr(node.init_expr) @@ -398,18 +423,35 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.call_expr(mut node) if node.name == 'json.decode' { w.mark_by_type((node.args[0].expr as ast.TypeNode).typ) + } else if node.name == 'json.encode' && node.args[0].typ != 0 { + sym := w.table.final_sym(node.args[0].typ) + if sym.info is ast.Map { + w.mark_by_type(w.table.find_or_register_array(sym.info.key_type)) + } + } else if !w.uses_str && node.is_method && node.name == 'str' { + w.uses_str = true + } else if !w.uses_free && node.is_method && node.name == 'free' { + w.uses_free = true } if !w.is_builtin_mod && !w.uses_external_type { if node.is_method { w.uses_external_type = node.mod == 'builtin' - } else if node.name.contains('.') { + } else if node.name.contains('.') && node.name.all_before_last('.').len > 1 { w.uses_external_type = true } } + if node.is_method && node.left_type != 0 + && w.table.final_sym(node.left_type).kind in [.array_fixed, .array] { + w.mark_by_type(node.return_type) + } } ast.CastExpr { w.expr(node.expr) w.expr(node.arg) + if !w.uses_memdup { + fsym := w.table.final_sym(node.typ) + w.uses_memdup = fsym.kind == .sum_type + } w.mark_by_type(node.typ) if node.typ.has_flag(.option) { w.used_option++ @@ -441,8 +483,7 @@ fn (mut w Walker) expr(node_ ast.Expr) { } ast.DumpExpr { w.expr(node.expr) - w.fn_by_name('eprint') - w.fn_by_name('eprintln') + w.uses_dump = true w.mark_by_type(node.expr_type) } ast.SpawnExpr { @@ -450,7 +491,8 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.fn_by_name('free') } w.expr(node.call_expr) - w.fn_by_name('tos3') + w.uses_spawn = true + if w.pref.os == .windows { w.fn_by_name('panic_lasterr') w.fn_by_name('winapi_lasterr_str') @@ -463,11 +505,16 @@ fn (mut w Walker) expr(node_ ast.Expr) { if node.is_expr { w.fn_by_name('free') } + w.mark_by_type(node.call_expr.return_type) w.expr(node.call_expr) } ast.IndexExpr { w.expr(node.left) w.expr(node.index) + if node.or_expr.kind == .block { + w.uses_guard = true + } + w.mark_by_type(node.typ) w.or_block(node.or_expr) if node.left_type == 0 { return @@ -505,8 +552,8 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.expr(node.right) w.or_block(node.or_block) if node.left_type != 0 { - w.mark_by_type(node.left_type) sym := w.table.sym(node.left_type) + w.mark_by_sym(sym) if sym.kind == .struct { if opmethod := sym.find_method(node.op.str()) { unsafe { @@ -527,14 +574,21 @@ fn (mut w Walker) expr(node_ ast.Expr) { if right_sym.kind == .map { w.features.used_maps++ } + if !w.uses_arr_void && right_sym.kind in [.array, .array_fixed] { + w.uses_arr_void = true + } } else if node.op in [.key_is, .not_is] { w.mark_by_sym(right_sym) } } + if !w.uses_eq && node.op in [.eq, .ne] { + w.uses_eq = true + } } ast.IfGuardExpr { w.expr(node.expr) w.mark_by_type(node.expr_type) + w.uses_guard = true } ast.IfExpr { w.expr(node.left) @@ -557,8 +611,9 @@ fn (mut w Walker) expr(node_ ast.Expr) { .global { w.mark_global_as_used(node.name) } + .blank_ident {} else { - // `.unresolved`, `.blank_ident`, `.variable`, `.function` + // `.unresolved`, `.variable` // println('>>> else, ast.Ident ${node.name} kind: $node.kind ') if node.name in w.all_consts { w.mark_const_as_used(node.name) @@ -571,6 +626,9 @@ fn (mut w Walker) expr(node_ ast.Expr) { } w.fn_by_name(node.name) } + if !w.uses_atomic && node.info is ast.IdentVar { + w.uses_atomic = node.info.typ.has_flag(.atomic_f) + } } } w.or_block(node.or_expr) @@ -587,6 +645,9 @@ fn (mut w Walker) expr(node_ ast.Expr) { if node.has_update_expr { w.expr(node.update_expr) } + if node.vals.len > 0 && node.has_update_expr { + w.uses_map_update = true + } mapinfo := w.table.final_sym(node.typ).map_info() ksym := w.table.sym(mapinfo.key_type) vsym := w.table.sym(mapinfo.value_type) @@ -596,6 +657,7 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.mark_by_sym(ksym) w.mark_by_sym(vsym) w.features.used_maps++ + w.mark_by_type(node.typ) } ast.MatchExpr { w.expr(node.cond) @@ -617,6 +679,9 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.expr(node.expr) } ast.PrefixExpr { + if !w.uses_memdup && node.op == .amp { + w.uses_memdup = true + } w.expr(node.right) } ast.PostfixExpr { @@ -635,7 +700,7 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.mark_by_type(node.typ) } ast.StringInterLiteral { - w.used_interp++ + w.uses_interp = true w.exprs(node.exprs) } ast.SelectorExpr { @@ -657,6 +722,7 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.expr(node.limit_expr) w.expr(node.where_expr) w.mark_by_type(node.typ) + w.uses_orm = true } ast.StructInit { if node.typ == 0 { @@ -730,6 +796,13 @@ pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) { if node == unsafe { nil } { return } + if w.level == 0 { + last_is_builtin_mod := w.is_builtin_mod + w.is_builtin_mod = node.mod in ['builtin', 'os', 'strconv', 'builtin.closure'] + defer { + w.is_builtin_mod = last_is_builtin_mod + } + } if node.language == .c { w.mark_fn_as_used(node.fkey()) w.mark_fn_ret_and_params(node.return_type, node.params) @@ -742,15 +815,22 @@ pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) { if node.no_body { return } + if w.trace_enabled { + 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) } w.mark_fn_ret_and_params(node.return_type, node.params) w.mark_fn_as_used(fkey) - last_is_builtin_mod := w.is_builtin_mod - w.is_builtin_mod = node.mod in ['builtin', 'os', 'strconv', 'builtin.closure'] w.stmts(node.stmts) - w.is_builtin_mod = last_is_builtin_mod w.defer_stmts(node.defer_stmts) } @@ -768,9 +848,7 @@ pub fn (mut w Walker) call_expr(mut node ast.CallExpr) { if node.name in ['C.wyhash', 'C.wyhash64'] { w.features.used_maps++ } - if node.return_type != 0 { - w.mark_by_type(node.return_type) - } + w.mark_by_type(node.return_type) return } if node.is_method && node.left_type != 0 { @@ -804,6 +882,17 @@ pub fn (mut w Walker) call_expr(mut node ast.CallExpr) { fn_embed := '${int(embed_types.last())}.${node.name}' w.fn_by_name(fn_embed) } + } else { + match left_sym.info { + ast.Array, ast.ArrayFixed { + if !w.uses_arr_void && node.name in ['contains', 'index'] { + if w.table.final_sym(left_sym.info.elem_type).kind == .function { + w.uses_arr_void = true + } + } + } + else {} + } } } w.expr(node.left) @@ -844,9 +933,22 @@ pub fn (mut w Walker) call_expr(mut node ast.CallExpr) { receiver_typ = node.receiver_concrete_type w.mark_fn_as_used(fn_name) } + w.mark_by_type(node.return_type) 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 { + if w.trace_enabled { + w.level++ + defer { + w.level-- + } + receiver_name := if node.receiver_type != 0 { + w.table.type_to_str(node.receiver_type) + '.' + } else { + '' + } + eprintln('>>>${' '.repeat(w.level)}${receiver_name}${node.name} [call]') + } w.mark_fn_ret_and_params(stmt.return_type, stmt.params) w.stmts(stmt.stmts) } @@ -863,6 +965,18 @@ pub fn (mut w Walker) fn_by_name(fn_name string) { return } stmt := w.all_fns[fn_name] or { return } + 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()) + '.' + } else { + '' + } + eprintln('>>>${' '.repeat(w.level)}${receiver_name}${fn_name.all_after_last('.')} [by_name]') + } w.mark_fn_as_used(fn_name) w.mark_fn_ret_and_params(stmt.return_type, stmt.params) w.stmts(stmt.stmts) @@ -873,6 +987,9 @@ pub fn (mut w Walker) struct_fields(sfields []ast.StructField) { if sf.has_default_expr { w.expr(sf.default_expr) } + if !w.uses_atomic && sf.typ.has_flag(.atomic_f) { + w.uses_atomic = true + } } } @@ -895,27 +1012,6 @@ pub fn (mut w Walker) or_block(node ast.OrExpr) { } } -fn (mut w Walker) mark_panic_deps() { - ref_array_idx_str := int(ast.array_type.ref()).str() - string_idx_str := ast.string_type_idx.str() - array_idx_str := ast.array_type_idx.str() - charptr_idx_str := ast.charptr_type_idx.str() - - w.fn_by_name('__new_array_with_default') - w.fn_by_name('__new_array_with_default_noscan') - w.fn_by_name('str_intp') - w.fn_by_name(ref_array_idx_str + '.push') - w.fn_by_name(ref_array_idx_str + '.push_noscan') - w.fn_by_name(string_idx_str + '.substr') - w.fn_by_name(array_idx_str + '.slice') - w.fn_by_name(array_idx_str + '.get') - w.fn_by_name('v_fixed_index') - w.fn_by_name(charptr_idx_str + '.vstring_literal') - - w.mark_by_sym_name('StrIntpData') - w.mark_by_sym_name('StrIntpMem') -} - pub fn (mut w Walker) mark_fn_ret_and_params(return_type ast.Type, params []ast.Param) { if return_type != 0 { if return_type.has_flag(.option) { @@ -930,6 +1026,7 @@ pub fn (mut w Walker) mark_fn_ret_and_params(return_type ast.Type, params []ast. } } +@[inline] pub fn (mut w Walker) mark_by_sym_name(name string) { if sym := w.table.find_sym(name) { w.mark_by_sym(sym) @@ -956,7 +1053,7 @@ pub fn (mut w Walker) mark_by_sym(isym ast.TypeSymbol) { w.expr(ifield.default_expr) } if ifield.typ != 0 { - fsym := w.table.sym(ifield.typ) + fsym := w.table.sym(ifield.typ.idx()) if ifield.typ.has_flag(.option) { w.used_option++ if !ifield.has_default_expr { @@ -964,9 +1061,6 @@ pub fn (mut w Walker) mark_by_sym(isym ast.TypeSymbol) { } } match fsym.info { - ast.Array, ast.ArrayFixed { - w.mark_by_sym(fsym) - } ast.Map { w.features.used_maps++ w.mark_by_sym(fsym) @@ -976,17 +1070,26 @@ pub fn (mut w Walker) mark_by_sym(isym ast.TypeSymbol) { } } } + if !w.features.auto_str_ptr && ifield.typ.is_ptr() + && isym.idx in w.features.print_types { + w.features.auto_str_ptr = true + } } for embed in isym.info.embeds { w.mark_by_type(embed) } if decl := w.all_structs[isym.name] { + w.struct_fields(decl.fields) + if !w.uses_mem_align { + w.uses_mem_align = decl.attrs.contains('aligned') + } for iface_typ in decl.implements_types { w.mark_by_type(iface_typ.typ) } } } ast.ArrayFixed, ast.Array { + w.uses_array = true w.mark_by_type(isym.info.elem_type) } ast.SumType { @@ -1030,6 +1133,13 @@ pub fn (mut w Walker) mark_by_sym(isym ast.TypeSymbol) { } ast.Enum { w.mark_by_type(isym.info.typ) + if enum_ := w.table.enum_decls[isym.name] { + for field in enum_.fields { + if field.has_expr { + w.expr(field.expr) + } + } + } } ast.Interface { for typ in isym.info.types { @@ -1037,6 +1147,17 @@ pub fn (mut w Walker) mark_by_sym(isym ast.TypeSymbol) { w.features.used_maps++ } w.mark_by_type(typ) + for method in isym.info.methods { + for ityp in [typ.set_nr_muls(1), typ.set_nr_muls(0)] { + mname := int(ityp.clear_flags()).str() + '.' + method.name + w.fn_by_name(mname) + } + } + for embed_method in w.table.get_embed_methods(w.table.sym(typ)) { + mname := int(embed_method.params[0].typ.clear_flags()).str() + '.' + + embed_method.name + w.fn_by_name(mname) + } } for embed in isym.info.embeds { w.mark_by_type(embed) @@ -1053,6 +1174,9 @@ pub fn (mut w Walker) mark_by_sym(isym ast.TypeSymbol) { w.mark_fn_ret_and_params(method.return_type, method.params) } } + ast.Thread { + w.mark_by_type(isym.info.return_type) + } else {} } } @@ -1087,6 +1211,17 @@ fn (mut w Walker) remove_unused_dump_type() { } fn (mut w Walker) mark_resource_dependencies() { + if w.trace_enabled { + eprintln('>>>>>>>>>> DEPS USAGE') + } + if w.uses_eq { + w.fn_by_name('fast_string_eq') + } + if w.features.auto_str_ptr { + w.fn_by_name('isnil') + w.fn_by_name('tos4') + w.fn_by_name('str_intp') + } if w.uses_channel { w.fn_by_name('sync.new_channel_st') w.fn_by_name('sync.channel_select') @@ -1094,6 +1229,28 @@ fn (mut w Walker) mark_resource_dependencies() { if w.uses_lock { w.mark_by_sym_name('sync.RwMutex') } + if w.uses_array { + if w.pref.gc_mode in [.boehm_full_opt, .boehm_incr_opt] { + w.fn_by_name('__new_array_noscan') + w.fn_by_name('new_array_from_c_array_noscan') + w.fn_by_name('__new_array_with_multi_default_noscan') + w.fn_by_name('__new_array_with_array_default_noscan') + w.fn_by_name('__new_array_with_default_noscan') + } + w.fn_by_name('__new_array') + w.fn_by_name('new_array_from_c_array') + w.fn_by_name('__new_array_with_multi_default') + w.fn_by_name('__new_array_with_array_default') + w.fn_by_name('__new_array_with_default') + w.fn_by_name(int(ast.array_type.ref()).str() + '.set') + } + if w.uses_orm { + w.fn_by_name('__new_array_with_default_noscan') + w.fn_by_name('new_array_from_c_array') + w.fn_by_name('__new_array') + w.fn_by_name('${ast.array_type_idx}.get') + w.fn_by_name(int(ast.array_type.ref()).str() + '.push') + } if w.uses_ct_fields { w.mark_by_sym_name('FieldData') } @@ -1112,14 +1269,91 @@ fn (mut w Walker) mark_resource_dependencies() { if w.uses_ct_attribute { w.mark_by_sym_name('VAttribute') } + if w.uses_map_update { + w.fn_by_name('new_map_update_init') + } + if w.uses_mem_align { + w.fn_by_name('memdup_align') + } + if w.uses_guard { + w.fn_by_name('error') + } + if w.uses_dump { + w.fn_by_name('eprint') + w.fn_by_name('eprintln') + } + if w.uses_spawn { + w.fn_by_name('malloc') + w.fn_by_name('tos3') + } + if w.uses_memdup { + w.fn_by_name('memdup') + } + if w.uses_debugger { + w.mark_by_type(w.table.find_or_register_map(ast.string_type, ast.string_type)) + } + if w.uses_arr_void { + w.mark_by_type(w.table.find_or_register_array(ast.voidptr_type)) + } + if w.trace_enabled { + types := w.table.used_features.print_types.keys().map(w.table.type_to_str(it)) + eprintln('>>>>>>>>>> PRINT TYPES ${types}') + } + for typ, _ in w.table.used_features.print_types { + w.mark_by_type(typ) + } + if w.trace_enabled { + eprintln('>>>>>>>>>> ALL_FNS LOOP') + } + mut has_ptr_print := false + has_str_call := w.uses_interp || w.uses_asserts || w.uses_str || w.features.print_types.len > 0 + 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 { + w.fn_by_name(k) + if !has_ptr_print && func.receiver.typ.is_ptr() { + w.fn_by_name('ptr_str') + has_ptr_print = true + } + } + continue + } + if w.pref.autofree || (w.uses_free && k.ends_with('.free') + && func.receiver.typ.idx() in w.used_syms) { + w.fn_by_name(k) + continue + } + if w.uses_atomic && k.starts_with('_Atomic') { + w.fn_by_name(k) + continue + } + if func.name in ['+', '-', '*', '%', '/', '<', '=='] { + if func.receiver.typ.idx() in w.used_syms { + w.fn_by_name(k) + } + continue + } + if !func.is_static_type_method && func.receiver.typ != ast.void_type + && func.generic_names.len > 0 { + 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 + } + } + } } pub fn (mut w Walker) finalize(include_panic_deps bool) { - if include_panic_deps || w.used_interp > 0 || w.uses_external_type { - w.mark_panic_deps() - } w.mark_resource_dependencies() - + if w.trace_enabled { + eprintln('>>>>>>>>>> FINALIZE') + } + if w.uses_asserts { + w.fn_by_name('__print_assert_failure') + w.fn_by_name('isnil') + 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') @@ -1140,13 +1374,50 @@ pub fn (mut w Walker) finalize(include_panic_deps bool) { if (w.used_option + w.used_result + w.used_none) > 0 { w.mark_const_as_used('none__') } - w.mark_by_sym_name('array') + if include_panic_deps || w.uses_external_type || w.uses_asserts || w.uses_debugger + || w.uses_interp { + if w.trace_enabled { + println('>>>>> PANIC DEPS ${include_panic_deps} | external_type=${w.uses_external_type} | asserts=${w.uses_asserts} | dbg=${w.uses_debugger}') + } + ref_array_idx_str := int(ast.array_type.ref()).str() + string_idx_str := ast.string_type_idx.str() - if w.table.used_features.asserts { - w.mark_by_sym_name('VAssertMetaInfo') + if w.pref.gc_mode in [.boehm_full_opt, .boehm_incr_opt] { + w.fn_by_name('__new_array_with_default_noscan') + w.fn_by_name(ref_array_idx_str + '.push_noscan') + } + w.fn_by_name('str_intp') + w.fn_by_name('__new_array_with_default') + w.fn_by_name(ref_array_idx_str + '.push') + w.fn_by_name(string_idx_str + '.substr') + w.fn_by_name('v_fixed_index') + w.mark_by_sym_name('StrIntpData') + w.mark_by_sym_name('StrIntpMem') } // 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)) + eprintln('>>>>>>>>>> USED SYMS ${syms}') + } +} + +pub fn (mut w Walker) mark_generic_types() { + if w.trace_enabled { + eprintln('>>>>>>>>>> COMPTIME SYMS+CALLS') + } + for k, _ in w.table.used_features.comptime_calls { + w.fn_by_name(k) + } + + for k, _ in w.table.used_features.comptime_syms { + sym := w.table.sym(k) + w.mark_by_sym(sym) + for method in sym.get_methods() { + w.fn_by_name('${k}.${method.name}') + } + } } diff --git a/vlib/v/type_resolver/comptime_resolver.v b/vlib/v/type_resolver/comptime_resolver.v index bfcdfec256..79f296e19d 100644 --- a/vlib/v/type_resolver/comptime_resolver.v +++ b/vlib/v/type_resolver/comptime_resolver.v @@ -22,6 +22,8 @@ pub fn (t &ResolverInfo) has_comptime_expr(node ast.Expr) bool { && node.init_fields.any(it.expr is ast.AnonFn && it.expr.decl.generic_names.len > 0)) || (node is ast.PostfixExpr && t.has_comptime_expr(node.expr)) || (node is ast.SelectorExpr && t.has_comptime_expr(node.expr)) + || (node is ast.ArrayInit && node.elem_type.has_flag(.generic)) + || (node is ast.MapInit && node.typ.has_flag(.generic)) || (node is ast.InfixExpr && (t.has_comptime_expr(node.left) || t.has_comptime_expr(node.right))) }