From 9feb0103552bba8db1385eefa63bea74903387ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=C3=A4schle?= Date: Thu, 19 Nov 2020 17:28:46 +0100 Subject: [PATCH] all: mutable smartcasts (#6878) --- vlib/v/ast/ast.v | 2 +- vlib/v/checker/checker.v | 59 +++++++++++++++++++++--------- vlib/v/fmt/fmt.v | 2 +- vlib/v/parser/if_match.v | 6 +-- vlib/v/tests/union_sum_type_test.v | 46 +++++++++++++++++++++++ 5 files changed, 93 insertions(+), 22 deletions(-) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 987f7e88cf..15c4f6deae 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -533,7 +533,7 @@ pub: body_pos token.Position comments []Comment left_as_name string // `name` in `if cond is SumType as name` - mut_name bool // `if mut name is` + is_mut_name bool // `if mut name is` pub mut: stmts []Stmt smartcast bool // true when cond is `x is SumType`, set in checker.if_expr // no longer needed with union sum types TODO: remove diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index e24cea9e72..d7addc0369 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3516,21 +3516,41 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol } mut scope := c.file.scope.innermost(branch.pos.pos) match node.cond as node_cond { - ast.SelectorExpr { scope.register_struct_field(ast.ScopeStructField{ - struct_type: node_cond.expr_type - name: node_cond.field_name - typ: node.cond_type - sum_type_cast: expr_type - pos: node_cond.pos - }) } - ast.Ident { scope.register(node.var_name, ast.Var{ - name: node.var_name - typ: node.cond_type - pos: node_cond.pos - is_used: true - is_mut: node.is_mut - sum_type_cast: expr_type - }) } + ast.SelectorExpr { + expr_sym := c.table.get_type_symbol(node_cond.expr_type) + field := c.table.struct_find_field(expr_sym, node_cond.field_name) or { + table.Field{} + } + is_mut := field.is_mut + is_root_mut := scope.is_selector_root_mutable(c.table, node_cond) + // smartcast either if the value is immutable or if the mut argument is explicitly given + if (!is_root_mut && !is_mut) || node.is_mut { + scope.register_struct_field(ast.ScopeStructField{ + struct_type: node_cond.expr_type + name: node_cond.field_name + typ: node.cond_type + sum_type_cast: expr_type + pos: node_cond.pos + }) + } + } + ast.Ident { + mut is_mut := false + if v := scope.find_var(node_cond.name) { + is_mut = v.is_mut + } + // smartcast either if the value is immutable or if the mut argument is explicitly given + if !is_mut || node.is_mut { + scope.register(node.var_name, ast.Var{ + name: node.var_name + typ: node.cond_type + pos: node_cond.pos + is_used: true + is_mut: node.is_mut + sum_type_cast: expr_type + }) + } + } else {} } } @@ -3762,7 +3782,9 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { if v := scope.find_var(infix_left.name) { is_mut = v.is_mut } - if !is_mut && left_sym.kind == .union_sum_type { + // smartcast either if the value is immutable or if the mut argument is explicitly given + if (!is_mut || branch.is_mut_name) && + left_sym.kind == .union_sum_type { scope.register(branch.left_as_name, ast.Var{ name: branch.left_as_name typ: infix.left_type @@ -3780,7 +3802,10 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { is_mut = field.is_mut is_root_mut := scope.is_selector_root_mutable(c.table, selector) - if !is_root_mut && !is_mut && left_sym.kind == .union_sum_type { + // smartcast either if the value is immutable or if the mut argument is explicitly given + if ((!is_root_mut && !is_mut) || + branch.is_mut_name) && + left_sym.kind == .union_sum_type { scope.register_struct_field(ast.ScopeStructField{ struct_type: selector.expr_type name: selector.field_name diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 090f618d84..b939a5148b 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -1423,7 +1423,7 @@ pub fn (mut f Fmt) if_expr(it ast.IfExpr) { } if i < it.branches.len - 1 || !it.has_else { f.write('${dollar}if ') - if branch.mut_name { + if branch.is_mut_name { f.write('mut ') } f.expr(branch.cond) diff --git a/vlib/v/parser/if_match.v b/vlib/v/parser/if_match.v index c4e7cc4db7..454a8cc8c3 100644 --- a/vlib/v/parser/if_match.v +++ b/vlib/v/parser/if_match.v @@ -86,9 +86,9 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr { } comments << p.eat_comments() // `if mut name is T` - mut mut_name := false + mut is_mut_name := false if p.tok.kind == .key_mut && p.peek_tok2.kind == .key_is { - mut_name = true + is_mut_name = true p.next() comments << p.eat_comments() } @@ -150,7 +150,7 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr { body_pos: body_pos.extend(p.prev_tok.position()) comments: comments left_as_name: left_as_name - mut_name: mut_name + is_mut_name: is_mut_name } comments = p.eat_comments() if is_comptime { diff --git a/vlib/v/tests/union_sum_type_test.v b/vlib/v/tests/union_sum_type_test.v index 73f73f26ec..e9a1aec3b3 100644 --- a/vlib/v/tests/union_sum_type_test.v +++ b/vlib/v/tests/union_sum_type_test.v @@ -346,10 +346,12 @@ fn test_zero_value_init() { } struct Milk { +mut: name string } struct Eggs { +mut: name string } @@ -364,6 +366,50 @@ fn test_match_aggregate() { } } +fn test_match_mut() { + mut f := Food(Milk{'test'}) + match union mut f { + Eggs { + f.name = 'eggs' + assert f.name == 'eggs' + } + Milk { + f.name = 'milk' + assert f.name == 'milk' + } + } +} + +fn test_match_not_mut() { + mut f := Food(Milk{'test'}) + match union f { + Eggs { + // only works without smartcast + assert f is Eggs + } + Milk { + // only works without smartcast + assert f is Milk + } + } +} + +fn test_if_mut() { + mut f := Food(Milk{'test'}) + if mut f is Milk { + f.name = 'milk' + assert f.name == 'milk' + } +} + +fn test_if_not_mut() { + mut f := Food(Milk{'test'}) + if f is Milk { + // only works without smartcast + assert f is Milk + } +} + fn test_sum_type_match() { // TODO: Remove these casts assert is_gt_simple('3', int(2))