From 3c336b566df45b1048b0dd8878c2a9090d820606 Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Thu, 15 Oct 2020 16:30:36 +0100 Subject: [PATCH] checker: add check_expected() which returns an optional error (#6623) --- vlib/v/checker/check_types.v | 9 ++++ vlib/v/checker/checker.v | 44 ++++++++----------- .../tests/arrow_op_wrong_left_type_err_b.out | 2 +- .../checker/tests/assign_expr_type_err_i.out | 4 +- vlib/v/checker/tests/cannot_assign_array.out | 2 +- vlib/v/checker/tests/in_mismatch_type.out | 22 +++++----- vlib/v/checker/tests/map_init_wrong_type.out | 2 +- .../tests/modules/overload_return_type.out | 2 +- vlib/v/checker/tests/overload_return_type.out | 2 +- vlib/v/checker/tests/sum.out | 4 +- 10 files changed, 48 insertions(+), 45 deletions(-) diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index 87a8c835a9..c36f6ad1f8 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -278,6 +278,15 @@ pub fn (mut c Checker) check_types(got table.Type, expected table.Type) bool { return true } +pub fn (mut c Checker) check_expected(got table.Type, expected table.Type) ? { + if c.check_types(got, expected) { + return + } + exps := c.table.type_to_str(expected) + gots := c.table.type_to_str(got) + return error('expected `$exps`, not `$gots`') +} + pub fn (mut c Checker) symmetric_check(left table.Type, right table.Type) bool { // allow direct int-literal assignment for pointers for now // maybe in the future optionals should be used for that diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 9c30e89783..c69593485f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -373,12 +373,8 @@ pub fn (mut c Checker) struct_decl(decl ast.StructDecl) { if field.has_default_expr { c.expected_type = field.typ field_expr_type := c.expr(field.default_expr) - if !c.check_types(field_expr_type, field.typ) { - field_expr_type_sym := c.table.get_type_symbol(field_expr_type) - field_type_sym := c.table.get_type_symbol(field.typ) - c.error('default expression for field `$field.name` ' + - 'has type `$field_expr_type_sym.source_name`, but should be `$field_type_sym.source_name`', - field.default_expr.position()) + c.check_expected(field_expr_type, field.typ) or { + c.error('incompatible initializer for field `$field.name`: $err', field.default_expr.position()) } // Check for unnecessary inits like ` = 0` and ` = ''` if field.typ.is_ptr() { @@ -507,11 +503,10 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type { c.expected_type = info_field.typ expr_type := c.expr(field.expr) expr_type_sym := c.table.get_type_symbol(expr_type) - field_type_sym := c.table.get_type_symbol(info_field.typ) - if !c.check_types(expr_type, info_field.typ) && expr_type != table.void_type && - expr_type_sym.kind != .placeholder { - c.error('cannot assign $expr_type_sym.kind `$expr_type_sym.source_name` as `$field_type_sym.source_name` for field `$info_field.name`', - field.pos) + if expr_type != table.void_type && expr_type_sym.kind != .placeholder { + c.check_expected(expr_type, info_field.typ) or { + c.error('cannot assign to field `$info_field.name`: $err', field.pos) + } } if info_field.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() && !expr_type.is_number() { @@ -566,7 +561,6 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { infix_expr.right_type = right_type mut right := c.table.get_type_symbol(right_type) mut left := c.table.get_type_symbol(left_type) - left_default := c.table.get_type_symbol(c.table.mktyp(left_type)) left_pos := infix_expr.left.position() right_pos := infix_expr.right.position() if (left_type.is_ptr() || left.is_pointer()) && @@ -584,23 +578,22 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { match right.kind { .array { elem_type := right.array_info().elem_type - right_sym := c.table.get_type_symbol(c.table.mktyp(right.array_info().elem_type)) // if left_default.kind != right_sym.kind { - if !c.check_types(left_type, elem_type) { - c.error('the data type on the left of `$infix_expr.op.str()` (`$left.name`) does not match the array item type (`$right_sym.source_name`)', + c.check_expected(left_type, elem_type) or { + c.error('left operand to `$infix_expr.op` does not match the array element type: $err', infix_expr.pos) } } .map { - key_sym := c.table.get_type_symbol(c.table.mktyp(right.map_info().key_type)) - if left_default.kind != key_sym.kind { - c.error('the data type on the left of `$infix_expr.op.str()` (`$left.name`) does not match the map key type `$key_sym.source_name`', + elem_type := right.map_info().key_type + c.check_expected(left_type, elem_type) or { + c.error('left operand to `$infix_expr.op` does not match the map key type: $err', infix_expr.pos) } } .string { - if left.kind != .string { - c.error('the data type on the left of `$infix_expr.op.str()` must be a string (is `$left.name`)', + c.check_expected(left_type, right_type) or { + c.error('left operand to `$infix_expr.op` does not match: $err', infix_expr.pos) } } @@ -1950,7 +1943,7 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { if is_decl { c.check_valid_snake_case(left.name, 'variable name', left.pos) } - mut ident_var_info := left.var_info() + mut ident_var_info := left.info as ast.IdentVar if ident_var_info.share == .shared_t { left_type = left_type.set_flag(.shared_f) } @@ -1995,6 +1988,7 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { right_sym := c.table.get_type_symbol(right_type_unwrapped) if (left_type.is_ptr() || left_sym.is_pointer()) && assign_stmt.op !in [.assign, .decl_assign] && !c.inside_unsafe { + // ptr op= c.warn('pointer arithmetic is only allowed in `unsafe` blocks', assign_stmt.pos) } if c.pref.translated { @@ -2054,10 +2048,10 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { else {} } // Dual sides check (compatibility check) - if !is_blank_ident && !c.check_types(right_type_unwrapped, left_type_unwrapped) && - right_sym.kind != .placeholder { - c.error('cannot assign `$right_sym.source_name` to `$left.str()` of type `$left_sym.source_name`', - right.position()) + if !is_blank_ident && right_sym.kind != .placeholder { + c.check_expected(right_type_unwrapped, left_type_unwrapped) or { + c.error('cannot assign to `$left`: $err', right.position()) + } } } } diff --git a/vlib/v/checker/tests/arrow_op_wrong_left_type_err_b.out b/vlib/v/checker/tests/arrow_op_wrong_left_type_err_b.out index a1369cac7e..a85a421768 100644 --- a/vlib/v/checker/tests/arrow_op_wrong_left_type_err_b.out +++ b/vlib/v/checker/tests/arrow_op_wrong_left_type_err_b.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/arrow_op_wrong_left_type_err_b.vv:4:8: error: cannot assign `string` to `obj` of type `int` +vlib/v/checker/tests/arrow_op_wrong_left_type_err_b.vv:4:8: error: cannot assign to `obj`: expected `int`, not `string` 2 | ch := chan string{} 3 | mut obj := 9 4 | obj = <-ch diff --git a/vlib/v/checker/tests/assign_expr_type_err_i.out b/vlib/v/checker/tests/assign_expr_type_err_i.out index 5e6e52e686..1edbbc0719 100644 --- a/vlib/v/checker/tests/assign_expr_type_err_i.out +++ b/vlib/v/checker/tests/assign_expr_type_err_i.out @@ -1,6 +1,6 @@ -vlib/v/checker/tests/assign_expr_type_err_i.vv:3:9: error: cannot assign `string` to `foo` of type `f64` +vlib/v/checker/tests/assign_expr_type_err_i.vv:3:9: error: cannot assign to `foo`: expected `f64`, not `string` 1 | fn main() { 2 | mut foo := 1.5 - 3 | foo += 'hello' + 3 | foo += 'hello' | ~~~~~~~ 4 | } diff --git a/vlib/v/checker/tests/cannot_assign_array.out b/vlib/v/checker/tests/cannot_assign_array.out index ae758d4105..537cd37f52 100644 --- a/vlib/v/checker/tests/cannot_assign_array.out +++ b/vlib/v/checker/tests/cannot_assign_array.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/cannot_assign_array.vv:9:11: error: cannot assign `[8]f64` to `ctx.vb` of type `string` +vlib/v/checker/tests/cannot_assign_array.vv:9:11: error: cannot assign to `ctx.vb`: expected `string`, not `[8]f64` 7 | mut ctx := Context{} 8 | x := 2.32 9 | ctx.vb = [1.1, x, 3.3, 4.4, 5.0, 6.0, 7.0, 8.9]!! diff --git a/vlib/v/checker/tests/in_mismatch_type.out b/vlib/v/checker/tests/in_mismatch_type.out index 33f973c553..8c02b4376c 100644 --- a/vlib/v/checker/tests/in_mismatch_type.out +++ b/vlib/v/checker/tests/in_mismatch_type.out @@ -1,74 +1,74 @@ -vlib/v/checker/tests/in_mismatch_type.vv:10:7: error: the data type on the left of `in` (`any_int`) does not match the array item type (`string`) +vlib/v/checker/tests/in_mismatch_type.vv:10:7: error: left operand to `in` does not match the array element type: expected `string`, not `any_int` 8 | } 9 | s := 'abcd' 10 | if 1 in a_s { | ~~ 11 | println('ok') 12 | } -vlib/v/checker/tests/in_mismatch_type.vv:13:7: error: the data type on the left of `in` (`any_int`) does not match the map key type `string` +vlib/v/checker/tests/in_mismatch_type.vv:13:7: error: left operand to `in` does not match the map key type: expected `string`, not `any_int` 11 | println('ok') 12 | } 13 | if 2 in m { | ~~ 14 | println('yeah') 15 | } -vlib/v/checker/tests/in_mismatch_type.vv:16:7: error: the data type on the left of `in` must be a string (is `any_int`) +vlib/v/checker/tests/in_mismatch_type.vv:16:7: error: left operand to `in` does not match: expected `string`, not `any_int` 14 | println('yeah') 15 | } 16 | if 3 in s { | ~~ 17 | println('dope') 18 | } -vlib/v/checker/tests/in_mismatch_type.vv:19:9: error: the data type on the left of `in` must be a string (is `rune`) +vlib/v/checker/tests/in_mismatch_type.vv:19:9: error: left operand to `in` does not match: expected `string`, not `rune` 17 | println('dope') 18 | } 19 | if `a` in s { | ~~ 20 | println("oh no :'(") 21 | } -vlib/v/checker/tests/in_mismatch_type.vv:22:7: error: `in` can only be used with an array/map/string +vlib/v/checker/tests/in_mismatch_type.vv:22:7: error: `in` can only be used with an array/map/string 20 | println("oh no :'(") 21 | } 22 | if 1 in 12 { | ~~ 23 | println('right') 24 | } -vlib/v/checker/tests/in_mismatch_type.vv:25:12: error: the data type on the left of `in` (`Int`) does not match the map key type `string` +vlib/v/checker/tests/in_mismatch_type.vv:25:12: error: left operand to `in` does not match the map key type: expected `string`, not `Int` 23 | println('right') 24 | } 25 | if Int(2) in m { | ~~ 26 | println('yeah') 27 | } -vlib/v/checker/tests/in_mismatch_type.vv:28:9: error: the data type on the left of `in` (`string`) does not match the array item type (`int`) +vlib/v/checker/tests/in_mismatch_type.vv:28:9: error: left operand to `in` does not match the array element type: expected `int`, not `string` 26 | println('yeah') 27 | } 28 | if '3' in a_i { | ~~ 29 | println('sure') 30 | } -vlib/v/checker/tests/in_mismatch_type.vv:31:9: error: the data type on the left of `in` (`string`) does not match the array item type (`int`) +vlib/v/checker/tests/in_mismatch_type.vv:31:9: error: left operand to `in` does not match the array element type: expected `int`, not `string` 29 | println('sure') 30 | } 31 | if '2' in a_i { | ~~ 32 | println('all right') 33 | } -vlib/v/checker/tests/in_mismatch_type.vv:34:7: error: the data type on the left of `!in` (`any_int`) does not match the array item type (`string`) +vlib/v/checker/tests/in_mismatch_type.vv:34:7: error: left operand to `!in` does not match the array element type: expected `string`, not `any_int` 32 | println('all right') 33 | } 34 | if 1 !in a_s { | ~~~ 35 | println('ok') 36 | } -vlib/v/checker/tests/in_mismatch_type.vv:37:9: error: the data type on the left of `!in` (`string`) does not match the array item type (`int`) +vlib/v/checker/tests/in_mismatch_type.vv:37:9: error: left operand to `!in` does not match the array element type: expected `int`, not `string` 35 | println('ok') 36 | } 37 | if '1' !in a_i { | ~~~ 38 | println('good') 39 | } -vlib/v/checker/tests/in_mismatch_type.vv:41:7: error: the data type on the left of `!in` (`any_int`) does not match the map key type `string` +vlib/v/checker/tests/in_mismatch_type.vv:41:7: error: left operand to `!in` does not match the map key type: expected `string`, not `any_int` 39 | } 40 | 41 | if 5 !in m { diff --git a/vlib/v/checker/tests/map_init_wrong_type.out b/vlib/v/checker/tests/map_init_wrong_type.out index 9e8d833b32..c49ce4d904 100644 --- a/vlib/v/checker/tests/map_init_wrong_type.out +++ b/vlib/v/checker/tests/map_init_wrong_type.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/map_init_wrong_type.vv:3:10: error: cannot assign `map[string]f64` to `a` of type `map[string]f32` +vlib/v/checker/tests/map_init_wrong_type.vv:3:10: error: cannot assign to `a`: expected `map[string]f32`, not `map[string]f64` 1 | fn main() { 2 | mut a := map[string]f32{} 3 | a = { 'x': 12.3 } diff --git a/vlib/v/checker/tests/modules/overload_return_type.out b/vlib/v/checker/tests/modules/overload_return_type.out index de1dc3e0c9..d5f3df3537 100644 --- a/vlib/v/checker/tests/modules/overload_return_type.out +++ b/vlib/v/checker/tests/modules/overload_return_type.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/modules/overload_return_type/main.v:8:11: error: cannot assign `int` to `two` of type `Point` +vlib/v/checker/tests/modules/overload_return_type/main.v:8:11: error: cannot assign to `two`: expected `Point`, not `int` 6 | one := Point {x:1, y:2} 7 | mut two := Point {x:5, y:1} 8 | two = one + two diff --git a/vlib/v/checker/tests/overload_return_type.out b/vlib/v/checker/tests/overload_return_type.out index b15bd4967e..547519c4f7 100644 --- a/vlib/v/checker/tests/overload_return_type.out +++ b/vlib/v/checker/tests/overload_return_type.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/overload_return_type.vv:14:11: error: cannot assign `int` to `two` of type `Point` +vlib/v/checker/tests/overload_return_type.vv:14:11: error: cannot assign to `two`: expected `Point`, not `int` 12 | mut one := Point {x:1, y:2} 13 | mut two := Point {x:5, y:1} 14 | two = one + two diff --git a/vlib/v/checker/tests/sum.out b/vlib/v/checker/tests/sum.out index 330761e29c..670c58ed71 100644 --- a/vlib/v/checker/tests/sum.out +++ b/vlib/v/checker/tests/sum.out @@ -13,13 +13,13 @@ vlib/v/checker/tests/sum.vv:6:8: error: cannot cast non-sum type `int` using `as 7 | } 8 | vlib/v/checker/tests/sum.vv:10:11: error: cannot cast `rune` to `Var` - 8 | + 8 | 9 | fn sum() { 10 | _ := Var(`J`) | ~~~ 11 | mut s2 := Var('') 12 | s2 = true -vlib/v/checker/tests/sum.vv:12:7: error: cannot assign `bool` to `s2` of type `Var` +vlib/v/checker/tests/sum.vv:12:7: error: cannot assign to `s2`: expected `Var`, not `bool` 10 | _ := Var(`J`) 11 | mut s2 := Var('') 12 | s2 = true