From a38fe4fca993981a59ebbf5f4d5c85a91ae8244b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=C3=A4schle?= Date: Thu, 10 Dec 2020 00:59:39 +0100 Subject: [PATCH] checker: fix smartcast inside smartcast (#7215) --- vlib/v/ast/ast.v | 10 ++++++ vlib/v/checker/checker.v | 22 +++++++++--- .../tests/sum_type_mutable_cast_err.out | 19 ++++------ .../tests/sum_type_mutable_cast_err.vv | 4 --- vlib/v/gen/cgen.v | 17 +++++---- vlib/v/gen/comptime.v | 9 ++--- vlib/v/tests/sum_type_test.v | 35 +++++++++++++++++++ 7 files changed, 83 insertions(+), 33 deletions(-) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 96b045b09e..adb9d18ea6 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -114,6 +114,16 @@ pub mut: name_type table.Type // T in `T.name` or typeof in `typeof(expr).name` } +// root_ident returns the origin ident where the selector started. +pub fn (e &SelectorExpr) root_ident() Ident { + mut root := e.expr + for root is SelectorExpr { + selector_expr := root as SelectorExpr + root = selector_expr.expr + } + return root as Ident +} + // module declaration pub struct Module { pub: diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index c5487c5e8e..c4001ad602 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2977,9 +2977,10 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { mut is_mut := false if mut node.right.left is ast.Ident { ident := node.right.left - if ident.obj is ast.Var { - v := ident.obj as ast.Var - is_mut = v.is_mut + // TODO: temporary, remove this + ident_obj := ident.obj + if ident_obj is ast.Var { + is_mut = ident_obj.is_mut } } if !c.inside_unsafe && is_mut { @@ -3611,7 +3612,12 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol mut sum_type_casts := []table.Type{} expr_sym := c.table.get_type_symbol(node.cond.expr_type) if field := c.table.struct_find_field(expr_sym, node.cond.field_name) { - is_mut = field.is_mut + if field.is_mut { + root_ident := node.cond.root_ident() + if v := scope.find_var(root_ident.name) { + is_mut = v.is_mut + } + } } if field := scope.find_struct_field(node.cond.expr_type, node.cond.field_name) { sum_type_casts << field.sum_type_casts @@ -3864,6 +3870,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { is_variable := if mut infix.left is ast.Ident { infix.left.kind == .variable } else { true } // Register shadow variable or `as` variable with actual type if is_variable { + // TODO: merge this code with match_expr because it has the same logic implemented if left_sym.kind in [.interface_, .sum_type] { mut is_mut := false mut scope := c.file.scope.innermost(branch.body_pos.pos) @@ -3902,7 +3909,12 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { mut sum_type_casts := []table.Type{} expr_sym := c.table.get_type_symbol(infix.left.expr_type) if field := c.table.struct_find_field(expr_sym, infix.left.field_name) { - is_mut = field.is_mut + if field.is_mut { + root_ident := infix.left.root_ident() + if v := scope.find_var(root_ident.name) { + is_mut = v.is_mut + } + } } if field := scope.find_struct_field(infix.left.expr_type, infix.left.field_name) { diff --git a/vlib/v/checker/tests/sum_type_mutable_cast_err.out b/vlib/v/checker/tests/sum_type_mutable_cast_err.out index 6de52c7f59..a70b863c89 100644 --- a/vlib/v/checker/tests/sum_type_mutable_cast_err.out +++ b/vlib/v/checker/tests/sum_type_mutable_cast_err.out @@ -4,18 +4,11 @@ vlib/v/checker/tests/sum_type_mutable_cast_err.vv:23:10: error: cannot use opera 23 | _ := x + 5 | ^ 24 | } - 25 | f := Foo{Bar{Abc(0)}} -vlib/v/checker/tests/sum_type_mutable_cast_err.vv:27:14: error: cannot use operator `+` with `Abc` - 25 | f := Foo{Bar{Abc(0)}} - 26 | if f.b.a is int { + 25 | mut f2 := Foo2{Bar2{Abc(0)}} +vlib/v/checker/tests/sum_type_mutable_cast_err.vv:27:8: error: undefined ident: `f` + 25 | mut f2 := Foo2{Bar2{Abc(0)}} + 26 | if f2.b.a is int { 27 | _ := f.b.a + 5 - | ^ + | ^ 28 | } - 29 | mut f2 := Foo2{Bar2{Abc(0)}} -vlib/v/checker/tests/sum_type_mutable_cast_err.vv:31:14: error: cannot use operator `+` with `Abc` - 29 | mut f2 := Foo2{Bar2{Abc(0)}} - 30 | if f2.b.a is int { - 31 | _ := f.b.a + 5 - | ^ - 32 | } - 33 | } \ No newline at end of file + 29 | } \ No newline at end of file diff --git a/vlib/v/checker/tests/sum_type_mutable_cast_err.vv b/vlib/v/checker/tests/sum_type_mutable_cast_err.vv index 1244a49b2c..fcea074ff1 100644 --- a/vlib/v/checker/tests/sum_type_mutable_cast_err.vv +++ b/vlib/v/checker/tests/sum_type_mutable_cast_err.vv @@ -22,10 +22,6 @@ fn main() { if x is int { _ := x + 5 } - f := Foo{Bar{Abc(0)}} - if f.b.a is int { - _ := f.b.a + 5 - } mut f2 := Foo2{Bar2{Abc(0)}} if f2.b.a is int { _ := f.b.a + 5 diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 73a42b4d39..c0f806ed85 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -1740,8 +1740,10 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { // id_info := ident.var_info() // var_type = id_info.typ blank_assign = left.kind == .blank_ident - if left.info is ast.IdentVar { - share := (left.info as ast.IdentVar).share + // TODO: temporary, remove this + left_info := left.info + if left_info is ast.IdentVar { + share := left_info.share if share == .shared_t { var_type = var_type.set_flag(.shared_f) } @@ -3413,19 +3415,20 @@ fn (mut g Gen) ident(node ast.Ident) { g.write('_const_') } mut name := c_name(node.name) - if node.info is ast.IdentVar { - ident_var := node.info as ast.IdentVar + // TODO: temporary, remove this + node_info := node.info + if node_info is ast.IdentVar { // x ?int // `x = 10` => `x.data = 10` (g.right_is_opt == false) // `x = new_opt()` => `x = new_opt()` (g.right_is_opt == true) // `println(x)` => `println(*(int*)x.data)` - if ident_var.is_optional && !(g.is_assign_lhs && g.right_is_opt) { + if node_info.is_optional && !(g.is_assign_lhs && g.right_is_opt) { g.write('/*opt*/') - styp := g.base_type(ident_var.typ) + styp := g.base_type(node_info.typ) g.write('(*($styp*)${name}.data)') return } - if !g.is_assign_lhs && ident_var.share == .shared_t { + if !g.is_assign_lhs && node_info.share == .shared_t { g.write('${name}.val') return } diff --git a/vlib/v/gen/comptime.v b/vlib/v/gen/comptime.v index 5a58845b1c..1856b5b37d 100644 --- a/vlib/v/gen/comptime.v +++ b/vlib/v/gen/comptime.v @@ -304,10 +304,11 @@ fn (mut g Gen) comp_for(node ast.CompFor) { } } else if node.kind == .fields { // TODO add fields - if sym.info is table.Struct { - info := sym.info as table.Struct - mut fields := info.fields.filter(it.attrs.len == 0) - fields_with_attrs := info.fields.filter(it.attrs.len > 0) + // TODO: temporary, remove this + sym_info := sym.info + if sym_info is table.Struct { + mut fields := sym_info.fields.filter(it.attrs.len == 0) + fields_with_attrs := sym_info.fields.filter(it.attrs.len > 0) fields << fields_with_attrs if fields.len > 0 { g.writeln('\tFieldData $node.val_var;') diff --git a/vlib/v/tests/sum_type_test.v b/vlib/v/tests/sum_type_test.v index 9f216d33df..4e8ddb94a4 100644 --- a/vlib/v/tests/sum_type_test.v +++ b/vlib/v/tests/sum_type_test.v @@ -216,6 +216,41 @@ fn test_match() { } } +type WrapperType = FoodWrapper | string + +fn test_non_mut_ident_mut_selector_cast_match() { + w := WrapperType(FoodWrapper{Food(Eggs{'test'})}) + match w { + FoodWrapper { + match w.food { + Eggs { + assert w.food.name + '2' == 'test2' + } + else { + assert false + } + } + } + else { + assert false + } + } +} + +fn test_non_mut_ident_mut_selector_cast_if() { + w := WrapperType(FoodWrapper{Food(Eggs{'test'})}) + if w is FoodWrapper { + if w.food is Eggs { + assert w.food.name + '2' == 'test2' + + } else { + assert false + } + } else { + assert false + } +} + type Abc = int | string fn test_string_cast_to_sumtype() {