From f7feb634d274c9165757c1ad7631553f547d65c2 Mon Sep 17 00:00:00 2001 From: yuyi Date: Sun, 13 Mar 2022 22:33:50 +0800 Subject: [PATCH] checker: fix error for if mut with immutable variable (#13728) --- vlib/v/ast/ast.v | 4 ++-- vlib/v/checker/assign.v | 20 +++++++++---------- vlib/v/checker/checker.v | 5 +++-- vlib/v/checker/for.v | 2 +- vlib/v/checker/if.v | 7 ++++++- vlib/v/checker/match.v | 2 +- .../tests/if_mut_with_immutable_var_err.out | 14 +++++++++++++ .../tests/if_mut_with_immutable_var_err.vv | 11 ++++++++++ vlib/v/fmt/fmt.v | 2 +- vlib/v/gen/c/assign.v | 7 ++++--- vlib/v/gen/c/cgen.v | 4 ++-- vlib/v/gen/c/fn.v | 2 +- vlib/v/gen/c/infix_expr.v | 4 ++-- vlib/v/gen/c/str_intp.v | 2 +- vlib/v/parser/comptime.v | 4 ++-- 15 files changed, 61 insertions(+), 29 deletions(-) create mode 100644 vlib/v/checker/tests/if_mut_with_immutable_var_err.out create mode 100644 vlib/v/checker/tests/if_mut_with_immutable_var_err.vv diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 21e62e47a5..3963072860 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -624,7 +624,6 @@ pub struct Stmt { pub struct Var { pub: name string - expr Expr share ShareType is_mut bool is_autofree_tmp bool @@ -632,6 +631,7 @@ pub: is_auto_deref bool is_inherited bool pub mut: + expr Expr typ Type orig_type Type // original sumtype type; 0 if it's not a sumtype smartcasts []Type // nested sum types require nested smart casting, for that a list of types is needed @@ -997,7 +997,6 @@ pub struct ForInStmt { pub: key_var string val_var string - cond Expr is_range bool high Expr // `10` in `for i in 0..10 {` stmts []Stmt @@ -1005,6 +1004,7 @@ pub: val_is_mut bool // `for mut val in vals {` means that modifying `val` will modify the array // and the array cannot be indexed inside the loop pub mut: + cond Expr key_type Type val_type Type cond_type Type diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index f7ac0f3ac7..e418d724a4 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -16,7 +16,7 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { node.left_types = [] mut right_len := node.right.len mut right_type0 := ast.void_type - for i, right in node.right { + for i, mut right in node.right { if right in [ast.CallExpr, ast.IfExpr, ast.LockExpr, ast.MatchExpr] { if right in [ast.IfExpr, ast.MatchExpr] && node.left.len == node.right.len && !is_decl && node.left[i] in [ast.Ident, ast.SelectorExpr] && !node.left[i].is_blank_ident() { @@ -41,18 +41,18 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { right_len = 0 } } - if right is ast.InfixExpr { + if mut right is ast.InfixExpr { if right.op == .arrow { c.error('cannot use `<-` on the right-hand side of an assignment, as it does not return any values', right.pos) } } - if right is ast.Ident { + if mut right is ast.Ident { if right.is_mut { c.error('unexpected `mut` on right-hand side of assignment', right.mut_pos) } } - if right is ast.None { + if mut right is ast.None { c.error('you can not assign a `none` value to a variable', right.pos) } } @@ -112,9 +112,9 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { node.right_types << c.check_expr_opt_call(node.right[i], right_type) } } - right := if i < node.right.len { node.right[i] } else { node.right[0] } + mut right := if i < node.right.len { node.right[i] } else { node.right[0] } mut right_type := node.right_types[i] - if right is ast.Ident { + if mut right is ast.Ident { right_sym := c.table.sym(right_type) if right_sym.info is ast.Struct { if right_sym.info.generic_types.len > 0 { @@ -128,12 +128,12 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { } if is_decl { // check generic struct init and return unwrap generic struct type - if right is ast.StructInit { + if mut right is ast.StructInit { if right.typ.has_flag(.generic) { c.expr(right) right_type = right.typ } - } else if right is ast.PrefixExpr { + } else if mut right is ast.PrefixExpr { if right.op == .amp && right.right is ast.StructInit { right_type = c.expr(right) } @@ -144,7 +144,7 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { left_type = ast.mktyp(right_type) } if left_type == ast.int_type { - if right is ast.IntegerLiteral { + if mut right is ast.IntegerLiteral { mut is_large := right.val.len > 13 if !is_large && right.val.len > 8 { val := right.val.i64() @@ -244,7 +244,7 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { } } if left_type in ast.unsigned_integer_type_idxs { - if right is ast.IntegerLiteral { + if mut right is ast.IntegerLiteral { if right.val[0] == `-` { c.error('Cannot assign negative value to unsigned integer type', right.pos) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 228d2306f8..82bd9a2684 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3280,10 +3280,11 @@ pub fn (mut c Checker) concat_expr(mut node ast.ConcatExpr) ast.Type { } // smartcast takes the expression with the current type which should be smartcasted to the target type in the given scope -fn (mut c Checker) smartcast(expr ast.Expr, cur_type ast.Type, to_type_ ast.Type, mut scope ast.Scope) { +fn (mut c Checker) smartcast(expr_ ast.Expr, cur_type ast.Type, to_type_ ast.Type, mut scope ast.Scope) { sym := c.table.sym(cur_type) to_type := if sym.kind == .interface_ { to_type_.ref() } else { to_type_ } - match expr { + mut expr := unsafe { expr_ } + match mut expr { ast.SelectorExpr { mut is_mut := false mut smartcasts := []ast.Type{} diff --git a/vlib/v/checker/for.v b/vlib/v/checker/for.v index b4209e23ea..94bbea266a 100644 --- a/vlib/v/checker/for.v +++ b/vlib/v/checker/for.v @@ -102,7 +102,7 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) { } if node.val_is_mut { value_type = value_type.ref() - match node.cond { + match mut node.cond { ast.Ident { if mut node.cond.obj is ast.Var { if !node.cond.obj.is_mut { diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v index ee0f45f147..4660ac3287 100644 --- a/vlib/v/checker/if.v +++ b/vlib/v/checker/if.v @@ -309,12 +309,17 @@ fn (mut c Checker) smartcast_if_conds(node ast.Expr, mut scope ast.Scope) { c.error('cannot use type `$expect_str` as type `$expr_str`', node.pos) } if node.left in [ast.Ident, ast.SelectorExpr] && node.right is ast.TypeNode { - is_variable := if mut node.left is ast.Ident { + is_variable := if node.left is ast.Ident { node.left.kind == .variable } else { true } if is_variable { + if (node.left is ast.Ident && (node.left as ast.Ident).is_mut) + || (node.left is ast.SelectorExpr + && (node.left as ast.SelectorExpr).is_mut) { + c.fail_if_immutable(node.left) + } if left_sym.kind in [.interface_, .sum_type] { c.smartcast(node.left, node.left_type, right_type, mut scope) } diff --git a/vlib/v/checker/match.v b/vlib/v/checker/match.v index b6371783ff..81e9ef937f 100644 --- a/vlib/v/checker/match.v +++ b/vlib/v/checker/match.v @@ -237,7 +237,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym } } } - } else if mut cond_type_sym.info is ast.SumType { + } else if cond_type_sym.info is ast.SumType { if expr_type !in cond_type_sym.info.variants { expr_str := c.table.type_to_str(expr_type) expect_str := c.table.type_to_str(node.cond_type) diff --git a/vlib/v/checker/tests/if_mut_with_immutable_var_err.out b/vlib/v/checker/tests/if_mut_with_immutable_var_err.out new file mode 100644 index 0000000000..aca242045e --- /dev/null +++ b/vlib/v/checker/tests/if_mut_with_immutable_var_err.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/if_mut_with_immutable_var_err.vv:5:9: error: `i` is immutable, declare it with `mut` to make it mutable + 3 | fn main() { + 4 | i := Int(0) + 5 | if mut i is int { + | ^ + 6 | i = 1 + 7 | } else if mut i is byte { +vlib/v/checker/tests/if_mut_with_immutable_var_err.vv:7:16: error: `i` is immutable, declare it with `mut` to make it mutable + 5 | if mut i is int { + 6 | i = 1 + 7 | } else if mut i is byte { + | ^ + 8 | i = 2 + 9 | } diff --git a/vlib/v/checker/tests/if_mut_with_immutable_var_err.vv b/vlib/v/checker/tests/if_mut_with_immutable_var_err.vv new file mode 100644 index 0000000000..4c84c91420 --- /dev/null +++ b/vlib/v/checker/tests/if_mut_with_immutable_var_err.vv @@ -0,0 +1,11 @@ +type Int = byte | int + +fn main() { + i := Int(0) + if mut i is int { + i = 1 + } else if mut i is byte { + i = 2 + } + println(i) +} diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index cee30af7df..1a25649238 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -1803,7 +1803,7 @@ pub fn (mut f Fmt) enum_val(node ast.EnumVal) { } pub fn (mut f Fmt) ident(node ast.Ident) { - if mut node.info is ast.IdentVar { + if node.info is ast.IdentVar { if node.info.is_mut { f.write(node.info.share.str() + ' ') } diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index 6f401871a8..33ac7403de 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -7,7 +7,8 @@ import v.ast import v.util import v.token -fn (mut g Gen) gen_assign_stmt(node ast.AssignStmt) { +fn (mut g Gen) gen_assign_stmt(node_ ast.AssignStmt) { + mut node := unsafe { node_ } if node.is_static { g.write('static ') } @@ -88,7 +89,7 @@ fn (mut g Gen) gen_assign_stmt(node ast.AssignStmt) { g.checker_bug('node.left_types.len < node.left.len', node.pos) } - for i, left in node.left { + for i, mut left in node.left { mut is_auto_heap := false mut var_type := node.left_types[i] mut val_type := node.right_types[i] @@ -163,7 +164,7 @@ fn (mut g Gen) gen_assign_stmt(node ast.AssignStmt) { g.expr(left) g.is_assign_lhs = false g.is_arraymap_set = false - if left is ast.IndexExpr { + if mut left is ast.IndexExpr { sym := g.table.sym(left.left_type) if sym.kind in [.map, .array] { g.expr(val) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index dd269e29f4..d576ede838 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -3235,7 +3235,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { dot := if field.typ.is_ptr() { '->' } else { '.' } sum_type_deref_field += ')$dot' } - if mut cast_sym.info is ast.Aggregate { + if cast_sym.info is ast.Aggregate { agg_sym := g.table.sym(cast_sym.info.types[g.aggregate_type_idx]) sum_type_deref_field += '_$agg_sym.cname' } else { @@ -3695,7 +3695,7 @@ fn (mut g Gen) ident(node ast.Ident) { } } dot := if is_ptr || is_auto_heap { '->' } else { '.' } - if mut cast_sym.info is ast.Aggregate { + if cast_sym.info is ast.Aggregate { sym := g.table.sym(cast_sym.info.types[g.aggregate_type_idx]) g.write('${dot}_$sym.cname') } else { diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 2e3c8ef03d..445da57c66 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1342,7 +1342,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { } } dot := if is_ptr { '->' } else { '.' } - if mut cast_sym.info is ast.Aggregate { + if cast_sym.info is ast.Aggregate { sym := g.table.sym(cast_sym.info.types[g.aggregate_type_idx]) g.write('${dot}_$sym.cname') } else { diff --git a/vlib/v/gen/c/infix_expr.v b/vlib/v/gen/c/infix_expr.v index d844cd277a..af061cf77d 100644 --- a/vlib/v/gen/c/infix_expr.v +++ b/vlib/v/gen/c/infix_expr.v @@ -373,7 +373,7 @@ fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) { } if right.unaliased_sym.kind == .array { if left.sym.kind in [.sum_type, .interface_] { - if mut node.right is ast.ArrayInit { + if node.right is ast.ArrayInit { if node.right.exprs.len > 0 { mut infix_exprs := []ast.InfixExpr{} for i in 0 .. node.right.exprs.len { @@ -392,7 +392,7 @@ fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) { } } } - if mut node.right is ast.ArrayInit { + if node.right is ast.ArrayInit { if node.right.exprs.len > 0 { // `a in [1,2,3]` optimization => `a == 1 || a == 2 || a == 3` // avoids an allocation diff --git a/vlib/v/gen/c/str_intp.v b/vlib/v/gen/c/str_intp.v index 9817e4abdb..6890fb897c 100644 --- a/vlib/v/gen/c/str_intp.v +++ b/vlib/v/gen/c/str_intp.v @@ -200,7 +200,7 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { // fn (mut g Gen) str_int2(node ast.StringInterLiteral) { if g.inside_comptime_for_field { mut node_ := unsafe { node } - for i, expr in node_.exprs { + for i, mut expr in node_.exprs { if mut expr is ast.Ident { if mut expr.obj is ast.Var { node_.expr_types[i] = expr.obj.typ diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index 5a6f5858f8..6d07664354 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -259,10 +259,10 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { mut file := parse_comptime(v_code, p.table, p.pref, scope) file.path = tmpl_path // copy vars from current fn scope into vweb_tmpl scope - for stmt in file.stmts { + for mut stmt in file.stmts { if mut stmt is ast.FnDecl { if stmt.name == 'main.vweb_tmpl_$tmp_fn_name' { - for _, obj in p.scope.objects { + for _, mut obj in p.scope.objects { if mut obj is ast.Var { stmt.scope.register(ast.Var{ ...obj