From c80cc917c7c4f568d6759741710ebc96fca36ca2 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Fri, 15 Jan 2021 00:20:58 +0000 Subject: [PATCH] checker: check casting struct -> interface; disallow casting struct -> interface pointer (#8110) --- vlib/v/checker/checker.v | 11 ++++--- vlib/v/checker/tests/struct_type_cast_err.out | 29 ++++++++++++------- vlib/v/checker/tests/struct_type_cast_err.vv | 2 ++ .../tests/unimplemented_interface_e.out | 10 ++++++- .../tests/unimplemented_interface_e.vv | 1 + 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index aeeacbdd6a..a5e47dadf9 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3481,10 +3481,13 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) table.Type { } else if node.expr_type == table.none_type { type_name := c.table.type_to_str(node.typ) c.error('cannot cast `none` to `$type_name`', node.pos) - } else if from_type_sym.kind == .struct_ && !node.expr_type.is_ptr() && to_type_sym.kind !in - [.sum_type, .interface_] && !c.is_builtin_mod { - type_name := c.table.type_to_str(node.typ) - c.error('cannot cast `struct` to `$type_name`', node.pos) + } else if from_type_sym.kind == .struct_ && !node.expr_type.is_ptr() { + if (node.typ.is_ptr() || to_type_sym.kind !in [.sum_type, .interface_]) && !c.is_builtin_mod { + type_name := c.table.type_to_str(node.typ) + c.error('cannot cast struct to `$type_name`', node.pos) + } else if to_type_sym.kind == .interface_ { + c.type_implements(node.expr_type, node.typ, node.pos) + } } else if node.expr_type.has_flag(.optional) || node.expr_type.has_flag(.variadic) { // variadic case can happen when arrays are converted into variadic msg := if node.expr_type.has_flag(.optional) { 'an optional' } else { 'a variadic' } diff --git a/vlib/v/checker/tests/struct_type_cast_err.out b/vlib/v/checker/tests/struct_type_cast_err.out index cdf9f0c6b1..39b4ea3613 100644 --- a/vlib/v/checker/tests/struct_type_cast_err.out +++ b/vlib/v/checker/tests/struct_type_cast_err.out @@ -1,32 +1,32 @@ -vlib/v/checker/tests/struct_type_cast_err.vv:5:10: error: cannot cast `struct` to `string` +vlib/v/checker/tests/struct_type_cast_err.vv:5:10: error: cannot cast struct to `string` 3 | fn main() { 4 | foo := Foo{} 5 | _ := string(foo) | ~~~~~~~~~~~ 6 | _ := int(foo) 7 | _ := u64(foo) -vlib/v/checker/tests/struct_type_cast_err.vv:6:10: error: cannot cast `struct` to `int` +vlib/v/checker/tests/struct_type_cast_err.vv:6:10: error: cannot cast struct to `int` 4 | foo := Foo{} 5 | _ := string(foo) 6 | _ := int(foo) | ~~~~~~~~ 7 | _ := u64(foo) 8 | _ := u32(foo) -vlib/v/checker/tests/struct_type_cast_err.vv:7:10: error: cannot cast `struct` to `u64` +vlib/v/checker/tests/struct_type_cast_err.vv:7:10: error: cannot cast struct to `u64` 5 | _ := string(foo) 6 | _ := int(foo) 7 | _ := u64(foo) | ~~~~~~~~ 8 | _ := u32(foo) 9 | _ := rune(foo) -vlib/v/checker/tests/struct_type_cast_err.vv:8:10: error: cannot cast `struct` to `u32` +vlib/v/checker/tests/struct_type_cast_err.vv:8:10: error: cannot cast struct to `u32` 6 | _ := int(foo) 7 | _ := u64(foo) 8 | _ := u32(foo) | ~~~~~~~~ 9 | _ := rune(foo) 10 | _ := byte(foo) -vlib/v/checker/tests/struct_type_cast_err.vv:9:10: error: cannot cast `struct` to `rune` +vlib/v/checker/tests/struct_type_cast_err.vv:9:10: error: cannot cast struct to `rune` 7 | _ := u64(foo) 8 | _ := u32(foo) 9 | _ := rune(foo) @@ -40,24 +40,31 @@ vlib/v/checker/tests/struct_type_cast_err.vv:10:10: error: cannot cast type `Foo | ~~~~~~~~~ 11 | _ := i8(foo) 12 | _ := i64(foo) -vlib/v/checker/tests/struct_type_cast_err.vv:11:10: error: cannot cast `struct` to `i8` +vlib/v/checker/tests/struct_type_cast_err.vv:11:10: error: cannot cast struct to `i8` 9 | _ := rune(foo) 10 | _ := byte(foo) 11 | _ := i8(foo) | ~~~~~~~ 12 | _ := i64(foo) 13 | _ := int(foo) -vlib/v/checker/tests/struct_type_cast_err.vv:12:10: error: cannot cast `struct` to `i64` +vlib/v/checker/tests/struct_type_cast_err.vv:12:10: error: cannot cast struct to `i64` 10 | _ := byte(foo) 11 | _ := i8(foo) 12 | _ := i64(foo) | ~~~~~~~~ 13 | _ := int(foo) - 14 | } -vlib/v/checker/tests/struct_type_cast_err.vv:13:10: error: cannot cast `struct` to `int` + 14 | _ = &I1(foo) +vlib/v/checker/tests/struct_type_cast_err.vv:13:10: error: cannot cast struct to `int` 11 | _ := i8(foo) 12 | _ := i64(foo) 13 | _ := int(foo) | ~~~~~~~~ - 14 | } - 15 | + 14 | _ = &I1(foo) + 15 | } +vlib/v/checker/tests/struct_type_cast_err.vv:14:10: error: cannot cast struct to `&I1` + 12 | _ := i64(foo) + 13 | _ := int(foo) + 14 | _ = &I1(foo) + | ~~~~~~~ + 15 | } + 16 | diff --git a/vlib/v/checker/tests/struct_type_cast_err.vv b/vlib/v/checker/tests/struct_type_cast_err.vv index e800a5662b..a27eefdb81 100644 --- a/vlib/v/checker/tests/struct_type_cast_err.vv +++ b/vlib/v/checker/tests/struct_type_cast_err.vv @@ -11,5 +11,7 @@ fn main() { _ := i8(foo) _ := i64(foo) _ := int(foo) + _ = &I1(foo) } +interface I1{} diff --git a/vlib/v/checker/tests/unimplemented_interface_e.out b/vlib/v/checker/tests/unimplemented_interface_e.out index 49d1fe3512..9ccd0bdf71 100644 --- a/vlib/v/checker/tests/unimplemented_interface_e.out +++ b/vlib/v/checker/tests/unimplemented_interface_e.out @@ -3,5 +3,13 @@ vlib/v/checker/tests/unimplemented_interface_e.vv:12:6: error: `Cat` incorrectly 11 | fn main() { 12 | foo(Cat{}) | ~~~~~ - 13 | } + 13 | _ = Animal(Cat{}) + 14 | } +details: main.Animal has `speak(s string)` +vlib/v/checker/tests/unimplemented_interface_e.vv:13:6: error: `Cat` incorrectly implements method `speak` of interface `Animal`: expected `string`, not `&string` for parameter 1 + 11 | fn main() { + 12 | foo(Cat{}) + 13 | _ = Animal(Cat{}) + | ~~~~~~~~~~~~~ + 14 | } details: main.Animal has `speak(s string)` diff --git a/vlib/v/checker/tests/unimplemented_interface_e.vv b/vlib/v/checker/tests/unimplemented_interface_e.vv index 34d3d797fc..85cee4230d 100644 --- a/vlib/v/checker/tests/unimplemented_interface_e.vv +++ b/vlib/v/checker/tests/unimplemented_interface_e.vv @@ -10,4 +10,5 @@ fn foo(a Animal) {} fn main() { foo(Cat{}) + _ = Animal(Cat{}) }