checker: fix error for if mut with immutable variable (#13728)

pull/13733/head
yuyi 2022-03-13 22:33:50 +08:00 committed by GitHub
parent 9495aacf3e
commit f7feb634d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 61 additions and 29 deletions

View File

@ -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

View File

@ -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)

View File

@ -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{}

View File

@ -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 {

View File

@ -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)
} }

View File

@ -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)

View File

@ -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 | }

View File

@ -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)
}

View File

@ -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() + ' ')
} }

View File

@ -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)

View File

@ -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 {

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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