diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index e7dabab438..678a12d75a 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -535,11 +535,11 @@ pub: pub struct GlobalField { pub: name string - expr Expr has_expr bool pos token.Position typ_pos token.Position pub mut: + expr Expr typ Type comments []Comment } diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index fbcc5e239f..4b19dcf7fc 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -26,6 +26,7 @@ pub mut: is_fmt bool used_fns map[string]bool // filled in by the checker, when pref.skip_unused = true; used_consts map[string]bool // filled in by the checker, when pref.skip_unused = true; + used_globals map[string]bool // filled in by the checker, when pref.skip_unused = true; used_vweb_types []Type // vweb context types, filled in by checker, when pref.skip_unused = true; used_maps int // how many times maps were used, filled in by checker, when pref.skip_unused = true; panic_handler FnPanicHandler = default_table_panic_handler @@ -52,6 +53,7 @@ pub fn (t &Table) free() { t.cmod_prefix.free() t.used_fns.free() t.used_consts.free() + t.used_globals.free() t.used_vweb_types.free() } } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 2438d707b0..5f3d5f90bf 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -4544,7 +4544,7 @@ fn (mut c Checker) stmt(node ast.Stmt) { c.for_stmt(mut node) } ast.GlobalDecl { - c.global_decl(node) + c.global_decl(mut node) } ast.GotoLabel {} ast.GotoStmt { @@ -4793,8 +4793,8 @@ fn (mut c Checker) for_stmt(mut node ast.ForStmt) { c.in_for_count-- } -fn (mut c Checker) global_decl(node ast.GlobalDecl) { - for field in node.fields { +fn (mut c Checker) global_decl(mut node ast.GlobalDecl) { + for mut field in node.fields { c.check_valid_snake_case(field.name, 'global name', field.pos) if field.name in c.global_names { c.error('duplicate global `$field.name`', field.pos) @@ -4803,6 +4803,14 @@ fn (mut c Checker) global_decl(node ast.GlobalDecl) { if sym.kind == .placeholder { c.error('unknown type `$sym.name`', field.typ_pos) } + if field.expr !is ast.EmptyExpr { + expr_typ := c.expr(field.expr) + if !c.check_types(expr_typ, field.typ) { + got_sym := c.table.get_type_symbol(expr_typ) + c.error('cannot initialize global variable `$field.name` of type `$sym.name` with expression of type `$got_sym.name`', + field.expr.position()) + } + } c.global_names << field.name } } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index eeba8e8adb..a8dd6e0ffd 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -5113,9 +5113,18 @@ fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ fn (mut g Gen) global_decl(node ast.GlobalDecl) { mod := if g.pref.build_mode == .build_module && g.is_builtin_mod { 'static ' } else { '' } for field in node.fields { + if g.pref.skip_unused { + if field.name !in g.table.used_globals { + $if trace_skip_unused_globals ? { + eprintln('>> skipping unused global name: $field.name') + } + continue + } + } styp := g.typ(field.typ) if field.has_expr { - g.definitions.writeln('$mod$styp $field.name = $field.expr; // global') + g.definitions.writeln('$mod$styp $field.name;') + g.global_initializations.writeln('\t$field.name = ${g.expr_string(field.expr)}; // global') } else { default_initializer := g.type_default(field.typ) if default_initializer == '{0}' { diff --git a/vlib/v/markused/markused.v b/vlib/v/markused/markused.v index a81651c745..77c0957e36 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, pref &pref.Preferences, ast_files []&ast.File) { - mut all_fns, all_consts := all_fn_and_const(ast_files) + mut all_fns, all_consts, all_globals := all_fn_const_and_global(ast_files) util.timing_start(@METHOD) defer { util.timing_measure(@METHOD) @@ -316,6 +316,7 @@ pub fn mark_used(mut table ast.Table, pref &pref.Preferences, ast_files []&ast.F files: ast_files all_fns: all_fns all_consts: all_consts + all_globals: all_globals pref: pref } // println( all_fns.keys() ) @@ -368,21 +369,24 @@ pub fn mark_used(mut table ast.Table, pref &pref.Preferences, ast_files []&ast.F table.used_fns = walker.used_fns.move() table.used_consts = walker.used_consts.move() + table.used_globals = walker.used_globals.move() $if trace_skip_unused ? { eprintln('>> t.used_fns: $table.used_fns.keys()') eprintln('>> t.used_consts: $table.used_consts.keys()') + eprintln('>> t.used_globals: $table.used_globals.keys()') eprintln('>> walker.table.used_maps: $walker.table.used_maps') } } -fn all_fn_and_const(ast_files []&ast.File) (map[string]ast.FnDecl, map[string]ast.ConstField) { +fn all_fn_const_and_global(ast_files []&ast.File) (map[string]ast.FnDecl, map[string]ast.ConstField, map[string]ast.GlobalField) { util.timing_start(@METHOD) defer { util.timing_measure(@METHOD) } mut all_fns := map[string]ast.FnDecl{} mut all_consts := map[string]ast.ConstField{} + mut all_globals := map[string]ast.GlobalField{} for i in 0 .. ast_files.len { file := ast_files[i] for node in file.stmts { @@ -397,9 +401,15 @@ fn all_fn_and_const(ast_files []&ast.File) (map[string]ast.FnDecl, map[string]as all_consts[ckey] = cfield } } + ast.GlobalDecl { + for gfield in node.fields { + gkey := gfield.name + all_globals[gkey] = gfield + } + } else {} } } } - return all_fns, all_consts + return all_fns, all_consts, all_globals } diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index 680ffbb829..fea9c09073 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -9,15 +9,17 @@ import v.pref pub struct Walker { pub mut: - table &ast.Table - used_fns map[string]bool // used_fns['println'] == true - used_consts map[string]bool // used_consts['os.args'] == true - n_asserts int - pref &pref.Preferences + table &ast.Table + used_fns map[string]bool // used_fns['println'] == true + used_consts map[string]bool // used_consts['os.args'] == true + used_globals map[string]bool + n_asserts int + pref &pref.Preferences mut: - files []&ast.File - all_fns map[string]ast.FnDecl - all_consts map[string]ast.ConstField + files []&ast.File + all_fns map[string]ast.FnDecl + all_consts map[string]ast.ConstField + all_globals map[string]ast.GlobalField } pub fn (mut w Walker) mark_fn_as_used(fkey string) { @@ -36,6 +38,15 @@ pub fn (mut w Walker) mark_const_as_used(ckey string) { w.expr(cfield.expr) } +pub fn (mut w Walker) mark_global_as_used(ckey string) { + $if trace_skip_unused_marked ? { + eprintln(' global > |$ckey|') + } + w.used_globals[ckey] = true + gfield := w.all_globals[ckey] or { return } + w.expr(gfield.expr) +} + pub fn (mut w Walker) mark_root_fns(all_fn_root_names []string) { for fn_name in all_fn_root_names { if fn_name !in w.used_fns { @@ -268,8 +279,11 @@ fn (mut w Walker) expr(node ast.Expr) { .function { w.fn_by_name(node.name) } + .global { + w.mark_global_as_used(node.name) + } else { - // `.unresolved`, `.blank_ident`, `.variable`, `.global`, `.function` + // `.unresolved`, `.blank_ident`, `.variable`, `.function` // println('>>> else, ast.Ident kind: $node.kind') } }