From 0aa9f5a00782252b4b916ea343ea074a13983761 Mon Sep 17 00:00:00 2001 From: joe-conigliaro Date: Sat, 12 Dec 2020 19:01:12 +1100 Subject: [PATCH] all: optimize scope usage in checker & parser. store scope in ast nodes (#7281) --- vlib/v/ast/ast.v | 11 +++ vlib/v/checker/checker.v | 151 ++++++++++++++++++--------------------- vlib/v/gen/cgen.v | 4 +- vlib/v/parser/comptime.v | 3 +- vlib/v/parser/fn.v | 36 +++++++--- vlib/v/parser/for.v | 32 +++++---- vlib/v/parser/if_match.v | 26 +++---- vlib/v/parser/lock.v | 1 + vlib/v/parser/parser.v | 20 +++++- vlib/v/parser/pratt.v | 1 + vlib/v/parser/struct.v | 1 + 11 files changed, 167 insertions(+), 119 deletions(-) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index b10b532937..cefe86dfcc 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -112,6 +112,7 @@ pub mut: expr_type table.Type // type of `Foo` in `Foo.bar` typ table.Type // type of the entire thing (`Foo.bar`) name_type table.Type // T in `T.name` or typeof in `typeof(expr).name` + scope &Scope } // root_ident returns the origin ident where the selector started. @@ -287,6 +288,7 @@ pub mut: return_type table.Type comments []Comment // comments *after* the header, but *before* `{`; used for InterfaceDecl source_file &File = 0 + scope &Scope } // break, continue @@ -317,6 +319,7 @@ pub mut: generic_type table.Type // TODO array, to support multiple types generic_list_pos token.Position free_receiver bool // true if the receiver expression needs to be freed + scope &Scope } /* @@ -460,6 +463,7 @@ pub: pos token.Position mut_pos token.Position pub mut: + scope &Scope obj ScopeObject mod string name string @@ -548,6 +552,7 @@ pub: pub mut: stmts []Stmt smartcast bool // true when cond is `x is SumType`, set in checker.if_expr // no longer needed with union sum types TODO: remove + scope &Scope } pub struct UnsafeExpr { @@ -590,6 +595,8 @@ pub: comments []Comment // comment above `xxx {` is_else bool post_comments []Comment +pub mut: + scope &Scope } pub struct SelectExpr { @@ -637,6 +644,7 @@ pub: pos token.Position pub mut: label string // `label: for {` + scope &Scope } pub struct ForInStmt { @@ -656,6 +664,7 @@ pub mut: cond_type table.Type kind table.Kind // array/map/string label string // `label: for {` + scope &Scope } pub struct ForCStmt { @@ -670,6 +679,7 @@ pub: pos token.Position pub mut: label string // `label: for {` + scope &Scope } // #include etc @@ -942,6 +952,7 @@ pub: pos token.Position pub mut: typ table.Type + scope &Scope } pub struct SizeOf { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 09d25f8fad..43afd8108e 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -147,6 +147,9 @@ pub fn (mut c Checker) check_files(ast_files []ast.File) { mod: 'main' file: first_main_file.path return_type: table.void_type + scope: &ast.Scope{ + parent: 0 + } } has_main_fn = true } @@ -1141,15 +1144,13 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { is_sort := method_name == 'sort' if is_filter_map || is_sort { array_info := left_type_sym.info as table.Array - args_pos := call_expr.pos.pos + call_expr.name.len - mut scope := c.file.scope.innermost(args_pos) if is_filter_map { // position of `it` doesn't matter - scope_register_it(mut scope, call_expr.pos, array_info.elem_type) + scope_register_it(mut call_expr.scope, call_expr.pos, array_info.elem_type) } else if is_sort { c.fail_if_immutable(call_expr.left) // position of `a` and `b` doesn't matter, they're the same - scope_register_ab(mut scope, call_expr.pos, array_info.elem_type) + scope_register_ab(mut call_expr.scope, call_expr.pos, array_info.elem_type) // Verify `.sort(a < b)` if call_expr.args.len > 0 { if call_expr.args[0].expr !is ast.InfixExpr { @@ -1450,8 +1451,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type { } // check for arg (var) of fn type if !found { - scope := c.file.scope.innermost(call_expr.pos.pos) - if v := scope.find_var(fn_name) { + if v := call_expr.scope.find_var(fn_name) { if v.typ != 0 { vts := c.table.get_type_symbol(v.typ) if vts.kind == .function { @@ -1468,8 +1468,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type { return table.void_type } if !found_in_args { - scope := c.file.scope.innermost(call_expr.pos.pos) - if _ := scope.find_var(fn_name) { + if _ := call_expr.scope.find_var(fn_name) { c.error('ambiguous call to: `$fn_name`, may refer to fn `$fn_name` or variable `$fn_name`', call_expr.pos) } @@ -1825,8 +1824,7 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) table.T field_sym := c.table.get_type_symbol(field.typ) if field_sym.kind == .sum_type { if !prevent_sum_type_unwrapping_once { - scope := c.file.scope.innermost(selector_expr.pos.pos) - if scope_field := scope.find_struct_field(utyp, field_name) { + if scope_field := selector_expr.scope.find_struct_field(utyp, field_name) { return scope_field.sum_type_casts.last() } } @@ -2052,38 +2050,6 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { } return } - // Check `x := &y` and `mut x := <-ch` - if right_first is ast.PrefixExpr { - node := right_first - left_first := assign_stmt.left[0] - if left_first is ast.Ident { - assigned_var := left_first - if node.right is ast.Ident { - scope := c.file.scope.innermost(node.pos.pos) - if v := scope.find_var(node.right.name) { - right_type0 = v.typ - if node.op == .amp { - if !v.is_mut && assigned_var.is_mut && !c.inside_unsafe { - c.error('`$node.right.name` is immutable, cannot have a mutable reference to it', - node.pos) - } - } - } - } - if node.op == .arrow { - if assigned_var.is_mut { - right_sym := c.table.get_type_symbol(right_type0) - if right_sym.kind == .chan { - chan_info := right_sym.chan_info() - if chan_info.elem_type.is_ptr() && !chan_info.is_mut { - c.error('cannot have a mutable reference to object from `$right_sym.name`', - node.pos) - } - } - } - } - } - } // is_decl := assign_stmt.op == .decl_assign for i, left in assign_stmt.left { @@ -2284,6 +2250,40 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { } } } + // this needs to run after the assign stmt left exprs have been run through checker so that ident.obj is set + // Check `x := &y` and `mut x := <-ch` + if right_first is ast.PrefixExpr { + node := right_first + left_first := assign_stmt.left[0] + if left_first is ast.Ident { + assigned_var := left_first + c.expr(node.right) + if node.right is ast.Ident { + if node.right.obj is ast.Var { + v := node.right.obj + right_type0 = v.typ + if node.op == .amp { + if !v.is_mut && assigned_var.is_mut && !c.inside_unsafe { + c.error('`$node.right.name` is immutable, cannot have a mutable reference to it', + node.pos) + } + } + } + } + if node.op == .arrow { + if assigned_var.is_mut { + right_sym := c.table.get_type_symbol(right_type0) + if right_sym.kind == .chan { + chan_info := right_sym.chan_info() + if chan_info.elem_type.is_ptr() && !chan_info.is_mut { + c.error('cannot have a mutable reference to object from `$right_sym.name`', + node.pos) + } + } + } + } + } + } } fn scope_register_it(mut s ast.Scope, pos token.Position, typ table.Type) { @@ -2422,25 +2422,18 @@ pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) table.Type { // [50]byte mut fixed_size := 1 init_expr := array_init.exprs[0] + c.expr(init_expr) match init_expr { ast.IntegerLiteral { fixed_size = init_expr.val.int() } ast.Ident { - // if obj := c.file.global_scope.find_const(init_expr.name) { - // if obj := scope.find(init_expr.name) { - // scope := c.file.scope.innermost(array_init.pos.pos) - // eprintln('scope: ${scope.str()}') - // scope.find(init_expr.name) or { - // c.error('undefined ident: `$init_expr.name`', array_init.pos) - // } - mut full_const_name := init_expr.mod + '.' + init_expr.name - if obj := c.file.global_scope.find_const(full_const_name) { - if cint := const_int_value(obj) { + if init_expr.obj is ast.ConstField { + if cint := const_int_value(init_expr.obj) { fixed_size = cint } } else { - c.error('non existent integer const $full_const_name while initializing the size of a static array', + c.error('non existent integer const $init_expr.name while initializing the size of a static array', array_init.pos) } } @@ -2584,7 +2577,6 @@ fn (mut c Checker) stmt(node ast.Stmt) { c.error('range type can not be string', node.cond.position()) } } else { - mut scope := c.file.scope.innermost(node.pos.pos) sym := c.table.get_type_symbol(typ) if sym.kind == .map && !(node.key_var.len > 0 && node.val_var.len > 0) { c.error('declare a key and a value variable when ranging a map: `for key, val in map {`\n' + @@ -2596,7 +2588,7 @@ fn (mut c Checker) stmt(node ast.Stmt) { else { table.int_type } } node.key_type = key_type - scope.update_var_type(node.key_var, key_type) + node.scope.update_var_type(node.key_var, key_type) } mut value_type := c.table.value_type(typ) if value_type == table.void_type || typ.has_flag(.optional) { @@ -2611,7 +2603,7 @@ fn (mut c Checker) stmt(node ast.Stmt) { node.cond_type = typ node.kind = sym.kind node.val_type = value_type - scope.update_var_type(node.val_var, value_type) + node.scope.update_var_type(node.val_var, value_type) } c.check_loop_label(node.label, node.pos) c.stmts(node.stmts) @@ -2873,8 +2865,7 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { return node.typ.to_ptr() } ast.Assoc { - scope := c.file.scope.innermost(node.pos.pos) - v := scope.find_var(node.var_name) or { panic(err) } + v := node.scope.find_var(node.var_name) or { panic(err) } for i, _ in node.fields { c.expr(node.exprs[i]) } @@ -3270,8 +3261,7 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type { if ident.tok_kind == .assign && ident.is_mut { c.error('`mut` not allowed with `=` (use `:=` to declare a variable)', ident.pos) } - start_scope := c.file.scope.innermost(ident.pos.pos) - if obj := start_scope.find(ident.name) { + if obj := ident.scope.find(ident.name) { match mut obj { ast.GlobalField { ident.kind = .global @@ -3509,7 +3499,8 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol // an expr was used in the match mut branch_exprs := map[string]int{} cond_type_sym := c.table.get_type_symbol(node.cond_type) - for branch in node.branches { + for branch_i, _ in node.branches { + mut branch := node.branches[branch_i] mut expr_types := []ast.Type{} for expr in branch.exprs { mut key := '' @@ -3623,7 +3614,6 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol } else { expr_type = expr_types[0].typ } - mut scope := c.file.scope.innermost(branch.pos.pos) match mut node.cond { ast.SelectorExpr { mut is_mut := false @@ -3632,18 +3622,19 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol if field := c.table.struct_find_field(expr_sym, node.cond.field_name) { if field.is_mut { root_ident := node.cond.root_ident() - if v := scope.find_var(root_ident.name) { + if v := branch.scope.find_var(root_ident.name) { is_mut = v.is_mut } } } - if field := scope.find_struct_field(node.cond.expr_type, node.cond.field_name) { + if field := branch.scope.find_struct_field(node.cond.expr_type, + node.cond.field_name) { sum_type_casts << field.sum_type_casts } // smartcast either if the value is immutable or if the mut argument is explicitly given if !is_mut || node.cond.is_mut { sum_type_casts << expr_type - scope.register_struct_field(ast.ScopeStructField{ + branch.scope.register_struct_field(ast.ScopeStructField{ struct_type: node.cond.expr_type name: node.cond.field_name typ: node.cond_type @@ -3656,7 +3647,8 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol mut is_mut := false mut sum_type_casts := []table.Type{} mut is_already_casted := false - if v := scope.find_var(node.cond.name) { + if node.cond.obj is ast.Var { + v := node.cond.obj as ast.Var is_mut = v.is_mut sum_type_casts << v.sum_type_casts is_already_casted = v.pos.pos == node.cond.pos.pos @@ -3664,7 +3656,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol // smartcast either if the value is immutable or if the mut argument is explicitly given if (!is_mut || node.cond.is_mut) && !is_already_casted { sum_type_casts << expr_type - scope.register(ast.Var{ + branch.scope.register(ast.Var{ name: node.cond.name typ: node.cond_type pos: node.cond.pos @@ -3891,10 +3883,9 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { // TODO: merge this code with match_expr because it has the same logic implemented if left_sym.kind in [.interface_, .sum_type] { mut is_mut := false - mut scope := c.file.scope.innermost(branch.body_pos.pos) if mut infix.left is ast.Ident { mut sum_type_casts := []table.Type{} - if v := scope.find_var(infix.left.name) { + if v := branch.scope.find_var(infix.left.name) { is_mut = v.is_mut sum_type_casts << v.sum_type_casts } @@ -3902,7 +3893,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { // smartcast either if the value is immutable or if the mut argument is explicitly given if !is_mut || infix.left.is_mut { sum_type_casts << right_expr.typ - scope.register(ast.Var{ + branch.scope.register(ast.Var{ name: infix.left.name typ: infix.left_type sum_type_casts: sum_type_casts @@ -3912,7 +3903,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { }) } } else if left_sym.kind == .interface_ { - scope.register(ast.Var{ + branch.scope.register(ast.Var{ name: infix.left.name typ: right_expr.typ.to_ptr() sum_type_casts: sum_type_casts @@ -3929,19 +3920,19 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { if field := c.table.struct_find_field(expr_sym, infix.left.field_name) { if field.is_mut { root_ident := infix.left.root_ident() - if v := scope.find_var(root_ident.name) { - is_mut = v.is_mut + if root_ident.obj is ast.Var { + is_mut = root_ident.obj.is_mut } } } - if field := scope.find_struct_field(infix.left.expr_type, + if field := branch.scope.find_struct_field(infix.left.expr_type, infix.left.field_name) { sum_type_casts << field.sum_type_casts } // smartcast either if the value is immutable or if the mut argument is explicitly given if (!is_mut || infix.left.is_mut) && left_sym.kind == .sum_type { sum_type_casts << right_expr.typ - scope.register_struct_field(ast.ScopeStructField{ + branch.scope.register_struct_field(ast.ScopeStructField{ struct_type: infix.left.expr_type name: infix.left.field_name typ: infix.left_type @@ -4159,12 +4150,12 @@ fn (mut c Checker) comp_if_branch(cond ast.Expr, pos token.Position) bool { } else if cond.name !in c.pref.compile_defines_all { // `$if some_var {}` typ := c.expr(cond) - scope := c.file.scope.innermost(pos.pos) - obj := scope.find(cond.name) or { + if cond.obj !is ast.Var && + cond.obj !is ast.ConstField && cond.obj !is ast.GlobalField { c.error('unknown var: `$cond.name`', pos) return false } - expr := c.find_obj_definition(obj) or { + expr := c.find_obj_definition(cond.obj) or { c.error(err, cond.pos) return false } @@ -4285,8 +4276,8 @@ pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) table.Type { if !c.inside_unsafe && (typ.is_ptr() || typ.is_pointer()) { mut is_ok := false if mut node.left is ast.Ident { - scope := c.file.scope.innermost(node.left.pos.pos) - if v := scope.find_var(node.left.name) { + if node.left.obj is ast.Var { + v := node.left.obj as ast.Var // `mut param []T` function parameter is_ok = v.is_mut && v.is_arg && !typ.deref().is_ptr() } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index a4f6eb941b..c037fe0a08 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -1735,7 +1735,9 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { val := assign_stmt.right[i] mut is_call := false mut blank_assign := false - mut ident := ast.Ident{} + mut ident := ast.Ident{ + scope: 0 + } if left is ast.Ident { ident = left // id_info := ident.var_info() diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index 1a6c0ac7a3..af1dec5e85 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -126,7 +126,8 @@ fn (mut p Parser) vweb() ast.ComptimeCall { for stmt in file.stmts { if stmt is ast.FnDecl { if stmt.name == 'main.vweb_tmpl_$tmp_fn_name' { - mut tmpl_scope := file.scope.innermost(stmt.body_pos.pos) + // mut tmpl_scope := file.scope.innermost(stmt.body_pos.pos) + mut tmpl_scope := stmt.scope for _, obj in p.scope.objects { if obj is ast.Var { mut v := obj diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 9cf036e8d1..32a924323e 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -101,6 +101,7 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp kind: or_kind pos: or_pos } + scope: p.scope } } @@ -201,12 +202,16 @@ fn (mut p Parser) fn_decl() ast.FnDecl { rec_type = p.parse_type_with_mut(rec_mut) if rec_type.idx() == 0 { // error is set in parse_type - return ast.FnDecl{} + return ast.FnDecl{ + scope: 0 + } } rec_type_pos = rec_type_pos.extend(p.prev_tok.position()) if is_amp && rec_mut { p.error('use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)`') - return ast.FnDecl{} + return ast.FnDecl{ + scope: 0 + } } if is_shared { rec_type = rec_type.set_flag(.shared_f) @@ -228,13 +233,17 @@ fn (mut p Parser) fn_decl() ast.FnDecl { name = if language == .js { p.check_js_name() } else { p.check_name() } if language == .v && !p.pref.translated && util.contains_capital(name) && p.mod != 'builtin' { p.error('function names cannot contain uppercase letters, use snake_case instead') - return ast.FnDecl{} + return ast.FnDecl{ + scope: 0 + } } type_sym := p.table.get_type_symbol(rec_type) // interfaces are handled in the checker, methods can not be defined on them this way if is_method && (type_sym.has_method(name) && type_sym.kind != .interface_) { p.error('duplicate method `$name`') - return ast.FnDecl{} + return ast.FnDecl{ + scope: 0 + } } } if p.tok.kind in [.plus, .minus, .mul, .div, .mod] { @@ -255,7 +264,9 @@ fn (mut p Parser) fn_decl() ast.FnDecl { for param in params { if p.scope.known_var(param.name) { p.error_with_pos('redefinition of parameter `$param.name`', param.pos) - return ast.FnDecl{} + return ast.FnDecl{ + scope: 0 + } } p.scope.register(ast.Var{ name: param.name @@ -291,7 +302,9 @@ fn (mut p Parser) fn_decl() ast.FnDecl { if is_non_local { p.error_with_pos('cannot define new methods on non-local type $type_sym.name', rec_type_pos) - return ast.FnDecl{} + return ast.FnDecl{ + scope: 0 + } } // p.warn('reg method $type_sym.name . $name ()') type_sym_method_idx = type_sym.register_method(table.Fn{ @@ -341,12 +354,13 @@ fn (mut p Parser) fn_decl() ast.FnDecl { if p.tok.kind == .lcbr { stmts = p.parse_block_no_scope(true) } - p.close_scope() if !no_body && are_args_type_only { p.error_with_pos('functions with type only args can not have bodies', body_start_pos) - return ast.FnDecl{} + return ast.FnDecl{ + scope: 0 + } } - return ast.FnDecl{ + fn_decl := ast.FnDecl{ name: name mod: p.mod stmts: stmts @@ -373,7 +387,10 @@ fn (mut p Parser) fn_decl() ast.FnDecl { file: p.file_name is_builtin: p.builtin_mod || p.mod in util.builtin_module_parts attrs: p.attrs + scope: p.scope } + p.close_scope() + return fn_decl } fn (mut p Parser) anon_fn() ast.AnonFn { @@ -425,6 +442,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn { no_body: no_body pos: pos file: p.file_name + scope: p.scope } typ: typ } diff --git a/vlib/v/parser/for.v b/vlib/v/parser/for.v index d0f56c3efe..9da2058a58 100644 --- a/vlib/v/parser/for.v +++ b/vlib/v/parser/for.v @@ -19,13 +19,15 @@ fn (mut p Parser) for_stmt() ast.Stmt { // Infinite loop if p.tok.kind == .lcbr { p.inside_for = false - stmts := p.parse_block() - p.close_scope() - return ast.ForStmt{ + stmts := p.parse_block_no_scope(false) + for_stmt := ast.ForStmt{ stmts: stmts pos: pos is_inf: true + scope: p.scope } + p.close_scope() + return for_stmt } else if p.peek_tok.kind in [.decl_assign, .assign, .semicolon] || p.tok.kind == .semicolon { // `for i := 0; i < 10; i++ {` if p.tok.kind == .key_mut { @@ -60,9 +62,8 @@ fn (mut p Parser) for_stmt() ast.Stmt { has_inc = true } p.inside_for = false - stmts := p.parse_block() - p.close_scope() - return ast.ForCStmt{ + stmts := p.parse_block_no_scope(false) + for_c_stmt := ast.ForCStmt{ stmts: stmts has_init: has_init has_cond: has_cond @@ -71,7 +72,10 @@ fn (mut p Parser) for_stmt() ast.Stmt { cond: cond inc: inc pos: pos + scope: p.scope } + p.close_scope() + return for_c_stmt } else if p.peek_tok.kind in [.key_in, .comma] || (p.tok.kind == .key_mut && p.peek_tok2.kind in [.key_in, .comma]) { // `for i in vals`, `for i in start .. end` @@ -146,10 +150,9 @@ fn (mut p Parser) for_stmt() ast.Stmt { }) } p.inside_for = false - stmts := p.parse_block() + stmts := p.parse_block_no_scope(false) // println('nr stmts=$stmts.len') - p.close_scope() - return ast.ForInStmt{ + for_in_stmt := ast.ForInStmt{ stmts: stmts cond: cond key_var: key_var_name @@ -158,16 +161,21 @@ fn (mut p Parser) for_stmt() ast.Stmt { is_range: is_range pos: pos val_is_mut: val_is_mut + scope: p.scope } + p.close_scope() + return for_in_stmt } // `for cond {` cond := p.expr(0) p.inside_for = false - stmts := p.parse_block() - p.close_scope() - return ast.ForStmt{ + stmts := p.parse_block_no_scope(false) + for_stmt := ast.ForStmt{ cond: cond stmts: stmts pos: pos + scope: p.scope } + p.close_scope() + return for_stmt } diff --git a/vlib/v/parser/if_match.v b/vlib/v/parser/if_match.v index eeed18da41..08c381ed20 100644 --- a/vlib/v/parser/if_match.v +++ b/vlib/v/parser/if_match.v @@ -43,9 +43,9 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr { p.inside_if = false end_pos := p.prev_tok.position() body_pos := p.tok.position() + p.open_scope() // only declare `err` if previous branch was an `if` guard if prev_guard { - p.open_scope() p.scope.register(ast.Var{ name: 'errcode' typ: table.int_type @@ -60,18 +60,13 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr { }) } branches << ast.IfBranch{ - stmts: if prev_guard { - p.parse_block_no_scope(false) - } else { - p.parse_block() - } + stmts: p.parse_block_no_scope(false) pos: start_pos.extend(end_pos) body_pos: body_pos.extend(p.tok.position()) comments: comments + scope: p.scope } - if prev_guard { - p.close_scope() - } + p.close_scope() comments = [] break } @@ -116,16 +111,19 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr { end_pos := p.prev_tok.position() body_pos := p.tok.position() p.inside_if = false - stmts := p.parse_block() - if is_guard { - p.close_scope() - } + p.open_scope() + stmts := p.parse_block_no_scope(false) branches << ast.IfBranch{ cond: cond stmts: stmts pos: start_pos.extend(end_pos) body_pos: body_pos.extend(p.prev_tok.position()) comments: comments + scope: p.scope + } + p.close_scope() + if is_guard { + p.close_scope() } comments = p.eat_comments() if is_comptime { @@ -226,6 +224,7 @@ fn (mut p Parser) match_expr() ast.MatchExpr { // p.warn('match block') p.inside_match_body = true stmts := p.parse_block_no_scope(false) + branch_scope := p.scope p.close_scope() p.inside_match_body = false pos := token.Position{ @@ -242,6 +241,7 @@ fn (mut p Parser) match_expr() ast.MatchExpr { comments: comments is_else: is_else post_comments: post_comments + scope: branch_scope } if p.tok.kind == .rcbr || (is_else && no_lcbr) { break diff --git a/vlib/v/parser/lock.v b/vlib/v/parser/lock.v index 2889456119..3273265a1c 100644 --- a/vlib/v/parser/lock.v +++ b/vlib/v/parser/lock.v @@ -18,6 +18,7 @@ fn (mut p Parser) lock_expr() ast.LockExpr { name: p.tok.lit is_mut: true info: ast.IdentVar{} + scope: p.scope } p.next() if p.tok.kind == .lcbr { diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index bf3d4d0b32..632771ae99 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -521,6 +521,7 @@ pub fn (mut p Parser) top_stmt() ast.Stmt { stmts: stmts file: p.file_name return_type: table.void_type + scope: p.scope } } else if p.pref.is_fmt { return p.stmt(false) @@ -995,6 +996,7 @@ pub fn (mut p Parser) parse_ident(language table.Language) ast.Ident { is_mut: false is_static: false } + scope: p.scope } } if p.inside_match_body && name == 'it' { @@ -1017,12 +1019,17 @@ pub fn (mut p Parser) parse_ident(language table.Language) ast.Ident { is_static: is_static share: table.sharetype_from_flags(is_shared, is_atomic) } + scope: p.scope } } else { p.error('unexpected token `$p.tok.lit`') - return ast.Ident{} + return ast.Ident{ + scope: 0 + } + } + return ast.Ident{ + scope: 0 } - return ast.Ident{} } pub fn (mut p Parser) name_expr() ast.Expr { @@ -1230,9 +1237,11 @@ pub fn (mut p Parser) name_expr() ast.Expr { return ast.SelectorExpr{ expr: ast.Ident{ name: name + scope: p.scope } field_name: field pos: pos + scope: p.scope } } // `Color.green` @@ -1413,6 +1422,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { kind: or_kind pos: or_pos } + scope: p.scope } if is_filter || field_name == 'sort' { p.close_scope() @@ -1436,6 +1446,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { pos: name_pos is_mut: is_mut mut_pos: mut_pos + scope: p.scope } mut node := ast.Expr{} node = sel_expr @@ -2095,7 +2106,9 @@ fn (mut p Parser) assoc() ast.Assoc { pos := p.tok.position() mut v := p.scope.find_var(var_name) or { p.error('unknown variable `$var_name`') - return ast.Assoc{} + return ast.Assoc{ + scope: 0 + } } v.is_used = true // println('assoc var $name typ=$var.typ') @@ -2119,6 +2132,7 @@ fn (mut p Parser) assoc() ast.Assoc { fields: fields exprs: vals pos: pos + scope: p.scope } } diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index 6baded79c1..1bc27881cc 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -235,6 +235,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { left: node args: args pos: pos + scope: p.scope } } return node diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index 4c0ae4d636..3b408da05c 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -439,6 +439,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { return_type: table.void_type is_pub: true pos: method_start_pos.extend(p.prev_tok.position()) + scope: p.scope } if p.tok.kind.is_start_of_type() && p.tok.line_nr == line_nr { method.return_type = p.parse_type()