markused: skip unused symbols, dump fns and generic specialization (fix #24921) (fix #24927) (#24924)

This commit is contained in:
Felipe Pena 2025-07-23 02:50:31 -03:00 committed by GitHub
parent 938f462daf
commit ae1fa8aced
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 558 additions and 163 deletions

View File

@ -12,6 +12,7 @@ import v.util.version
import v.builder
import v.builder.cbuilder
@[markused]
const external_tools = [
'ast',
'bin2v',

View File

@ -32,6 +32,7 @@ pub:
thickness f32 = 1
}
@[markused]
pub struct Size {
pub mut:
width int

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -9,6 +9,7 @@ $if freebsd || openbsd {
#include <sys/sysctl.h>
}
@[markused]
pub const args = arguments()
fn C.readdir(voidptr) &C.dirent

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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')

View File

@ -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 {

View File

@ -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(' || ')
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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 ()

View File

@ -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) {

View File

@ -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)
}
}
}

View File

@ -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')
}
}

View File

@ -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

View File

@ -0,0 +1 @@
256

View File

@ -0,0 +1 @@
256

View File

@ -0,0 +1,6 @@
import encoding.binary
fn main() {
x := binary.big_endian_u16_fixed([u8(1), 0]!)
println(x)
}