diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 2c44feaaca..f6ed466772 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3104,37 +3104,16 @@ fn (mut c Checker) stmt(node ast.Stmt) { // c.expected_type = table.void_type match mut node { ast.AssertStmt { - cur_exp_typ := c.expected_type - assert_type := c.expr(node.expr) - if assert_type != table.bool_type_idx { - atype_name := c.table.get_type_symbol(assert_type).name - c.error('assert can be used only with `bool` expressions, but found `$atype_name` instead', - node.pos) - } - c.expected_type = cur_exp_typ + c.assert_stmt(node) } ast.AssignStmt { c.assign_stmt(mut node) } ast.Block { - if node.is_unsafe { - assert !c.inside_unsafe - c.inside_unsafe = true - c.stmts(node.stmts) - c.inside_unsafe = false - } else { - c.stmts(node.stmts) - } + c.block(node) } ast.BranchStmt { - if c.in_for_count == 0 { - c.error('$node.kind.str() statement not within a loop', node.pos) - } - if node.label.len > 0 { - if node.label != c.loop_label { - c.error('invalid label name `$node.label`', node.pos) - } - } + c.branch_stmt(node) } ast.CompFor { // node.typ = c.expr(node.expr) @@ -3166,147 +3145,19 @@ fn (mut c Checker) stmt(node ast.Stmt) { c.fn_decl(mut node) } ast.ForCStmt { - c.in_for_count++ - prev_loop_label := c.loop_label - c.stmt(node.init) - c.expr(node.cond) - c.stmt(node.inc) - c.check_loop_label(node.label, node.pos) - c.stmts(node.stmts) - c.loop_label = prev_loop_label - c.in_for_count-- + c.for_c_stmt(node) } ast.ForInStmt { - c.in_for_count++ - prev_loop_label := c.loop_label - typ := c.expr(node.cond) - typ_idx := typ.idx() - if node.key_var.len > 0 && node.key_var != '_' { - c.check_valid_snake_case(node.key_var, 'variable name', node.pos) - } - if node.val_var.len > 0 && node.val_var != '_' { - c.check_valid_snake_case(node.val_var, 'variable name', node.pos) - } - if node.is_range { - high_type := c.expr(node.high) - high_type_idx := high_type.idx() - if typ_idx in table.integer_type_idxs && high_type_idx !in table.integer_type_idxs { - c.error('range types do not match', node.cond.position()) - } else if typ_idx in table.float_type_idxs || high_type_idx in table.float_type_idxs { - c.error('range type can not be float', node.cond.position()) - } else if typ_idx == table.bool_type_idx || high_type_idx == table.bool_type_idx { - c.error('range type can not be bool', node.cond.position()) - } else if typ_idx == table.string_type_idx || high_type_idx == table.string_type_idx { - c.error('range type can not be string', node.cond.position()) - } - } else { - sym := c.table.get_type_symbol(typ) - if sym.kind == .struct_ { - // iterators - next_fn := sym.find_method('next') or { - c.error('a struct must have a `next()` method to be an iterator', - node.cond.position()) - return - } - if !next_fn.return_type.has_flag(.optional) { - c.error('iterator method `next()` must return an optional', node.cond.position()) - } - // the receiver - if next_fn.params.len != 1 { - c.error('iterator method `next()` must have 0 parameters', node.cond.position()) - } - val_type := next_fn.return_type.clear_flag(.optional) - node.cond_type = typ - node.kind = sym.kind - node.val_type = val_type - node.scope.update_var_type(node.val_var, val_type) - } else { - 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' + - 'use `_` if you do not need the variable', node.pos) - } - if node.key_var.len > 0 { - key_type := match sym.kind { - .map { sym.map_info().key_type } - else { table.int_type } - } - node.key_type = 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) { - if typ != table.void_type { - c.error('for in: cannot index `${c.table.type_to_str(typ)}`', - node.cond.position()) - } - } - if node.val_is_mut { - value_type = value_type.to_ptr() - match node.cond { - ast.Ident { - if node.cond.obj is ast.Var { - obj := node.cond.obj as ast.Var - if !obj.is_mut { - c.error('`$obj.name` is immutable, it cannot be changed', - node.cond.pos) - } - } - } - ast.ArrayInit { - c.error('array literal is immutable, it cannot be changed', - node.cond.pos) - } - ast.MapInit { - c.error('map literal is immutable, it cannot be changed', - node.cond.pos) - } - else {} - } - } - node.cond_type = typ - node.kind = sym.kind - node.val_type = value_type - node.scope.update_var_type(node.val_var, value_type) - } - } - c.check_loop_label(node.label, node.pos) - if node.val_is_mut { - c.for_in_mut_val_name = node.val_var - } - c.stmts(node.stmts) - if node.val_is_mut { - c.for_in_mut_val_name = '' - } - c.loop_label = prev_loop_label - c.in_for_count-- + c.for_in_stmt(mut node) } ast.ForStmt { c.for_stmt(mut node) } ast.GlobalDecl { - for 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) - } - c.global_names << field.name - } + c.global_decl(node) } ast.GoStmt { - c.call_expr(mut node.call_expr) - // Make sure there are no mutable arguments - for arg in node.call_expr.args { - if arg.is_mut && !arg.typ.is_ptr() { - c.error('function in `go` statement cannot contain mutable non-reference arguments', - arg.expr.position()) - } - } - if node.call_expr.is_method && node.call_expr.receiver_type.is_ptr() - && !node.call_expr.left_type.is_ptr() { - c.error('method in `go` statement cannot have non-reference mutable receiver', - node.call_expr.left.position()) - } + c.go_stmt(mut node) } ast.GotoLabel {} ast.GotoStmt { @@ -3346,6 +3197,217 @@ fn (mut c Checker) stmt(node ast.Stmt) { } } +fn (mut c Checker) assert_stmt(node ast.AssertStmt) { + cur_exp_typ := c.expected_type + assert_type := c.expr(node.expr) + if assert_type != table.bool_type_idx { + atype_name := c.table.get_type_symbol(assert_type).name + c.error('assert can be used only with `bool` expressions, but found `$atype_name` instead', + node.pos) + } + c.expected_type = cur_exp_typ +} + +fn (mut c Checker) block(node ast.Block) { + if node.is_unsafe { + assert !c.inside_unsafe + c.inside_unsafe = true + c.stmts(node.stmts) + c.inside_unsafe = false + } else { + c.stmts(node.stmts) + } +} + +fn (mut c Checker) branch_stmt(node ast.BranchStmt) { + if c.in_for_count == 0 { + c.error('$node.kind.str() statement not within a loop', node.pos) + } + if node.label.len > 0 { + if node.label != c.loop_label { + c.error('invalid label name `$node.label`', node.pos) + } + } +} + +fn (mut c Checker) for_c_stmt(node ast.ForCStmt) { + c.in_for_count++ + prev_loop_label := c.loop_label + c.stmt(node.init) + c.expr(node.cond) + c.stmt(node.inc) + c.check_loop_label(node.label, node.pos) + c.stmts(node.stmts) + c.loop_label = prev_loop_label + c.in_for_count-- +} + +fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) { + c.in_for_count++ + prev_loop_label := c.loop_label + typ := c.expr(node.cond) + typ_idx := typ.idx() + if node.key_var.len > 0 && node.key_var != '_' { + c.check_valid_snake_case(node.key_var, 'variable name', node.pos) + } + if node.val_var.len > 0 && node.val_var != '_' { + c.check_valid_snake_case(node.val_var, 'variable name', node.pos) + } + if node.is_range { + high_type := c.expr(node.high) + high_type_idx := high_type.idx() + if typ_idx in table.integer_type_idxs && high_type_idx !in table.integer_type_idxs { + c.error('range types do not match', node.cond.position()) + } else if typ_idx in table.float_type_idxs || high_type_idx in table.float_type_idxs { + c.error('range type can not be float', node.cond.position()) + } else if typ_idx == table.bool_type_idx || high_type_idx == table.bool_type_idx { + c.error('range type can not be bool', node.cond.position()) + } else if typ_idx == table.string_type_idx || high_type_idx == table.string_type_idx { + c.error('range type can not be string', node.cond.position()) + } + } else { + sym := c.table.get_type_symbol(typ) + if sym.kind == .struct_ { + // iterators + next_fn := sym.find_method('next') or { + c.error('a struct must have a `next()` method to be an iterator', node.cond.position()) + return + } + if !next_fn.return_type.has_flag(.optional) { + c.error('iterator method `next()` must return an optional', node.cond.position()) + } + // the receiver + if next_fn.params.len != 1 { + c.error('iterator method `next()` must have 0 parameters', node.cond.position()) + } + val_type := next_fn.return_type.clear_flag(.optional) + node.cond_type = typ + node.kind = sym.kind + node.val_type = val_type + node.scope.update_var_type(node.val_var, val_type) + } else { + 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' + + 'use `_` if you do not need the variable', node.pos) + } + if node.key_var.len > 0 { + key_type := match sym.kind { + .map { sym.map_info().key_type } + else { table.int_type } + } + node.key_type = 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) { + if typ != table.void_type { + c.error('for in: cannot index `${c.table.type_to_str(typ)}`', node.cond.position()) + } + } + if node.val_is_mut { + value_type = value_type.to_ptr() + match node.cond { + ast.Ident { + if node.cond.obj is ast.Var { + obj := node.cond.obj as ast.Var + if !obj.is_mut { + c.error('`$obj.name` is immutable, it cannot be changed', + node.cond.pos) + } + } + } + ast.ArrayInit { + c.error('array literal is immutable, it cannot be changed', node.cond.pos) + } + ast.MapInit { + c.error('map literal is immutable, it cannot be changed', node.cond.pos) + } + else {} + } + } + node.cond_type = typ + node.kind = sym.kind + node.val_type = value_type + node.scope.update_var_type(node.val_var, value_type) + } + } + c.check_loop_label(node.label, node.pos) + if node.val_is_mut { + c.for_in_mut_val_name = node.val_var + } + c.stmts(node.stmts) + if node.val_is_mut { + c.for_in_mut_val_name = '' + } + c.loop_label = prev_loop_label + c.in_for_count-- +} + +fn (mut c Checker) for_stmt(mut node ast.ForStmt) { + c.in_for_count++ + prev_loop_label := c.loop_label + c.expected_type = table.bool_type + typ := c.expr(node.cond) + if !node.is_inf && typ.idx() != table.bool_type_idx && !c.pref.translated { + c.error('non-bool used as for condition', node.pos) + } + if node.cond is ast.InfixExpr { + infix := node.cond + if infix.op == .key_is { + if (infix.left is ast.Ident || infix.left is ast.SelectorExpr) + && infix.right is ast.Type { + right_expr := infix.right as ast.Type + is_variable := if mut infix.left is ast.Ident { + infix.left.kind == .variable + } else { + true + } + left_type := c.expr(infix.left) + left_sym := c.table.get_type_symbol(left_type) + if is_variable { + if left_sym.kind == .sum_type { + c.smartcast_sumtype(infix.left, infix.left_type, right_expr.typ, mut + node.scope) + } + } + } + } + } + // TODO: update loop var type + // how does this work currenly? + c.check_loop_label(node.label, node.pos) + c.stmts(node.stmts) + c.loop_label = prev_loop_label + c.in_for_count-- +} + +fn (mut c Checker) global_decl(node ast.GlobalDecl) { + for 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) + } + c.global_names << field.name + } +} + +fn (mut c Checker) go_stmt(mut node ast.GoStmt) { + c.call_expr(mut node.call_expr) + // Make sure there are no mutable arguments + for arg in node.call_expr.args { + if arg.is_mut && !arg.typ.is_ptr() { + c.error('function in `go` statement cannot contain mutable non-reference arguments', + arg.expr.position()) + } + } + if node.call_expr.is_method && node.call_expr.receiver_type.is_ptr() + && !node.call_expr.left_type.is_ptr() { + c.error('method in `go` statement cannot have non-reference mutable receiver', + node.call_expr.left.position()) + } +} + fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { if c.skip_flags { return @@ -4659,44 +4721,6 @@ pub fn (mut c Checker) unsafe_expr(mut node ast.UnsafeExpr) table.Type { return t } -fn (mut c Checker) for_stmt(mut node ast.ForStmt) { - c.in_for_count++ - prev_loop_label := c.loop_label - c.expected_type = table.bool_type - typ := c.expr(node.cond) - if !node.is_inf && typ.idx() != table.bool_type_idx && !c.pref.translated { - c.error('non-bool used as for condition', node.pos) - } - if node.cond is ast.InfixExpr { - infix := node.cond - if infix.op == .key_is { - if (infix.left is ast.Ident || infix.left is ast.SelectorExpr) - && infix.right is ast.Type { - right_expr := infix.right as ast.Type - is_variable := if mut infix.left is ast.Ident { - infix.left.kind == .variable - } else { - true - } - left_type := c.expr(infix.left) - left_sym := c.table.get_type_symbol(left_type) - if is_variable { - if left_sym.kind == .sum_type { - c.smartcast_sumtype(infix.left, infix.left_type, right_expr.typ, mut - node.scope) - } - } - } - } - } - // TODO: update loop var type - // how does this work currenly? - c.check_loop_label(node.label, node.pos) - c.stmts(node.stmts) - c.loop_label = prev_loop_label - c.in_for_count-- -} - pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { if_kind := if node.is_comptime { '\$if' } else { 'if' } expr_required := c.expected_type != table.void_type