diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 4f2436f901..8f6285fbd9 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -519,7 +519,6 @@ pub mut: cond_type table.Type // type of `x` in `match x {` expected_type table.Type // for debugging only is_sum_type bool - is_interface bool } pub struct MatchBranch { diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index 09d2993065..e487461295 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -217,8 +217,13 @@ fn (c &Checker) promote_num(left_type, right_type table.Type) table.Type { } } else if idx_lo >= table.byte_type_idx { // both operands are unsigned return type_hi - } else if idx_lo >= table.i8_type_idx && (idx_hi <= table.i64_type_idx || idx_hi == table.rune_type_idx) { // both signed - return if idx_lo == table.i64_type_idx { type_lo } else { type_hi } + } else if idx_lo >= table.i8_type_idx && + (idx_hi <= table.i64_type_idx || idx_hi == table.rune_type_idx) { // both signed + return if idx_lo == table.i64_type_idx { + type_lo + } else { + type_hi + } } else if idx_hi - idx_lo < (table.byte_type_idx - table.i8_type_idx) { return type_lo // conversion unsigned -> signed if signed type is larger } else { @@ -361,11 +366,5 @@ pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) table.T } pub fn (c &Checker) check_sumtype_compatibility(a, b table.Type) bool { - if c.table.sumtype_has_variant(a, b) { - return true - } - if c.table.sumtype_has_variant(b, a) { - return true - } - return false + return c.table.sumtype_has_variant(a, b) || c.table.sumtype_has_variant(b, a) } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 0e4cd52b4c..d111c085cb 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -711,7 +711,7 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { c.error('$infix_expr.op.str(): type `$typ_sym.source_name` does not exist', type_expr.pos) } - if left.kind != .interface_ && left.kind != .sum_type { + if left.kind !in [.interface_, .sum_type] { c.error('`$infix_expr.op.str()` can only be used with interfaces and sum types', infix_expr.pos) } @@ -2891,23 +2891,6 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) table.Type { mut require_return := false mut branch_without_return := false for branch in node.branches { - for expr in branch.exprs { - c.expected_type = cond_type - typ := c.expr(expr) - typ_sym := c.table.get_type_symbol(typ) - if node.is_sum_type || node.is_interface { - ok := if cond_type_sym.kind == .sum_type { - c.table.sumtype_has_variant(cond_type, typ) - } else { - // interface match - c.type_implements(typ, cond_type, node.pos) - } - if !ok { - c.error('cannot use `$typ_sym.source_name` as `$cond_type_sym.source_name` in `match`', - node.pos) - } - } - } c.stmts(branch.stmts) // If the last statement is an expression, return its type if branch.stmts.len > 0 { @@ -2953,6 +2936,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol // branch_exprs is a histogram of how many times // an expr was used in the match mut branch_exprs := map[string]int{} + cond_type_sym := c.table.get_type_symbol(node.cond_type) for branch in node.branches { for expr in branch.exprs { mut key := '' @@ -3001,7 +2985,9 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol } c.expected_type = node.cond_type expr_type := c.expr(expr) - if !c.check_types(expr_type, c.expected_type) { + if cond_type_sym.kind == .interface_ { + c.type_implements(expr_type, c.expected_type, branch.pos) + } else if !c.check_types(expr_type, c.expected_type) { expr_str := c.table.type_to_str(expr_type) expect_str := c.table.type_to_str(c.expected_type) c.error('cannot use type `$expect_str` as type `$expr_str`', node.pos) @@ -3161,36 +3147,47 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { // smartcast sumtypes and interfaces when using `is` if !is_ct && branch.cond is ast.InfixExpr { infix := branch.cond as ast.InfixExpr - if infix.op == .key_is && - (infix.left is ast.Ident || infix.left is ast.SelectorExpr) && infix.right is ast.Type { + if infix.op == .key_is { right_expr := infix.right as ast.Type - is_variable := if infix.left is ast.Ident { (infix.left as ast.Ident).kind == - .variable } else { true } - // Register shadow variable or `as` variable with actual type - if is_variable { - left_sym := c.table.get_type_symbol(infix.left_type) - if left_sym.kind in [.sum_type, .interface_] && branch.left_as_name.len > 0 { - mut is_mut := false - mut scope := c.file.scope.innermost(branch.body_pos.pos) - if infix.left is ast.Ident as infix_left { - if var := scope.find_var(infix_left.name) { - is_mut = var.is_mut + left_sym := c.table.get_type_symbol(infix.left_type) + expr_type := c.expr(infix.left) + if left_sym.kind == .interface_ { + c.type_implements(right_expr.typ, expr_type, branch.pos) + } else if !c.check_types(expr_type, right_expr.typ) { + expect_str := c.table.type_to_str(right_expr.typ) + expr_str := c.table.type_to_str(expr_type) + c.error('cannot use type `$expect_str` as type `$expr_str`', branch.pos) + } + if (infix.left is ast.Ident || + infix.left is ast.SelectorExpr) && + infix.right is ast.Type { + is_variable := if infix.left is ast.Ident { (infix.left as ast.Ident).kind == + .variable } else { true } + // Register shadow variable or `as` variable with actual type + if is_variable { + if left_sym.kind in [.sum_type, .interface_] && branch.left_as_name.len > 0 { + mut is_mut := false + mut scope := c.file.scope.innermost(branch.body_pos.pos) + if infix.left is ast.Ident as infix_left { + if var := scope.find_var(infix_left.name) { + is_mut = var.is_mut + } + } else if infix.left is ast.SelectorExpr { + selector := infix.left as ast.SelectorExpr + field := c.table.struct_find_field(left_sym, selector.field_name) or { + table.Field{} + } + is_mut = field.is_mut } - } else if infix.left is ast.SelectorExpr { - selector := infix.left as ast.SelectorExpr - field := c.table.struct_find_field(left_sym, selector.field_name) or { - table.Field{} - } - is_mut = field.is_mut + scope.register(branch.left_as_name, ast.Var{ + name: branch.left_as_name + typ: right_expr.typ.to_ptr() + pos: infix.left.position() + is_used: true + is_mut: is_mut + }) + node.branches[i].smartcast = true } - scope.register(branch.left_as_name, ast.Var{ - name: branch.left_as_name - typ: right_expr.typ.to_ptr() - pos: infix.left.position() - is_used: true - is_mut: is_mut - }) - node.branches[i].smartcast = true } } } diff --git a/vlib/v/checker/tests/is_type_invalid.out b/vlib/v/checker/tests/is_type_invalid.out new file mode 100644 index 0000000000..2efb9fd7e0 --- /dev/null +++ b/vlib/v/checker/tests/is_type_invalid.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/is_type_invalid.vv:14:2: error: cannot use type `byte` as type `IoS` + 12 | + 13 | fn main() { + 14 | if IoS(1) is byte { + | ~~~~~~~~~~~~~~~~~ + 15 | println('not cool') + 16 | } +vlib/v/checker/tests/is_type_invalid.vv:18:2: error: `Cat` doesn't implement method `speak` + 16 | } + 17 | a := Animal(Dog{}) + 18 | if a is Cat { + | ~~~~~~~~~~~ + 19 | println('not cool either') + 20 | } diff --git a/vlib/v/checker/tests/is_type_invalid.vv b/vlib/v/checker/tests/is_type_invalid.vv new file mode 100644 index 0000000000..2e7f774567 --- /dev/null +++ b/vlib/v/checker/tests/is_type_invalid.vv @@ -0,0 +1,21 @@ +type IoS = int | string + +interface Animal { + speak() +} + +struct Dog {} + +fn (d Dog) speak() {} + +struct Cat {} + +fn main() { + if IoS(1) is byte { + println('not cool') + } + a := Animal(Dog{}) + if a is Cat { + println('not cool either') + } +} diff --git a/vlib/v/checker/tests/match_sumtype_type_invalid.out b/vlib/v/checker/tests/match_sumtype_type_invalid.out new file mode 100644 index 0000000000..2e28328bf5 --- /dev/null +++ b/vlib/v/checker/tests/match_sumtype_type_invalid.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/match_sumtype_type_invalid.vv:14:2: error: cannot use type `IoS` as type `byte` + 12 | + 13 | fn main() { + 14 | match IoS(1) { + | ~~~~~~~~~~~~~~ + 15 | byte { + 16 | println('not cool') +vlib/v/checker/tests/match_sumtype_type_invalid.vv:21:3: error: `Cat` doesn't implement method `speak` + 19 | a := Animal(Dog{}) + 20 | match a { + 21 | Cat { + | ~~~~~ + 22 | println('not cool either') + 23 | } diff --git a/vlib/v/checker/tests/match_sumtype_type_invalid.vv b/vlib/v/checker/tests/match_sumtype_type_invalid.vv new file mode 100644 index 0000000000..f75d29736d --- /dev/null +++ b/vlib/v/checker/tests/match_sumtype_type_invalid.vv @@ -0,0 +1,26 @@ +type IoS = int | string + +interface Animal { + speak() +} + +struct Dog {} + +fn (d Dog) speak() {} + +struct Cat {} + +fn main() { + match IoS(1) { + byte { + println('not cool') + } + } + a := Animal(Dog{}) + match a { + Cat { + println('not cool either') + } + else {} + } +} diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index e5fe7996ca..9e787e2170 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -1521,9 +1521,6 @@ pub fn (mut f Fmt) match_expr(it ast.MatchExpr) { single_line = false break } - } else if stmt is ast.Comment { - single_line = false - break } } for branch in it.branches {