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 struct Var {
|
||||||
pub:
|
pub:
|
||||||
name string
|
name string
|
||||||
expr Expr
|
|
||||||
share ShareType
|
share ShareType
|
||||||
is_mut bool
|
is_mut bool
|
||||||
is_autofree_tmp bool
|
is_autofree_tmp bool
|
||||||
|
@ -632,6 +631,7 @@ pub:
|
||||||
is_auto_deref bool
|
is_auto_deref bool
|
||||||
is_inherited bool
|
is_inherited bool
|
||||||
pub mut:
|
pub mut:
|
||||||
|
expr Expr
|
||||||
typ Type
|
typ Type
|
||||||
orig_type Type // original sumtype type; 0 if it's not a sumtype
|
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
|
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:
|
pub:
|
||||||
key_var string
|
key_var string
|
||||||
val_var string
|
val_var string
|
||||||
cond Expr
|
|
||||||
is_range bool
|
is_range bool
|
||||||
high Expr // `10` in `for i in 0..10 {`
|
high Expr // `10` in `for i in 0..10 {`
|
||||||
stmts []Stmt
|
stmts []Stmt
|
||||||
|
@ -1005,6 +1004,7 @@ pub:
|
||||||
val_is_mut bool // `for mut val in vals {` means that modifying `val` will modify the array
|
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
|
// and the array cannot be indexed inside the loop
|
||||||
pub mut:
|
pub mut:
|
||||||
|
cond Expr
|
||||||
key_type Type
|
key_type Type
|
||||||
val_type Type
|
val_type Type
|
||||||
cond_type Type
|
cond_type Type
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
|
||||||
node.left_types = []
|
node.left_types = []
|
||||||
mut right_len := node.right.len
|
mut right_len := node.right.len
|
||||||
mut right_type0 := ast.void_type
|
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.CallExpr, ast.IfExpr, ast.LockExpr, ast.MatchExpr] {
|
||||||
if right in [ast.IfExpr, ast.MatchExpr] && node.left.len == node.right.len && !is_decl
|
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() {
|
&& 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
|
right_len = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if right is ast.InfixExpr {
|
if mut right is ast.InfixExpr {
|
||||||
if right.op == .arrow {
|
if right.op == .arrow {
|
||||||
c.error('cannot use `<-` on the right-hand side of an assignment, as it does not return any values',
|
c.error('cannot use `<-` on the right-hand side of an assignment, as it does not return any values',
|
||||||
right.pos)
|
right.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if right is ast.Ident {
|
if mut right is ast.Ident {
|
||||||
if right.is_mut {
|
if right.is_mut {
|
||||||
c.error('unexpected `mut` on right-hand side of assignment', right.mut_pos)
|
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)
|
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)
|
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]
|
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)
|
right_sym := c.table.sym(right_type)
|
||||||
if right_sym.info is ast.Struct {
|
if right_sym.info is ast.Struct {
|
||||||
if right_sym.info.generic_types.len > 0 {
|
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 {
|
if is_decl {
|
||||||
// check generic struct init and return unwrap generic struct type
|
// 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) {
|
if right.typ.has_flag(.generic) {
|
||||||
c.expr(right)
|
c.expr(right)
|
||||||
right_type = right.typ
|
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 {
|
if right.op == .amp && right.right is ast.StructInit {
|
||||||
right_type = c.expr(right)
|
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)
|
left_type = ast.mktyp(right_type)
|
||||||
}
|
}
|
||||||
if left_type == ast.int_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
|
mut is_large := right.val.len > 13
|
||||||
if !is_large && right.val.len > 8 {
|
if !is_large && right.val.len > 8 {
|
||||||
val := right.val.i64()
|
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 left_type in ast.unsigned_integer_type_idxs {
|
||||||
if right is ast.IntegerLiteral {
|
if mut right is ast.IntegerLiteral {
|
||||||
if right.val[0] == `-` {
|
if right.val[0] == `-` {
|
||||||
c.error('Cannot assign negative value to unsigned integer type',
|
c.error('Cannot assign negative value to unsigned integer type',
|
||||||
right.pos)
|
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
|
// 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)
|
sym := c.table.sym(cur_type)
|
||||||
to_type := if sym.kind == .interface_ { to_type_.ref() } else { to_type_ }
|
to_type := if sym.kind == .interface_ { to_type_.ref() } else { to_type_ }
|
||||||
match expr {
|
mut expr := unsafe { expr_ }
|
||||||
|
match mut expr {
|
||||||
ast.SelectorExpr {
|
ast.SelectorExpr {
|
||||||
mut is_mut := false
|
mut is_mut := false
|
||||||
mut smartcasts := []ast.Type{}
|
mut smartcasts := []ast.Type{}
|
||||||
|
|
|
@ -102,7 +102,7 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
|
||||||
}
|
}
|
||||||
if node.val_is_mut {
|
if node.val_is_mut {
|
||||||
value_type = value_type.ref()
|
value_type = value_type.ref()
|
||||||
match node.cond {
|
match mut node.cond {
|
||||||
ast.Ident {
|
ast.Ident {
|
||||||
if mut node.cond.obj is ast.Var {
|
if mut node.cond.obj is ast.Var {
|
||||||
if !node.cond.obj.is_mut {
|
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)
|
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 {
|
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
|
node.left.kind == .variable
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
if is_variable {
|
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] {
|
if left_sym.kind in [.interface_, .sum_type] {
|
||||||
c.smartcast(node.left, node.left_type, right_type, mut scope)
|
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 {
|
if expr_type !in cond_type_sym.info.variants {
|
||||||
expr_str := c.table.type_to_str(expr_type)
|
expr_str := c.table.type_to_str(expr_type)
|
||||||
expect_str := c.table.type_to_str(node.cond_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) {
|
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 {
|
if node.info.is_mut {
|
||||||
f.write(node.info.share.str() + ' ')
|
f.write(node.info.share.str() + ' ')
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,8 @@ import v.ast
|
||||||
import v.util
|
import v.util
|
||||||
import v.token
|
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 {
|
if node.is_static {
|
||||||
g.write('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)
|
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 is_auto_heap := false
|
||||||
mut var_type := node.left_types[i]
|
mut var_type := node.left_types[i]
|
||||||
mut val_type := node.right_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.expr(left)
|
||||||
g.is_assign_lhs = false
|
g.is_assign_lhs = false
|
||||||
g.is_arraymap_set = false
|
g.is_arraymap_set = false
|
||||||
if left is ast.IndexExpr {
|
if mut left is ast.IndexExpr {
|
||||||
sym := g.table.sym(left.left_type)
|
sym := g.table.sym(left.left_type)
|
||||||
if sym.kind in [.map, .array] {
|
if sym.kind in [.map, .array] {
|
||||||
g.expr(val)
|
g.expr(val)
|
||||||
|
|
|
@ -3235,7 +3235,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
|
||||||
dot := if field.typ.is_ptr() { '->' } else { '.' }
|
dot := if field.typ.is_ptr() { '->' } else { '.' }
|
||||||
sum_type_deref_field += ')$dot'
|
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])
|
agg_sym := g.table.sym(cast_sym.info.types[g.aggregate_type_idx])
|
||||||
sum_type_deref_field += '_$agg_sym.cname'
|
sum_type_deref_field += '_$agg_sym.cname'
|
||||||
} else {
|
} else {
|
||||||
|
@ -3695,7 +3695,7 @@ fn (mut g Gen) ident(node ast.Ident) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dot := if is_ptr || is_auto_heap { '->' } else { '.' }
|
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])
|
sym := g.table.sym(cast_sym.info.types[g.aggregate_type_idx])
|
||||||
g.write('${dot}_$sym.cname')
|
g.write('${dot}_$sym.cname')
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1342,7 +1342,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dot := if is_ptr { '->' } else { '.' }
|
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])
|
sym := g.table.sym(cast_sym.info.types[g.aggregate_type_idx])
|
||||||
g.write('${dot}_$sym.cname')
|
g.write('${dot}_$sym.cname')
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -373,7 +373,7 @@ fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) {
|
||||||
}
|
}
|
||||||
if right.unaliased_sym.kind == .array {
|
if right.unaliased_sym.kind == .array {
|
||||||
if left.sym.kind in [.sum_type, .interface_] {
|
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 {
|
if node.right.exprs.len > 0 {
|
||||||
mut infix_exprs := []ast.InfixExpr{}
|
mut infix_exprs := []ast.InfixExpr{}
|
||||||
for i in 0 .. node.right.exprs.len {
|
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 {
|
if node.right.exprs.len > 0 {
|
||||||
// `a in [1,2,3]` optimization => `a == 1 || a == 2 || a == 3`
|
// `a in [1,2,3]` optimization => `a == 1 || a == 2 || a == 3`
|
||||||
// avoids an allocation
|
// 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) {
|
// fn (mut g Gen) str_int2(node ast.StringInterLiteral) {
|
||||||
if g.inside_comptime_for_field {
|
if g.inside_comptime_for_field {
|
||||||
mut node_ := unsafe { node }
|
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 is ast.Ident {
|
||||||
if mut expr.obj is ast.Var {
|
if mut expr.obj is ast.Var {
|
||||||
node_.expr_types[i] = expr.obj.typ
|
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)
|
mut file := parse_comptime(v_code, p.table, p.pref, scope)
|
||||||
file.path = tmpl_path
|
file.path = tmpl_path
|
||||||
// copy vars from current fn scope into vweb_tmpl scope
|
// 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 mut stmt is ast.FnDecl {
|
||||||
if stmt.name == 'main.vweb_tmpl_$tmp_fn_name' {
|
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 {
|
if mut obj is ast.Var {
|
||||||
stmt.scope.register(ast.Var{
|
stmt.scope.register(ast.Var{
|
||||||
...obj
|
...obj
|
||||||
|
|
Loading…
Reference in New Issue