checker: smartcast only if type is not mut (#6841)
parent
4559b4138f
commit
20bec81678
|
@ -201,3 +201,21 @@ pub fn (sc &Scope) show(depth int, max_depth int) string {
|
|||
pub fn (sc &Scope) str() string {
|
||||
return sc.show(0, 0)
|
||||
}
|
||||
|
||||
// is_selector_root_mutable checks if the root ident is mutable
|
||||
// Example:
|
||||
// ```
|
||||
// mut x := MyStruct{}
|
||||
// x.foo.bar.z
|
||||
// ```
|
||||
// Since x is mutable, it returns true.
|
||||
pub fn (s &Scope) is_selector_root_mutable(t &table.Table, selector_expr SelectorExpr) bool {
|
||||
if selector_expr.expr is SelectorExpr as left_expr {
|
||||
return s.is_selector_root_mutable(t, left_expr)
|
||||
} else if selector_expr.expr is Ident as left_expr {
|
||||
if v := s.find_var(left_expr.name) {
|
||||
return v.is_mut
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -2202,44 +2202,8 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
|
|||
else {}
|
||||
}
|
||||
if !is_blank_ident && right_sym.kind != .placeholder {
|
||||
// Assign to sum type if ordinary value
|
||||
mut final_left_type := left_type_unwrapped
|
||||
mut scope := c.file.scope.innermost(left.position().pos)
|
||||
match left {
|
||||
ast.SelectorExpr {
|
||||
if _ := scope.find_struct_field(left.expr_type, left.field_name) {
|
||||
final_left_type = right_type_unwrapped
|
||||
mut inner_scope := c.open_scope(mut scope, left.pos.pos)
|
||||
inner_scope.register_struct_field(ast.ScopeStructField{
|
||||
struct_type: left.expr_type
|
||||
name: left.field_name
|
||||
typ: final_left_type
|
||||
sum_type_cast: right_type_unwrapped
|
||||
pos: left.pos
|
||||
})
|
||||
}
|
||||
}
|
||||
ast.Ident {
|
||||
if v := scope.find_var(left.name) {
|
||||
if v.sum_type_cast != 0 &&
|
||||
c.table.sumtype_has_variant(final_left_type, right_type_unwrapped) {
|
||||
final_left_type = right_type_unwrapped
|
||||
mut inner_scope := c.open_scope(mut scope, left.pos.pos)
|
||||
inner_scope.register(left.name, ast.Var{
|
||||
name: left.name
|
||||
typ: final_left_type
|
||||
pos: left.pos
|
||||
is_used: true
|
||||
is_mut: left.is_mut
|
||||
sum_type_cast: right_type_unwrapped
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
// Dual sides check (compatibility check)
|
||||
c.check_expected(right_type_unwrapped, final_left_type) or {
|
||||
c.check_expected(right_type_unwrapped, left_type_unwrapped) or {
|
||||
c.error('cannot assign to `$left`: $err', right.position())
|
||||
}
|
||||
}
|
||||
|
@ -3760,7 +3724,7 @@ 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 left_sym.kind == .union_sum_type {
|
||||
if !is_mut && left_sym.kind == .union_sum_type {
|
||||
scope.register(branch.left_as_name, ast.Var{
|
||||
name: branch.left_as_name
|
||||
typ: infix.left_type
|
||||
|
@ -3771,11 +3735,14 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
|||
})
|
||||
}
|
||||
} else if infix.left is ast.SelectorExpr as selector {
|
||||
field := c.table.struct_find_field(left_sym, selector.field_name) or {
|
||||
expr_sym := c.table.get_type_symbol(selector.expr_type)
|
||||
field := c.table.struct_find_field(expr_sym, selector.field_name) or {
|
||||
table.Field{}
|
||||
}
|
||||
is_mut = field.is_mut
|
||||
if left_sym.kind == .union_sum_type {
|
||||
is_root_mut := scope.is_selector_root_mutable(c.table,
|
||||
selector)
|
||||
if !is_root_mut && !is_mut && left_sym.kind == .union_sum_type {
|
||||
scope.register_struct_field(ast.ScopeStructField{
|
||||
struct_type: selector.expr_type
|
||||
name: selector.field_name
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
vlib/v/checker/tests/sum_type_mutable_cast_err.vv:23:10: error: infix expr: cannot use `any_int` (right expression) as `Abc`
|
||||
21 | mut x := Abc(0)
|
||||
22 | if x is int {
|
||||
23 | _ := x + 5
|
||||
| ^
|
||||
24 | }
|
||||
25 | f := Foo{Bar{Abc(0)}}
|
||||
vlib/v/checker/tests/sum_type_mutable_cast_err.vv:27:14: error: infix expr: cannot use `any_int` (right expression) as `Abc`
|
||||
25 | f := Foo{Bar{Abc(0)}}
|
||||
26 | if f.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: infix expr: cannot use `any_int` (right expression) as `Abc`
|
||||
29 | mut f2 := Foo2{Bar2{Abc(0)}}
|
||||
30 | if f2.b.a is int {
|
||||
31 | _ := f.b.a + 5
|
||||
| ^
|
||||
32 | }
|
||||
33 | }
|
|
@ -0,0 +1,33 @@
|
|||
__type Abc = int | string
|
||||
|
||||
struct Bar {
|
||||
mut:
|
||||
a Abc
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
b Bar
|
||||
}
|
||||
|
||||
struct Bar2 {
|
||||
a Abc
|
||||
}
|
||||
|
||||
struct Foo2 {
|
||||
b Bar2
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut x := Abc(0)
|
||||
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
|
||||
}
|
||||
}
|
|
@ -279,19 +279,19 @@ fn test_assignment() {
|
|||
x = 'test'
|
||||
|
||||
if x is string {
|
||||
assert x == 'test'
|
||||
x2 := x as string
|
||||
assert x2 == 'test'
|
||||
}
|
||||
}
|
||||
|
||||
__type Inner = int | string
|
||||
struct InnerStruct {
|
||||
mut:
|
||||
x Inner
|
||||
}
|
||||
__type Outer = string | InnerStruct
|
||||
|
||||
fn test_nested_if_is() {
|
||||
mut b := Outer(InnerStruct{Inner(0)})
|
||||
b := Outer(InnerStruct{Inner(0)})
|
||||
if b is InnerStruct {
|
||||
if b.x is int {
|
||||
assert b.x == 0
|
||||
|
@ -299,113 +299,12 @@ fn test_nested_if_is() {
|
|||
}
|
||||
}
|
||||
|
||||
fn test_casted_sum_type_selector_reassign() {
|
||||
mut b := InnerStruct{Inner(0)}
|
||||
if b.x is int {
|
||||
assert typeof(b.x) == 'int'
|
||||
// this check works only if x is castet
|
||||
assert b.x == 0
|
||||
b.x = 'test'
|
||||
// this check works only if x is castet
|
||||
assert b.x[0] == `t`
|
||||
assert typeof(b.x) == 'string'
|
||||
}
|
||||
// this check works only if x is not castet
|
||||
assert b.x is string
|
||||
}
|
||||
|
||||
fn test_casted_sum_type_ident_reassign() {
|
||||
mut x := Inner(0)
|
||||
if x is int {
|
||||
// this check works only if x is castet
|
||||
assert x == 0
|
||||
assert typeof(x) == 'int'
|
||||
x = 'test'
|
||||
// this check works only if x is castet
|
||||
assert x[0] == `t`
|
||||
assert typeof(x) == 'string'
|
||||
}
|
||||
// this check works only if x is not castet
|
||||
assert x is string
|
||||
}
|
||||
|
||||
__type Expr2 = int | string
|
||||
|
||||
fn test_match_with_reassign_casted_type() {
|
||||
mut e := Expr2(0)
|
||||
match union mut e {
|
||||
int {
|
||||
e = int(5)
|
||||
assert e == 5
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_if_is_with_reassign_casted_type() {
|
||||
mut e := Expr2(0)
|
||||
if e is int {
|
||||
e = int(5)
|
||||
assert e == 5
|
||||
}
|
||||
}
|
||||
|
||||
struct Expr2Wrapper {
|
||||
__type Expr3 = CallExpr | CTempVarExpr
|
||||
struct Expr3Wrapper {
|
||||
mut:
|
||||
expr Expr2
|
||||
expr Expr3
|
||||
}
|
||||
|
||||
fn test_change_type_if_is_selector() {
|
||||
mut e := Expr2Wrapper{Expr2(0)}
|
||||
if e.expr is int {
|
||||
e.expr = 'str'
|
||||
assert e.expr.len == 3
|
||||
}
|
||||
assert e.expr is string
|
||||
}
|
||||
|
||||
fn test_change_type_if_is() {
|
||||
mut e := Expr2(0)
|
||||
if e is int {
|
||||
e = 'str'
|
||||
assert e.len == 3
|
||||
}
|
||||
assert e is string
|
||||
}
|
||||
|
||||
fn test_change_type_match() {
|
||||
mut e := Expr2(0)
|
||||
match union mut e {
|
||||
int {
|
||||
e = 'str'
|
||||
assert e.len == 3
|
||||
}
|
||||
else {}
|
||||
}
|
||||
assert e is string
|
||||
}
|
||||
|
||||
__type Expr3 = CallExpr | string
|
||||
|
||||
struct CallExpr {
|
||||
mut:
|
||||
is_expr bool
|
||||
}
|
||||
|
||||
fn test_assign_sum_type_casted_field() {
|
||||
mut e := Expr3(CallExpr{})
|
||||
if e is CallExpr {
|
||||
e.is_expr = true
|
||||
assert e.is_expr
|
||||
}
|
||||
}
|
||||
|
||||
__type Expr4 = CallExpr2 | CTempVarExpr
|
||||
struct Expr4Wrapper {
|
||||
mut:
|
||||
expr Expr4
|
||||
}
|
||||
struct CallExpr2 {
|
||||
y int
|
||||
x string
|
||||
}
|
||||
|
@ -414,29 +313,28 @@ struct CTempVarExpr {
|
|||
x string
|
||||
}
|
||||
|
||||
fn gen(_ Expr4) CTempVarExpr {
|
||||
fn gen(_ Expr3) CTempVarExpr {
|
||||
return CTempVarExpr{}
|
||||
}
|
||||
|
||||
fn test_reassign_from_function_with_parameter() {
|
||||
mut f := Expr4(CallExpr2{})
|
||||
if f is CallExpr2 {
|
||||
mut f := Expr3(CallExpr{})
|
||||
if f is CallExpr {
|
||||
f = gen(f)
|
||||
}
|
||||
}
|
||||
|
||||
fn test_reassign_from_function_with_parameter_selector() {
|
||||
mut f := Expr4Wrapper{Expr4(CallExpr2{})}
|
||||
if f.expr is CallExpr2 {
|
||||
mut f := Expr3Wrapper{Expr3(CallExpr{})}
|
||||
if f.expr is CallExpr {
|
||||
f.expr = gen(f.expr)
|
||||
}
|
||||
}
|
||||
|
||||
fn test_match_multi_branch() {
|
||||
f := Expr4(CTempVarExpr{'ctemp'})
|
||||
mut y := ''
|
||||
f := Expr3(CTempVarExpr{'ctemp'})
|
||||
match union f {
|
||||
CallExpr2, CTempVarExpr {
|
||||
CallExpr, CTempVarExpr {
|
||||
// this check works only if f is not castet
|
||||
assert f is CTempVarExpr
|
||||
}
|
||||
|
@ -444,17 +342,17 @@ fn test_match_multi_branch() {
|
|||
}
|
||||
|
||||
fn test_typeof() {
|
||||
x := Expr4(CTempVarExpr{})
|
||||
x := Expr3(CTempVarExpr{})
|
||||
assert typeof(x) == 'CTempVarExpr'
|
||||
}
|
||||
|
||||
struct Outer2 {
|
||||
e Expr4
|
||||
e Expr3
|
||||
}
|
||||
|
||||
fn test_zero_value_init() {
|
||||
// no c compiler error then it's successful
|
||||
o := Outer2{}
|
||||
_ := Outer2{}
|
||||
}
|
||||
|
||||
fn test_sum_type_match() {
|
||||
|
|
Loading…
Reference in New Issue