diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index a7cd74cacb..56bfafddce 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -337,6 +337,7 @@ pub mut: next_comments []Comment // coments that are one line after the decl; used for InterfaceDecl source_file &File = 0 scope &Scope + label_names []string } pub struct GenericParam { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index f4f16b6bc7..197ff7a336 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3273,6 +3273,9 @@ fn (mut c Checker) stmt(node ast.Stmt) { } ast.GotoLabel {} ast.GotoStmt { + if node.name !in c.cur_fn.label_names { + c.error('unknown label `$node.name`', node.pos) + } // TODO: check label doesn't bypass variable declarations } ast.HashStmt { @@ -5281,7 +5284,6 @@ pub fn (mut c Checker) offset_of(node ast.OffsetOf) table.Type { c.error('first argument of __offsetof must be struct', node.pos) return table.u32_type } - if !c.table.struct_has_field(node.struct_type, node.field) { c.error('struct `$sym.name` has no field called `$node.field`', node.pos) } @@ -5540,7 +5542,6 @@ fn (mut c Checker) fetch_and_verify_orm_fields(info table.Struct, pos token.Posi fn (mut c Checker) post_process_generic_fns() { // Loop thru each generic function concrete type. // Check each specific fn instantiation. - for i in 0 .. c.file.generic_fns.len { if c.table.fn_gen_types.len == 0 { // no concrete types, so just skip: @@ -5689,7 +5690,6 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { } c.fn_scope = node.scope c.stmts(node.stmts) - if c.fn_mut_arg_names.len > 0 { c.fn_mut_arg_names.clear() } diff --git a/vlib/v/checker/tests/goto_label.out b/vlib/v/checker/tests/goto_label.out new file mode 100644 index 0000000000..fd97bf7e8f --- /dev/null +++ b/vlib/v/checker/tests/goto_label.out @@ -0,0 +1,49 @@ +vlib/v/checker/tests/goto_label.vv:5:7: error: unknown label `a1` + 3 | goto f2 + 4 | f1: + 5 | goto a1 + | ~~ + 6 | _ = fn(){ + 7 | goto f1 +vlib/v/checker/tests/goto_label.vv:7:8: error: unknown label `f1` + 5 | goto a1 + 6 | _ = fn(){ + 7 | goto f1 + | ~~ + 8 | goto f2 + 9 | goto a1 +vlib/v/checker/tests/goto_label.vv:8:8: error: unknown label `f2` + 6 | _ = fn(){ + 7 | goto f1 + 8 | goto f2 + | ~~ + 9 | goto a1 + 10 | a1: +vlib/v/checker/tests/goto_label.vv:14:7: error: unknown label `a1` + 12 | } + 13 | f2: + 14 | goto a1 + | ~~ + 15 | goto f1 // back + 16 | goto f2 +vlib/v/checker/tests/goto_label.vv:22:7: error: unknown label `f1` + 20 | goto g1 // forward + 21 | g1: + 22 | goto f1 + | ~~ + 23 | goto a1 + 24 | goto g1 // back +vlib/v/checker/tests/goto_label.vv:23:7: error: unknown label `a1` + 21 | g1: + 22 | goto f1 + 23 | goto a1 + | ~~ + 24 | goto g1 // back + 25 | goto undefined +vlib/v/checker/tests/goto_label.vv:25:7: error: unknown label `undefined` + 23 | goto a1 + 24 | goto g1 // back + 25 | goto undefined + | ~~~~~~~~~ + 26 | } + 27 | diff --git a/vlib/v/checker/tests/goto_label.vv b/vlib/v/checker/tests/goto_label.vv new file mode 100644 index 0000000000..d25d5d97e8 --- /dev/null +++ b/vlib/v/checker/tests/goto_label.vv @@ -0,0 +1,32 @@ +fn f() { + goto f1 // forward + goto f2 + f1: + goto a1 + _ = fn(){ + goto f1 + goto f2 + goto a1 + a1: + goto a1 + } + f2: + goto a1 + goto f1 // back + goto f2 +} + +fn g() { + goto g1 // forward + g1: + goto f1 + goto a1 + goto g1 // back + goto undefined +} + +// implicit main +goto m1 +m1: +goto m1 + diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 2a0927099a..8b7421a712 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -427,7 +427,9 @@ fn (mut p Parser) fn_decl() ast.FnDecl { is_builtin: p.builtin_mod || p.mod in util.builtin_module_parts attrs: p.attrs scope: p.scope + label_names: p.label_names } + p.label_names = [] p.close_scope() return fn_decl } @@ -514,8 +516,13 @@ fn (mut p Parser) anon_fn() ast.AnonFn { p.error_with_pos('unexpected `$p.tok.kind` after anonymous function signature, expecting `{`', p.tok.position()) } + mut label_names := []string{} if p.tok.kind == .lcbr { + tmp := p.label_names + p.label_names = [] stmts = p.parse_block_no_scope(false) + label_names = p.label_names + p.label_names = tmp } p.close_scope() mut func := table.Fn{ @@ -542,6 +549,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn { pos: pos.extend(p.prev_tok.position()) file: p.file_name scope: p.scope + label_names: label_names } typ: typ } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index e6dc90c73d..73bebe724e 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -69,6 +69,7 @@ mut: warnings []errors.Warning vet_errors []vet.Error cur_fn_name string + label_names []string in_generic_params bool // indicates if parsing between `<` and `>` of a method/function name_error bool // indicates if the token is not a name or the name is on another line } @@ -552,6 +553,7 @@ pub fn (mut p Parser) top_stmt() ast.Stmt { file: p.file_name return_type: table.void_type scope: p.scope + label_names: p.label_names } } else if p.pref.is_fmt { return p.stmt(false) @@ -656,6 +658,10 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt { // `label:` spos := p.tok.position() name := p.check_name() + if name in p.label_names { + p.error_with_pos('duplicate label `$name`', spos) + } + p.label_names << name p.next() if p.tok.kind == .key_for { mut stmt := p.stmt(is_top_level)