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 {`
|
cond_type table.Type // type of `x` in `match x {`
|
||||||
expected_type table.Type // for debugging only
|
expected_type table.Type // for debugging only
|
||||||
is_sum_type bool
|
is_sum_type bool
|
||||||
is_interface bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MatchBranch {
|
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
|
} else if idx_lo >= table.byte_type_idx { // both operands are unsigned
|
||||||
return type_hi
|
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
|
} else if idx_lo >= table.i8_type_idx &&
|
||||||
return if idx_lo == table.i64_type_idx { type_lo } else { type_hi }
|
(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) {
|
} 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
|
return type_lo // conversion unsigned -> signed if signed type is larger
|
||||||
} else {
|
} 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 {
|
pub fn (c &Checker) check_sumtype_compatibility(a, b table.Type) bool {
|
||||||
if c.table.sumtype_has_variant(a, b) {
|
return c.table.sumtype_has_variant(a, b) || c.table.sumtype_has_variant(b, a)
|
||||||
return true
|
|
||||||
}
|
|
||||||
if c.table.sumtype_has_variant(b, a) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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',
|
c.error('$infix_expr.op.str(): type `$typ_sym.source_name` does not exist',
|
||||||
type_expr.pos)
|
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',
|
c.error('`$infix_expr.op.str()` can only be used with interfaces and sum types',
|
||||||
infix_expr.pos)
|
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 require_return := false
|
||||||
mut branch_without_return := false
|
mut branch_without_return := false
|
||||||
for branch in node.branches {
|
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)
|
c.stmts(branch.stmts)
|
||||||
// If the last statement is an expression, return its type
|
// If the last statement is an expression, return its type
|
||||||
if branch.stmts.len > 0 {
|
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
|
// branch_exprs is a histogram of how many times
|
||||||
// an expr was used in the match
|
// an expr was used in the match
|
||||||
mut branch_exprs := map[string]int{}
|
mut branch_exprs := map[string]int{}
|
||||||
|
cond_type_sym := c.table.get_type_symbol(node.cond_type)
|
||||||
for branch in node.branches {
|
for branch in node.branches {
|
||||||
for expr in branch.exprs {
|
for expr in branch.exprs {
|
||||||
mut key := ''
|
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
|
c.expected_type = node.cond_type
|
||||||
expr_type := c.expr(expr)
|
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)
|
expr_str := c.table.type_to_str(expr_type)
|
||||||
expect_str := c.table.type_to_str(c.expected_type)
|
expect_str := c.table.type_to_str(c.expected_type)
|
||||||
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)
|
||||||
|
@ -3161,36 +3147,47 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
||||||
// smartcast sumtypes and interfaces when using `is`
|
// smartcast sumtypes and interfaces when using `is`
|
||||||
if !is_ct && branch.cond is ast.InfixExpr {
|
if !is_ct && branch.cond is ast.InfixExpr {
|
||||||
infix := branch.cond as ast.InfixExpr
|
infix := branch.cond as ast.InfixExpr
|
||||||
if infix.op == .key_is &&
|
if infix.op == .key_is {
|
||||||
(infix.left is ast.Ident || infix.left is ast.SelectorExpr) && infix.right is ast.Type {
|
|
||||||
right_expr := infix.right as ast.Type
|
right_expr := infix.right as ast.Type
|
||||||
is_variable := if infix.left is ast.Ident { (infix.left as ast.Ident).kind ==
|
left_sym := c.table.get_type_symbol(infix.left_type)
|
||||||
.variable } else { true }
|
expr_type := c.expr(infix.left)
|
||||||
// Register shadow variable or `as` variable with actual type
|
if left_sym.kind == .interface_ {
|
||||||
if is_variable {
|
c.type_implements(right_expr.typ, expr_type, branch.pos)
|
||||||
left_sym := c.table.get_type_symbol(infix.left_type)
|
} else if !c.check_types(expr_type, right_expr.typ) {
|
||||||
if left_sym.kind in [.sum_type, .interface_] && branch.left_as_name.len > 0 {
|
expect_str := c.table.type_to_str(right_expr.typ)
|
||||||
mut is_mut := false
|
expr_str := c.table.type_to_str(expr_type)
|
||||||
mut scope := c.file.scope.innermost(branch.body_pos.pos)
|
c.error('cannot use type `$expect_str` as type `$expr_str`', branch.pos)
|
||||||
if infix.left is ast.Ident as infix_left {
|
}
|
||||||
if var := scope.find_var(infix_left.name) {
|
if (infix.left is ast.Ident ||
|
||||||
is_mut = var.is_mut
|
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 {
|
scope.register(branch.left_as_name, ast.Var{
|
||||||
selector := infix.left as ast.SelectorExpr
|
name: branch.left_as_name
|
||||||
field := c.table.struct_find_field(left_sym, selector.field_name) or {
|
typ: right_expr.typ.to_ptr()
|
||||||
table.Field{}
|
pos: infix.left.position()
|
||||||
}
|
is_used: true
|
||||||
is_mut = field.is_mut
|
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
|
single_line = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else if stmt is ast.Comment {
|
|
||||||
single_line = false
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for branch in it.branches {
|
for branch in it.branches {
|
||||||
|
|
Loading…
Reference in New Issue