checker: check type in `is` InfixExpr (#6407)
parent
ff92c3409d
commit
a1e127ae46
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 | }
|
|
@ -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')
|
||||
}
|
||||
}
|
|
@ -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 | }
|
|
@ -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 {}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue