diff --git a/cmd/v/v.v b/cmd/v/v.v index 2df96d6498..003bde83cf 100644 --- a/cmd/v/v.v +++ b/cmd/v/v.v @@ -12,6 +12,7 @@ import v.util.version import v.builder import v.builder.cbuilder +@[markused] const external_tools = [ 'ast', 'bin2v', diff --git a/vlib/gg/gg.v b/vlib/gg/gg.v index 5cae674f4e..c13965ba26 100644 --- a/vlib/gg/gg.v +++ b/vlib/gg/gg.v @@ -32,6 +32,7 @@ pub: thickness f32 = 1 } +@[markused] pub struct Size { pub mut: width int diff --git a/vlib/gg/image.c.v b/vlib/gg/image.c.v index 3b36cd6be3..222dc532e7 100644 --- a/vlib/gg/image.c.v +++ b/vlib/gg/image.c.v @@ -9,7 +9,7 @@ import sokol.sgl // Image holds the fields and data needed to // represent a bitmap/pixel based image in memory. -@[heap] +@[heap; markused] pub struct Image { pub mut: id int diff --git a/vlib/gx/color.v b/vlib/gx/color.v index 04fc39fea2..c09c843952 100644 --- a/vlib/gx/color.v +++ b/vlib/gx/color.v @@ -112,6 +112,7 @@ pub const light_red = Color{ } // Color represents a 32 bit color value in sRGB format +@[markused] pub struct Color { pub mut: r u8 diff --git a/vlib/gx/text.v b/vlib/gx/text.v index 95d745fbbf..29a1a7a238 100644 --- a/vlib/gx/text.v +++ b/vlib/gx/text.v @@ -4,7 +4,7 @@ module gx pub const align_left = HorizontalAlign.left pub const align_right = HorizontalAlign.right -@[params] +@[markused; params] pub struct TextCfg { pub: color Color = black diff --git a/vlib/os/os.c.v b/vlib/os/os.c.v index 5b7b0834b3..03d7846b0b 100644 --- a/vlib/os/os.c.v +++ b/vlib/os/os.c.v @@ -9,6 +9,7 @@ $if freebsd || openbsd { #include } +@[markused] pub const args = arguments() fn C.readdir(voidptr) &C.dirent diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index eb1e7989a8..2438743ce9 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -453,6 +453,7 @@ pub: pub mut: language Language fields []StructField + idx int } pub struct Embed { @@ -1484,6 +1485,7 @@ pub: comments []Comment pub mut: parent_type Type + is_markused bool } // SumTypeDecl is the ast node for `type MySumType = string | int` @@ -1497,7 +1499,8 @@ pub: generic_types []Type attrs []Attr // attributes of type declaration pub mut: - variants []TypeNode + variants []TypeNode + is_markused bool } pub struct FnTypeDecl { @@ -1510,6 +1513,7 @@ pub: comments []Comment generic_types []Type attrs []Attr // attributes of type declaration + is_markused bool } // TODO: handle this differently diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index ff2cc4a1f8..7d275ef5a5 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -34,6 +34,7 @@ pub mut: used_fns map[string]bool // filled in by markused used_consts map[string]bool // filled in by markused used_globals map[string]bool // filled in by markused + used_syms map[int]bool // filled in by markused 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 @@ -42,6 +43,7 @@ pub mut: // 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 } diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index e117ee3862..4bd3448cdf 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -183,6 +183,7 @@ pub mut: is_anon bool is_generic bool is_shared bool + is_markused bool has_option bool // contains any option field generic_types []Type concrete_types []Type diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 6756be23f2..136e291487 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3137,6 +3137,10 @@ pub fn (mut c Checker) expr(mut node ast.Expr) ast.Type { c.inside_if_guard = true node.expr_type = c.expr(mut node.expr) c.inside_if_guard = old_inside_if_guard + if c.pref.skip_unused && node.expr_type.has_flag(.generic) { + unwrapped_type := c.unwrap_generic(node.expr_type) + c.table.used_features.comptime_syms[unwrapped_type] = true + } if !node.expr_type.has_flag(.option) && !node.expr_type.has_flag(.result) { mut no_opt_or_res := true match mut node.expr { @@ -3435,6 +3439,9 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type { // allow conversion from none to every option type } else if to_sym.kind == .sum_type { to_sym_info := to_sym.info as ast.SumType + if c.pref.skip_unused && to_sym_info.concrete_types.len > 0 { + c.table.used_features.comptime_syms[to_type] = true + } if to_sym_info.generic_types.len > 0 && to_sym_info.concrete_types.len == 0 { c.error('generic sumtype `${to_sym.name}` must specify type parameter, e.g. ${to_sym.name}[int]', node.pos) @@ -5227,6 +5234,9 @@ fn (mut c Checker) chan_init(mut node ast.ChanInit) ast.Type { if node.has_cap { c.check_array_init_para_type('cap', mut node.cap_expr, node.pos) } + if c.pref.skip_unused && node.typ.has_flag(.generic) { + c.table.used_features.comptime_syms[c.unwrap_generic(node.typ)] = true + } return node.typ } else { c.error('`chan` of unknown type', node.pos) diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 47178abff5..45a3ef8f4f 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -339,6 +339,14 @@ 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 + } + if node.return_type.has_flag(.generic) { + c.table.used_features.comptime_syms[c.unwrap_generic(node.return_type)] = true + } + } if param.name == node.mod && param.name != 'main' { c.error('duplicate of a module name `${param.name}`', param.pos) } @@ -992,6 +1000,9 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. } typ := expr as ast.TypeNode node.return_type = if is_json_decode { typ.typ.set_flag(.result) } else { typ.typ } + if typ.typ.has_flag(.generic) { + c.table.used_features.comptime_syms[c.unwrap_generic(typ.typ)] = true + } return node.return_type } else if fn_name == '__addr' { if !c.inside_unsafe { @@ -1859,6 +1870,11 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. } } } + if c.pref.skip_unused && node.concrete_types.len > 0 { + for concrete_type in node.concrete_types { + c.table.used_features.comptime_syms[c.unwrap_generic(concrete_type)] = true + } + } } // resolve return generics struct to concrete type @@ -1877,6 +1893,9 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. if typ := c.table.convert_generic_type(func.return_type, func.generic_names, concrete_types) { node.return_type = typ 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 + } return typ } } @@ -1889,6 +1908,12 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. ret_type := c.resolve_fn_return_type(func, node, concrete_types) c.register_trace_call(node, func) node.return_type = ret_type + if ret_type.has_flag(.generic) { + unwrapped_ret := c.unwrap_generic(ret_type) + if c.table.sym(unwrapped_ret).kind == .multi_return { + c.table.used_features.comptime_syms[unwrapped_ret] = true + } + } return ret_type } c.register_trace_call(node, func) diff --git a/vlib/v/checker/return.v b/vlib/v/checker/return.v index 69357b7524..f6215a7c5d 100644 --- a/vlib/v/checker/return.v +++ b/vlib/v/checker/return.v @@ -72,6 +72,7 @@ fn (mut c Checker) return_stmt(mut node ast.Return) { expected_types = expected_type_sym.info.types.clone() if c.table.cur_concrete_types.len > 0 { expected_types = expected_types.map(c.unwrap_generic(it)) + c.table.used_features.comptime_syms[c.table.find_or_register_multi_return(expected_types)] = true } } mut got_types := []ast.Type{} @@ -226,6 +227,9 @@ fn (mut c Checker) return_stmt(mut node ast.Return) { } } } else { + if c.pref.skip_unused && got_types[i].has_flag(.generic) { + c.table.used_features.comptime_syms[got_type] = true + } if exp_type_sym.kind == .interface { if c.type_implements(got_type, exp_type, node.pos) { if !got_type.is_any_kind_of_pointer() && got_type_sym.kind != .interface diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index 0c298b97be..172aa92e05 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -17,6 +17,7 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) { } node_name := if node.scoped_name != '' { node.scoped_name } else { node.name } mut struct_sym, struct_typ_idx := c.table.find_sym_and_type_idx(node_name) + node.idx = struct_typ_idx mut has_generic_types := false if mut struct_sym.info is ast.Struct { for mut symfield in struct_sym.info.fields { @@ -534,6 +535,9 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.generic_names.len > 0 { c.table.unwrap_generic_type_ex(node.typ, c.table.cur_fn.generic_names, c.table.cur_concrete_types, true) + if c.pref.skip_unused && node.typ.has_flag(.generic) { + c.table.used_features.comptime_syms[c.unwrap_generic(node.typ)] = true + } } if !is_field_zero_struct_init { c.ensure_type_exists(node.typ, node.pos) diff --git a/vlib/v/checker/used_features.v b/vlib/v/checker/used_features.v index 6ddfec557d..b33f5896c2 100644 --- a/vlib/v/checker/used_features.v +++ b/vlib/v/checker/used_features.v @@ -27,7 +27,11 @@ fn (mut c Checker) markused_dumpexpr(mut node ast.DumpExpr) { if c.is_builtin_mod { return } - if !c.table.sym(c.unwrap_generic(node.expr_type)).has_method('str') { + unwrapped_type := c.unwrap_generic(node.expr_type) + if node.expr_type.has_flag(.generic) { + c.table.used_features.comptime_syms[unwrapped_type] = true + } + if !c.table.sym(unwrapped_type).has_method('str') { c.table.used_features.auto_str = true if node.expr_type.is_ptr() { c.table.used_features.auto_str_ptr = true diff --git a/vlib/v/gen/c/auto_eq_methods.v b/vlib/v/gen/c/auto_eq_methods.v index 475cced8bd..0c1730abdd 100644 --- a/vlib/v/gen/c/auto_eq_methods.v +++ b/vlib/v/gen/c/auto_eq_methods.v @@ -623,9 +623,13 @@ fn (mut g Gen) gen_interface_equality_fn(left_type ast.Type) string { fn_builder.writeln('\t\tint idx = v_typeof_interface_idx_${idx_fn}(${left_arg});') if info is ast.Interface { for typ in info.types { + sym := g.table.sym(typ.set_nr_muls(0)) + if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms { + continue + } fn_builder.writeln('\t\tif (idx == ${typ.idx()}) {') fn_builder.write_string('\t\t\treturn ') - match g.table.type_kind(typ.set_nr_muls(0)) { + match sym.kind { .struct { eq_fn := g.gen_struct_equality_fn(typ) l_eqfn := g.read_field(left_type, '_${eq_fn}', 'a') diff --git a/vlib/v/gen/c/auto_str_methods.v b/vlib/v/gen/c/auto_str_methods.v index 3dfd83ceda..af8237e36b 100644 --- a/vlib/v/gen/c/auto_str_methods.v +++ b/vlib/v/gen/c/auto_str_methods.v @@ -409,6 +409,9 @@ fn (mut g Gen) gen_str_for_interface(info ast.Interface, styp string, typ_str st fn_builder.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${styp} x, int indent_count) { /* gen_str_for_interface */') for typ in info.types { sub_sym := g.table.sym(ast.mktyp(typ)) + if g.pref.skip_unused && sub_sym.idx !in g.table.used_features.used_syms { + continue + } mut func_name := g.get_str_fn(typ) sym_has_str_method, str_method_expects_ptr, _ := sub_sym.str_method_info() if should_use_indent_func(sub_sym.kind) && !sym_has_str_method { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 914d421dd9..e0a846fe30 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -1120,6 +1120,9 @@ pub fn (mut g Gen) write_typeof_functions() { g.writeln('// >> typeof() support for sum types / interfaces') for ityp, sym in g.table.type_symbols { if sym.kind == .sum_type { + if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms { + continue + } static_prefix := if g.pref.build_mode == .build_module { 'static ' } else { '' } sum_info := sym.info as ast.SumType if sum_info.is_generic { @@ -1192,6 +1195,10 @@ pub fn (mut g Gen) write_typeof_functions() { if sub_sym.info is ast.Struct && sub_sym.info.is_unresolved_generic() { continue } + if g.pref.skip_unused && sub_sym.kind == .struct + && sub_sym.idx !in g.table.used_features.used_syms { + continue + } g.writeln('\tif (sidx == _${sym.cname}_${sub_sym.cname}_index) return "${util.strip_main_name(sub_sym.name)}";') } g.writeln2('\treturn "unknown ${util.strip_main_name(sym.name)}";', '}') @@ -1205,6 +1212,10 @@ pub fn (mut g Gen) write_typeof_functions() { if sub_sym.info is ast.Struct && sub_sym.info.is_unresolved_generic() { continue } + if g.pref.skip_unused && sub_sym.kind == .struct + && sub_sym.idx !in g.table.used_features.used_syms { + continue + } g.writeln('\tif (sidx == _${sym.cname}_${sub_sym.cname}_index) return ${int(t.set_nr_muls(0))};') } g.writeln2('\treturn ${int(ityp)};', '}') @@ -1768,6 +1779,9 @@ static inline void __${sym.cname}_pushval(${sym.cname} ch, ${push_arg} val) { } for sym in g.table.type_symbols { if sym.kind == .alias && !sym.is_builtin && sym.name !in ['byte', 'i32'] { + if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms { + continue + } g.write_alias_typesymbol_declaration(sym) } } @@ -1864,7 +1878,11 @@ pub fn (mut g Gen) write_interface_typesymbol_declaration(sym ast.TypeSymbol) { if mk_typ != variant && mk_typ in info.types { continue } - vcname := g.table.sym(mk_typ).cname + vsym := g.table.sym(mk_typ) + if g.pref.skip_unused && vsym.idx !in g.table.used_features.used_syms { + continue + } + vcname := vsym.cname g.type_definitions.writeln('\t\t${vcname}* _${vcname};') } g.type_definitions.writeln('\t};') @@ -1883,6 +1901,11 @@ pub fn (mut g Gen) write_fn_typesymbol_declaration(sym ast.TypeSymbol) { is_fn_sig := func.name == '' not_anon := !info.is_anon mut has_generic_arg := false + + if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms { + return + } + for param in func.params { if param.typ.has_flag(.generic) { has_generic_arg = true @@ -1965,6 +1988,9 @@ pub fn (mut g Gen) write_multi_return_types() { if info.types.any(it.has_flag(.generic)) { continue } + if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms { + continue + } g.typedefs.writeln('typedef struct ${sym.cname} ${sym.cname};') g.type_definitions.writeln('struct ${sym.cname} {') for i, mr_typ in info.types { @@ -2582,6 +2608,10 @@ fn (mut g Gen) stmt(node ast.Stmt) { // Register an option if it's not registered yet g.register_option(method.return_type) } else 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 + } // Register a result if it's not registered yet g.register_result(method.return_type) } @@ -2610,6 +2640,9 @@ fn (mut g Gen) stmt(node ast.Stmt) { if node.language == .c { return } + if g.pref.skip_unused && node.idx !in g.table.used_features.used_syms { + return + } if node.is_union { g.typedefs.writeln('typedef union ${name} ${name};') } else { @@ -4705,6 +4738,9 @@ fn (mut g Gen) enum_decl(node ast.EnumDecl) { if g.is_cc_msvc { mut last_value := '0' enum_typ_name := g.table.get_type_name(node.typ) + if g.pref.skip_unused && node.typ.idx() !in g.table.used_features.used_syms { + return + } g.enum_typedefs.writeln('') g.enum_typedefs.writeln('typedef ${enum_typ_name} ${enum_name};') for i, field in node.fields { @@ -4725,6 +4761,9 @@ fn (mut g Gen) enum_decl(node ast.EnumDecl) { } return } + if g.pref.skip_unused && node.typ.idx() !in g.table.used_features.used_syms { + return + } g.enum_typedefs.writeln('') if node.typ != ast.int_type { g.enum_typedefs.writeln('#pragma pack(push, 1)') @@ -6395,6 +6434,9 @@ fn (mut g Gen) write_debug_calls_typeof_functions() { if sum_info.is_generic { continue } + if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms { + continue + } g.writeln('\tv_typeof_sumtype_${sym.cname}(0);') } if sym.kind == .interface { @@ -6677,6 +6719,9 @@ 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 } @@ -6700,7 +6745,8 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) { } } ast.SumType { - if sym.info.is_generic || struct_names[name] { + if sym.info.is_generic || struct_names[name] + || (g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms) { continue } struct_names[name] = true @@ -6788,6 +6834,10 @@ 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}];') } } @@ -7728,6 +7778,10 @@ fn (mut g Gen) register_iface_return_types() { 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) } } @@ -7760,7 +7814,12 @@ fn (mut g Gen) interface_table() string { for k, method_name in inter_methods { method := isym.find_method_with_generic_parent(method_name) or { continue } methodidx[method.name] = k - ret_styp := g.ret_styp(method.return_type) + ret_styp := if g.pref.skip_unused + && g.table.sym(method.return_type).idx !in g.table.used_features.used_syms { + 'void' + } else { + g.ret_styp(method.return_type) + } methods_struct_def.write_string('\t${ret_styp} (*_method_${c_fn_name(method.name)})(void* _') // the first param is the receiver, it's handled by `void*` above for i in 1 .. method.params.len { @@ -7803,6 +7862,12 @@ fn (mut g Gen) interface_table() string { // cctype is the Cleaned Concrete Type name, *without ptr*, // i.e. cctype is always just Cat, not Cat_ptr: cctype := g.cc_type(ast.mktyp(st), true) + cctype2 := if g.pref.skip_unused && st_sym_info.idx !in g.table.used_features.used_syms { + 'voidptr' + } else { + cctype + } + cctype_param := if cctype == cctype2 { cctype } else { 'void' } $if debug_interface_table ? { eprintln('>> interface name: ${isym.name} | concrete type: ${st.debug()} | st symname: ${st_sym.name}') } @@ -7813,43 +7878,47 @@ fn (mut g Gen) interface_table() string { } already_generated_mwrappers[interface_index_name] = current_iinidx current_iinidx++ - sb.writeln('static ${interface_name} I_${cctype}_to_Interface_${interface_name}(${cctype}* x);') + sb.writeln('static ${interface_name} I_${cctype}_to_Interface_${interface_name}(${cctype_param}* x);') mut cast_struct := strings.new_builder(100) cast_struct.writeln('(${interface_name}) {') - cast_struct.writeln('\t\t._${cctype} = x,') + cast_struct.writeln('\t\t._${cctype2} = x,') cast_struct.writeln('\t\t._typ = ${interface_index_name},') - for field in inter_info.fields { - cname := c_name(field.name) - field_styp := g.styp(field.typ) - if _ := st_sym.find_field(field.name) { - cast_struct.writeln('\t\t.${cname} = (${field_styp}*)((char*)x + __offsetof_ptr(x, ${cctype}, ${cname})),') - } else if st_sym.kind == .array - && field.name in ['element_size', 'data', 'offset', 'len', 'cap', 'flags'] { - // Manually checking, we already knows array contains above fields - cast_struct.writeln('\t\t.${cname} = (${field_styp}*)((char*)x + __offsetof_ptr(x, ${cctype}, ${cname})),') - } else { - // the field is embedded in another struct - cast_struct.write_string('\t\t.${cname} = (${field_styp}*)((char*)x') - if st != ast.voidptr_type && st != ast.nil_type { - if st_sym.kind == .struct { - if _, embeds := g.table.find_field_from_embeds(st_sym, field.name) { - mut typ_name := '' - for i, embed in embeds { - esym := g.table.sym(embed) - if i == 0 { - cast_struct.write_string(' + __offsetof_ptr(x, ${cctype}, ${esym.embed_name()})') - } else { - cast_struct.write_string(' + __offsetof_ptr(x, ${typ_name}, ${esym.embed_name()})') + if cctype == cctype2 { + for field in inter_info.fields { + cname := c_name(field.name) + field_styp := g.styp(field.typ) + if _ := st_sym.find_field(field.name) { + cast_struct.writeln('\t\t.${cname} = (${field_styp}*)((char*)x + __offsetof_ptr(x, ${cctype2}, ${cname})),') + } else if st_sym.kind == .array + && field.name in ['element_size', 'data', 'offset', 'len', 'cap', 'flags'] { + // Manually checking, we already knows array contains above fields + cast_struct.writeln('\t\t.${cname} = (${field_styp}*)((char*)x + __offsetof_ptr(x, ${cctype2}, ${cname})),') + } else { + // the field is embedded in another struct + cast_struct.write_string('\t\t.${cname} = (${field_styp}*)((char*)x') + if st != ast.voidptr_type && st != ast.nil_type { + if st_sym.kind == .struct { + if _, embeds := g.table.find_field_from_embeds(st_sym, + field.name) + { + mut typ_name := '' + for i, embed in embeds { + esym := g.table.sym(embed) + if i == 0 { + cast_struct.write_string(' + __offsetof_ptr(x, ${cctype}, ${esym.embed_name()})') + } else { + cast_struct.write_string(' + __offsetof_ptr(x, ${typ_name}, ${esym.embed_name()})') + } + typ_name = esym.cname + } + if embeds.len > 0 { + cast_struct.write_string(' + __offsetof_ptr(x, ${typ_name}, ${cname})') } - typ_name = esym.cname - } - if embeds.len > 0 { - cast_struct.write_string(' + __offsetof_ptr(x, ${typ_name}, ${cname})') } } } + cast_struct.writeln('),') } - cast_struct.writeln('),') } } cast_struct.write_string('\t}') @@ -7861,7 +7930,7 @@ fn (mut g Gen) interface_table() string { } cast_functions.writeln(' -static inline ${interface_name} I_${cctype}_to_Interface_${interface_name}(${cctype}* x) { +static inline ${interface_name} I_${cctype}_to_Interface_${interface_name}(${cctype_param}* x) { return ${cast_struct_str}; }') @@ -7973,7 +8042,7 @@ return ${cast_shared_struct_str}; } styp := g.cc_type(method.params[0].typ, true) mut method_call := '${styp}_${name}' - if !method.params[0].typ.is_ptr() { + if cctype == cctype2 && !method.params[0].typ.is_ptr() { if method.name !in aliased_method_names { method_call = '${cctype}_${name}' } else { @@ -8056,6 +8125,10 @@ return ${cast_shared_struct_str}; conversion_functions.write_string('static inline bool I_${interface_name}_is_I_${vsym.cname}(${interface_name} x) {\n\treturn ') for i, variant in variants { variant_sym := g.table.sym(variant) + if g.pref.skip_unused && variant_sym.kind == .struct + && variant_sym.idx !in g.table.used_features.used_syms { + continue + } if i > 0 { conversion_functions.write_string(' || ') } diff --git a/vlib/v/gen/c/testdata/aligned_attr_gcc_windows.vv b/vlib/v/gen/c/testdata/aligned_attr_gcc_windows.vv index 06069956e0..ba50f21c2b 100644 --- a/vlib/v/gen/c/testdata/aligned_attr_gcc_windows.vv +++ b/vlib/v/gen/c/testdata/aligned_attr_gcc_windows.vv @@ -1,17 +1,20 @@ // vtest vflags: -cc gcc -os windows @[aligned: 8] +@[markused] struct Test { a int } @[aligned: 16] +@[markused] struct Test2 { a int b int } @[aligned: 8] +@[markused] union Test3 { a int } diff --git a/vlib/v/gen/c/testdata/aligned_attr_msvc_windows.vv b/vlib/v/gen/c/testdata/aligned_attr_msvc_windows.vv index 3bf0312b81..2018e72195 100644 --- a/vlib/v/gen/c/testdata/aligned_attr_msvc_windows.vv +++ b/vlib/v/gen/c/testdata/aligned_attr_msvc_windows.vv @@ -1,17 +1,19 @@ // vtest vflags: -cc msvc -os windows - @[aligned: 8] +@[markused] struct Test { a int } @[aligned: 16] +@[markused] struct Test2 { a int b int } @[aligned: 8] +@[markused] union Test3 { a int } diff --git a/vlib/v/gen/c/testdata/aligned_attr_nix.vv b/vlib/v/gen/c/testdata/aligned_attr_nix.vv index 24e6382472..9b0365a9e2 100644 --- a/vlib/v/gen/c/testdata/aligned_attr_nix.vv +++ b/vlib/v/gen/c/testdata/aligned_attr_nix.vv @@ -1,15 +1,17 @@ -@[aligned] +@[aligned; markused] struct Test { a int } @[aligned: 16] +@[markused] struct Test2 { a int b int } @[aligned: 8] +@[markused] union Test3 { a int } diff --git a/vlib/v/gen/c/testdata/func_type_dependency.vv b/vlib/v/gen/c/testdata/func_type_dependency.vv index b00ee2637b..de1e1b78a5 100644 --- a/vlib/v/gen/c/testdata/func_type_dependency.vv +++ b/vlib/v/gen/c/testdata/func_type_dependency.vv @@ -1,10 +1,14 @@ // vtest vflags: -shared module gdi +@[markused] pub type Get_proc_address = fn (&i8) &Function_ptr +@[markused] pub type Set_proc_address = fn (&Function_ptr2) +@[markused] pub type Function_ptr = fn () +@[markused] pub type Function_ptr2 = fn () diff --git a/vlib/v/markused/markused.v b/vlib/v/markused/markused.v index 01df6837aa..79385fbb4d 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_global_decl(ast_files) + mut all_fns, all_consts, all_globals, all_fields, all_decltypes := all_global_decl(ast_files) util.timing_start('MARKUSED') defer { util.timing_measure('MARKUSED') @@ -52,7 +52,6 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a println('> used_fn, found matching symbol: ${m}') } } - if pref_.backend == .native { // Note: this is temporary, until the native backend supports more features! all_fn_root_names << 'main.main' @@ -441,25 +440,38 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a } mut walker := Walker.new( - table: table - files: ast_files - all_fns: all_fns - all_consts: all_consts - all_globals: all_globals - all_fields: all_fields - pref: pref_ + table: table + files: ast_files + all_fns: all_fns + all_consts: all_consts + all_globals: all_globals + all_fields: all_fields + all_decltypes: all_decltypes + pref: pref_ ) 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() for k, _ in table.used_features.comptime_calls { walker.fn_by_name(k) + // println('>>>>> ${k}') } + for k, _ in table.used_features.comptime_syms { + walker.mark_by_sym(table.sym(k)) + // println('>>>>> ${k}') + } + // println(all_fn_root_names) + walker.mark_root_fns(all_fn_root_names) + walker.mark_by_sym_name('vweb.RedirectParams') + walker.mark_by_sym_name('vweb.RequestParams') + if table.used_features.used_maps > 0 { for k, mut mfn in all_fns { mut method_receiver_typename := '' @@ -516,17 +528,25 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a } if walker.used_none > 0 || table.used_features.auto_str { walker.mark_fn_as_used('_option_none') + walker.mark_by_sym_name('_option') } if walker.used_option > 0 { walker.mark_fn_as_used('_option_clone') walker.mark_fn_as_used('_option_ok') + walker.mark_by_sym_name('_option') } if walker.used_result > 0 { walker.mark_fn_as_used('_result_ok') + walker.mark_by_sym_name('_result') } if (walker.used_option + walker.used_result + walker.used_none) > 0 { walker.mark_const_as_used('none__') } + walker.mark_by_sym_name('array') + + if table.used_features.asserts { + walker.mark_by_sym_name('VAssertMetaInfo') + } if trace_skip_unused_fn_names { for key, _ in walker.used_fns { @@ -538,14 +558,20 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a if walker.used_none == 0 { walker.used_fns.delete('${int(ast.none_type)}.str') } + + walker.remove_unused_fn_generic_types() + walker.remove_unused_dump_type() + 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() + table.used_features.used_syms = walker.used_syms.move() if trace_skip_unused { eprintln('>> t.used_fns: ${table.used_features.used_fns.keys()}') eprintln('>> t.used_consts: ${table.used_features.used_consts.keys()}') eprintln('>> t.used_globals: ${table.used_features.used_globals.keys()}') + eprintln('>> t.used_syms: ${table.used_features.used_syms.keys()}') eprintln('>> walker.table.used_features.used_maps: ${walker.table.used_features.used_maps}') } if trace_skip_unused_just_unused_fns { @@ -560,7 +586,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) { +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) { util.timing_start(@METHOD) defer { util.timing_measure(@METHOD) @@ -569,6 +595,7 @@ fn all_global_decl(ast_files []&ast.File) (map[string]ast.FnDecl, map[string]ast 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{} for i in 0 .. ast_files.len { for node in ast_files[i].stmts { match node { @@ -596,11 +623,14 @@ fn all_global_decl(ast_files []&ast.File) (map[string]ast.FnDecl, map[string]ast all_fields[sfkey] = sfield } } + ast.TypeDecl { + all_decltypes[node.name] = node.typ + } else {} } } } - return all_fns, all_consts, all_globals, all_fields + return all_fns, all_consts, all_globals, all_fields, all_decltypes } 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 710432cf3f..39ae73cee2 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -14,9 +14,8 @@ pub mut: 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_structs map[string]bool used_fields map[string]bool - used_ifaces map[string]bool + used_syms map[int]bool used_none int // _option_none used_option int // _option_ok used_result int // _result_ok @@ -25,11 +24,12 @@ pub mut: n_asserts int 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 + 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 } pub fn Walker.new(params Walker) &Walker { @@ -78,6 +78,7 @@ pub fn (mut w Walker) mark_const_as_used(ckey string) { w.used_consts[ckey] = true cfield := w.all_consts[ckey] or { return } w.expr(cfield.expr) + w.mark_by_type(cfield.typ) } pub fn (mut w Walker) mark_global_as_used(ckey string) { @@ -91,10 +92,7 @@ pub fn (mut w Walker) mark_global_as_used(ckey string) { gfield := w.all_globals[ckey] or { return } w.expr(gfield.expr) if !gfield.has_expr && gfield.typ != 0 { - sym := w.table.sym(gfield.typ) - if sym.info is ast.Struct { - w.a_struct_info(sym.name, sym.info) - } + w.mark_by_type(gfield.typ) } } @@ -167,6 +165,20 @@ pub fn (mut w Walker) mark_markused_globals() { } } +pub fn (mut w Walker) mark_markused_syms() { + for sym in w.table.type_symbols { + if sym.info is ast.Struct && sym.info.is_markused { + w.mark_by_sym(sym) + } + } +} + +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 { @@ -201,7 +213,28 @@ pub fn (mut w Walker) stmt(node_ ast.Stmt) { w.stmts(node.stmts) } ast.ComptimeFor { + w.mark_by_type(node.typ) w.stmts(node.stmts) + match node.kind { + .attributes { + w.mark_by_sym_name('VAttribute') + } + .variants { + w.mark_by_sym_name('VariantData') + } + .params { + w.mark_by_sym_name('MethodParam') + } + .values { + w.mark_by_sym_name('EnumData') + } + .fields { + w.mark_by_sym_name('FieldData') + } + .methods { + w.mark_by_sym_name('FunctionData') + } + } } ast.ConstDecl { w.const_fields(node.fields) @@ -258,6 +291,9 @@ pub fn (mut w Walker) stmt(node_ ast.Stmt) { w.expr(node.db_expr) w.expr(node.or_expr) for line in node.lines { + if line.table_expr.typ != 0 { + w.mark_by_sym(w.table.sym(line.table_expr.typ)) + } w.expr(line.where_expr) w.exprs(line.update_exprs) } @@ -325,10 +361,7 @@ 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.mark_by_type(node.elem_type) w.expr(node.len_expr) w.expr(node.cap_expr) w.expr(node.init_expr) @@ -343,19 +376,27 @@ fn (mut w Walker) expr(node_ ast.Expr) { } ast.CallExpr { w.call_expr(mut node) + if node.name == 'json.decode' { + w.mark_by_type((node.args[0].expr as ast.TypeNode).typ) + } } ast.CastExpr { w.expr(node.expr) w.expr(node.arg) + w.mark_by_type(node.typ) if node.typ.has_flag(.option) { w.used_option++ + } else if node.typ.has_flag(.result) { + w.used_result++ } } ast.ChanInit { w.expr(node.cap_expr) + w.mark_by_type(node.typ) } ast.ConcatExpr { w.exprs(node.vals) + w.mark_by_sym(w.table.sym(node.return_type)) } ast.ComptimeSelector { w.expr(node.left) @@ -374,6 +415,9 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.expr(node.expr) w.fn_by_name('eprint') w.fn_by_name('eprintln') + if node.expr_type != 0 { + w.mark_by_type(node.expr_type) + } } ast.SpawnExpr { if node.is_expr { @@ -403,58 +447,71 @@ fn (mut w Walker) expr(node_ ast.Expr) { return } sym := w.table.final_sym(node.left_type) - if sym.kind == .map { + if sym.info is ast.Map { if node.is_setter { w.mark_builtin_map_method_as_used('set') } else { w.mark_builtin_map_method_as_used('get') } - + w.mark_by_sym(w.table.sym(sym.info.key_type)) + w.mark_by_sym(w.table.sym(sym.info.value_type)) w.features.used_maps++ - } else if sym.kind == .array { + } 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') } - + w.mark_by_sym(w.table.sym(sym.info.elem_type)) w.features.used_arrays++ } else if sym.kind == .string { if node.index is ast.RangeExpr { w.mark_builtin_array_method_as_used('slice') w.features.range_index = true } + } else if sym.info is ast.Struct { + w.mark_by_sym(sym) + } else if sym.info is ast.SumType { + w.mark_by_sym(sym) } } ast.InfixExpr { w.expr(node.left) w.expr(node.right) w.or_block(node.or_block) - if node.left_type == 0 { - return - } - sym := w.table.sym(node.left_type) - if sym.kind == .struct { - if opmethod := sym.find_method(node.op.str()) { - unsafe { - w.fn_decl(mut &ast.FnDecl(opmethod.source_fn)) + if node.left_type != 0 { + sym := w.table.sym(node.left_type) + if sym.kind == .struct { + if opmethod := sym.find_method(node.op.str()) { + unsafe { + w.fn_decl(mut &ast.FnDecl(opmethod.source_fn)) + } } } } - if node.right_type == 0 { - return + right_type := if node.right_type == 0 && mut node.right is ast.TypeNode { + node.right.typ + } else { + node.right_type } - right_sym := w.table.sym(node.right_type) - if node.op in [.not_in, .key_in] { - if right_sym.kind == .map { - w.features.used_maps++ - } else if right_sym.kind == .array { - w.features.used_arrays++ + if right_type != 0 { + right_sym := w.table.sym(right_type) + if node.op in [.not_in, .key_in] { + if right_sym.kind == .map { + w.features.used_maps++ + } else if right_sym.kind == .array { + w.features.used_arrays++ + } + } else if node.op in [.key_is, .not_is] { + w.mark_by_sym(right_sym) } } } ast.IfGuardExpr { w.expr(node.expr) + if node.expr_type != 0 { + w.mark_by_type(node.expr_type) + } } ast.IfExpr { w.expr(node.left) @@ -470,13 +527,23 @@ fn (mut w Walker) expr(node_ ast.Expr) { } .function { w.fn_by_name(node.name) + if node.info is ast.IdentFn { + w.mark_by_type(node.info.typ) + } } .global { w.mark_global_as_used(node.name) } else { // `.unresolved`, `.blank_ident`, `.variable`, `.function` - // println('>>> else, ast.Ident kind: $node.kind') + // println('>>> else, ast.Ident ${node.name} kind: $node.kind ') + if node.name in w.all_consts { + w.mark_const_as_used(node.name) + } else if node.name in w.all_globals { + w.mark_global_as_used(node.name) + } else { + w.fn_by_name(node.name) + } } } w.or_block(node.or_expr) @@ -493,12 +560,27 @@ fn (mut w Walker) expr(node_ ast.Expr) { if node.has_update_expr { w.expr(node.update_expr) } + mapinfo := w.table.final_sym(node.typ).map_info() + ksym := w.table.sym(mapinfo.key_type) + vsym := w.table.sym(mapinfo.value_type) + if node.typ.has_flag(.shared_f) { + if sym := w.table.find_sym('sync.RwMutex') { + w.mark_by_sym(sym) + } + } + w.mark_by_sym(ksym) + w.mark_by_sym(vsym) w.features.used_maps++ } ast.MatchExpr { w.expr(node.cond) for b in node.branches { w.exprs(b.exprs) + for expr in b.exprs { + if expr is ast.TypeNode { + w.mark_by_sym(w.table.sym(expr.typ)) + } + } w.stmts(b.stmts) } } @@ -525,6 +607,9 @@ fn (mut w Walker) expr(node_ ast.Expr) { } ast.SizeOf, ast.IsRefType { w.expr(node.expr) + if node.typ != 0 { + w.mark_by_type(node.typ) + } } ast.StringInterLiteral { w.used_interp++ @@ -533,14 +618,14 @@ fn (mut w Walker) expr(node_ ast.Expr) { ast.SelectorExpr { w.expr(node.expr) if node.expr_type != 0 { - esym := w.table.sym(node.expr_type) - if esym.kind == .interface { - w.mark_interface_by_symbol(esym) - } + w.mark_by_type(node.expr_type) if method := w.table.find_method(w.table.sym(node.expr_type), node.field_name) { w.fn_by_name(method.fkey()) } } + if node.typ != 0 { + w.mark_by_type(node.typ) + } w.or_block(node.or_block) } ast.SqlExpr { @@ -550,15 +635,14 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.expr(node.order_expr) w.expr(node.limit_expr) w.expr(node.where_expr) + w.mark_by_type(node.typ) } ast.StructInit { if node.typ == 0 { return } sym := w.table.sym(node.typ) - if sym.info is ast.Struct { - w.a_struct_info(sym.name, sym.info) - } + w.mark_by_sym(sym) if node.has_update_expr { w.expr(node.update_expr) } @@ -568,12 +652,16 @@ fn (mut w Walker) expr(node_ ast.Expr) { } ast.TypeOf { w.expr(node.expr) + if node.typ != 0 { + w.mark_by_type(node.typ) + } } /// ast.AsCast { w.expr(node.expr) w.fn_by_name('__as_cast') w.fn_by_name('new_array_from_c_array') + w.mark_by_sym_name('VCastTypeIndexName') } ast.AtExpr {} ast.BoolLiteral {} @@ -592,8 +680,12 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.expr(filtered[0].expr) } } + w.mark_by_sym_name(node.enum_name) } ast.LockExpr { + if sym := w.table.find_sym('sync.RwMutex') { + w.mark_by_sym(sym) + } w.stmts(node.stmts) } ast.OffsetOf {} @@ -606,7 +698,9 @@ fn (mut w Walker) expr(node_ ast.Expr) { w.stmts(branch.stmts) } } - ast.TypeNode {} + ast.TypeNode { + w.mark_by_type(node.typ) + } ast.UnsafeExpr { w.expr(node.expr) } @@ -614,65 +708,13 @@ fn (mut w Walker) expr(node_ ast.Expr) { } } -pub fn (mut w Walker) a_struct_info(sname string, info ast.Struct) { - if sname in w.used_structs { - return - } - w.used_structs[sname] = true - for ifield in info.fields { - if ifield.has_default_expr { - w.expr(ifield.default_expr) - } - if ifield.typ != 0 { - fsym := w.table.sym(ifield.typ) - if ifield.typ.has_flag(.option) { - w.used_option++ - if !ifield.has_default_expr { - w.used_none++ - } - } - match fsym.info { - ast.Struct { - w.a_struct_info(fsym.name, fsym.info) - } - ast.Alias { - value_sym := w.table.final_sym(ifield.typ) - if value_sym.info is ast.Struct { - w.a_struct_info(value_sym.name, value_sym.info) - } - } - ast.Array, ast.ArrayFixed { - w.features.used_arrays++ - value_sym := w.table.final_sym(w.table.value_type(ifield.typ)) - if value_sym.info is ast.Struct { - w.a_struct_info(value_sym.name, value_sym.info) - } - } - ast.Map { - w.features.used_maps++ - value_sym := w.table.final_sym(w.table.value_type(ifield.typ)) - if value_sym.info is ast.Struct { - w.a_struct_info(value_sym.name, value_sym.info) - } - } - else {} - } - } - } - for embed in info.embeds { - sym := w.table.final_sym(embed) - if sym.info is ast.Struct { - w.a_struct_info(sym.name, sym.info) - } - } -} - pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) { if node == unsafe { nil } { return } if node.language == .c { w.mark_fn_as_used(node.fkey()) + w.mark_fn_ret_and_params(node.return_type, node.params) return } fkey := node.fkey() @@ -682,11 +724,10 @@ pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) { if node.no_body { return } - if node.return_type.has_flag(.option) { - w.used_option++ - } else if node.return_type.has_flag(.result) { - w.used_result++ + 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) w.stmts(node.stmts) w.defer_stmts(node.defer_stmts) @@ -699,13 +740,20 @@ pub fn (mut w Walker) call_expr(mut node ast.CallExpr) { for arg in node.args { w.expr(arg.expr) } + for concrete_type in node.concrete_types { + w.mark_by_type(concrete_type) + } if node.language == .c { if node.name in ['C.wyhash', 'C.wyhash64'] { w.features.used_maps++ } + if node.return_type != 0 { + w.mark_by_type(node.return_type) + } return } if node.is_method && node.left_type != 0 { + w.mark_by_type(node.left_type) left_sym := w.table.sym(node.left_type) if left_sym.info is ast.Aggregate { for receiver_type in left_sym.info.types { @@ -718,7 +766,6 @@ pub fn (mut w Walker) call_expr(mut node ast.CallExpr) { } } } else if left_sym.info is ast.Interface { - w.mark_interface_by_symbol(left_sym) for typ in left_sym.info.types { sym := w.table.sym(typ) _, embed_types := w.table.find_method_from_embeds(sym, node.name) or { @@ -779,6 +826,7 @@ 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 { + w.mark_fn_ret_and_params(stmt.return_type, stmt.params) w.stmts(stmt.stmts) } if node.return_type.has_flag(.option) { @@ -795,6 +843,7 @@ pub fn (mut w Walker) fn_by_name(fn_name string) { } stmt := w.all_fns[fn_name] or { return } w.mark_fn_as_used(fn_name) + w.mark_fn_ret_and_params(stmt.return_type, stmt.params) w.stmts(stmt.stmts) } @@ -841,19 +890,174 @@ pub fn (mut w Walker) mark_panic_deps() { 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_interface_by_symbol(isym ast.TypeSymbol) { - if isym.name in w.used_ifaces { +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) { + w.used_option++ + } else if return_type.has_flag(.result) { + w.used_result++ + } + w.mark_by_type(return_type.clear_option_and_result()) + } + for param in params { + w.mark_by_type(param.typ) + } +} + +pub fn (mut w Walker) mark_by_sym_name(name string) { + if sym := w.table.find_sym(name) { + w.mark_by_sym(sym) + } +} + +pub fn (mut w Walker) mark_by_type(typ ast.Type) { + if typ.has_flag(.generic) { return } - w.used_ifaces[isym.name] = true - if isym.info is ast.Interface { - for typ in isym.info.types { - if typ == ast.map_type { - w.features.used_maps++ + sym := w.table.sym(typ) + w.mark_by_sym(sym) +} + +pub fn (mut w Walker) mark_by_sym(isym ast.TypeSymbol) { + if isym.idx in w.used_syms { + return + } + w.used_syms[isym.idx] = true + match isym.info { + ast.Struct { + for ifield in isym.info.fields { + if ifield.has_default_expr { + w.expr(ifield.default_expr) + } + if ifield.typ != 0 { + fsym := w.table.sym(ifield.typ) + if ifield.typ.has_flag(.option) { + w.used_option++ + if !ifield.has_default_expr { + w.used_none++ + } + } + match fsym.info { + ast.Struct, ast.SumType, ast.FnType, ast.Alias, ast.Chan { + w.mark_by_sym(fsym) + } + ast.Array, ast.ArrayFixed { + w.features.used_arrays++ + w.mark_by_type(ifield.typ) + } + ast.Map { + w.features.used_maps++ + w.mark_by_type(ifield.typ) + } + else {} + } + } + } + for embed in isym.info.embeds { + w.mark_by_type(embed) + } + } + ast.ArrayFixed, ast.Array { + w.mark_by_type(isym.info.elem_type) + } + ast.SumType { + for typ in isym.info.variants { + if typ == ast.map_type { + w.features.used_maps++ + continue + } + w.mark_by_type(typ) + } + } + ast.Map { + w.mark_by_type(isym.info.key_type) + w.mark_by_type(isym.info.value_type) + w.features.used_maps++ + } + ast.Alias { + w.mark_by_type(isym.info.parent_type) + } + ast.FnType { + for param in isym.info.func.params { + w.mark_by_type(param.typ) + } + if isym.info.func.return_type != 0 { + w.mark_by_type(isym.info.func.return_type.clear_option_and_result()) + } + } + ast.MultiReturn { + for typ in isym.info.types { + w.mark_by_type(typ) + } + } + ast.Chan { + w.mark_by_type(isym.info.elem_type) + } + ast.Aggregate { + for typ in isym.info.types { + w.mark_by_type(typ) + } + } + ast.Enum { + w.mark_by_type(isym.info.typ) + } + ast.Interface { + for typ in isym.info.types { + if typ == ast.map_type { + w.features.used_maps++ + } + w.mark_by_type(typ) + } + for embed in isym.info.embeds { + w.mark_by_type(embed) + } + for generic_type in isym.info.generic_types { + w.mark_by_type(generic_type) + } + if isym.info.parent_type != 0 { + w.mark_by_type(isym.info.parent_type) + } + for method in isym.methods { + if method.receiver_type != 0 { + w.mark_by_type(method.receiver_type) + } + w.mark_fn_ret_and_params(method.return_type, method.params) + } + } + else {} + } +} + +pub fn (mut w Walker) remove_unused_fn_generic_types() { + for _, node in w.all_fns { + mut count := 0 + nkey := node.fkey() + if all_concrete_types := w.table.fn_generic_types[nkey] { + if all_concrete_types.len == 0 { + continue + } + for k, concrete_types in all_concrete_types { + if concrete_types.len != 1 { + continue + } + if concrete_types[0].idx() !in w.used_syms { + w.table.fn_generic_types[nkey].delete(k - count) + count++ + } } - // sym := w.table.sym(typ); eprintln('>>>>>>>>> typ: ${typ.str():-30} | sym.name: ${sym.name}') + } + } +} + +pub 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) } } } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index c1edbcdb46..c098a8900f 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2605,6 +2605,7 @@ fn source_name(name string) string { } fn (mut p Parser) type_decl() ast.TypeDecl { + attrs := p.attrs start_pos := p.tok.pos() is_pub := p.tok.kind == .key_pub if is_pub { @@ -2648,7 +2649,6 @@ fn (mut p Parser) type_decl() ast.TypeDecl { p.table.sym(fn_type).is_pub = is_pub type_pos = type_pos.extend(p.tok.pos()) comments = p.eat_comments(same_line: true) - attrs := p.attrs p.attrs = [] return ast.FnTypeDecl{ name: fn_name @@ -2659,6 +2659,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl { comments: comments generic_types: generic_types attrs: attrs + is_markused: attrs.contains('markused') } } sum_variants << p.parse_sum_type_variants() @@ -2704,6 +2705,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl { attrs: p.attrs pos: decl_pos name_pos: name_pos + is_markused: attrs.contains('markused') } p.table.register_sumtype(node) return node @@ -2755,6 +2757,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl { type_pos: type_pos.extend(type_end_pos) pos: decl_pos comments: comments + is_markused: attrs.contains('markused') } } diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index 3c29729f06..78db510f40 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -406,6 +406,7 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl { is_typedef: attrs.contains('typedef') is_union: is_union is_heap: attrs.contains('heap') + is_markused: attrs.contains('markused') is_minify: is_minify is_generic: generic_types.len > 0 generic_types: generic_types diff --git a/vlib/v/tests/skip_unused/skip_sumtype_unused.run.out b/vlib/v/tests/skip_unused/skip_sumtype_unused.run.out new file mode 100644 index 0000000000..9183bf03fc --- /dev/null +++ b/vlib/v/tests/skip_unused/skip_sumtype_unused.run.out @@ -0,0 +1 @@ +256 diff --git a/vlib/v/tests/skip_unused/skip_sumtype_unused.skip_unused.run.out b/vlib/v/tests/skip_unused/skip_sumtype_unused.skip_unused.run.out new file mode 100644 index 0000000000..9183bf03fc --- /dev/null +++ b/vlib/v/tests/skip_unused/skip_sumtype_unused.skip_unused.run.out @@ -0,0 +1 @@ +256 diff --git a/vlib/v/tests/skip_unused/skip_sumtype_unused.vv b/vlib/v/tests/skip_unused/skip_sumtype_unused.vv new file mode 100644 index 0000000000..f4aa5efd03 --- /dev/null +++ b/vlib/v/tests/skip_unused/skip_sumtype_unused.vv @@ -0,0 +1,6 @@ +import encoding.binary + +fn main() { + x := binary.big_endian_u16_fixed([u8(1), 0]!) + println(x) +}