checker: fix error for if mut with immutable variable (#13728)
parent
9495aacf3e
commit
f7feb634d2
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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{}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 | }
|
|
@ -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)
|
||||
}
|
|
@ -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() + ' ')
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue