From 8ab0d42b5ffa903085db0fd60f2ad1877e64013a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kr=C3=BCger?= <45282134+UweKrueger@users.noreply.github.com> Date: Mon, 19 Apr 2021 10:41:21 +0200 Subject: [PATCH] checker: check argument for `chan.try_push/pop()` (#9798) --- doc/docs.md | 2 +- vlib/sync/channel_polling_test.v | 2 +- vlib/sync/channel_try_buf_test.v | 2 +- vlib/sync/channel_try_unbuf_test.v | 2 +- vlib/v/checker/checker.v | 39 ++++++++++++++++++++++++------ vlib/v/checker/tests/chan_args.out | 28 +++++++++++++++++++++ vlib/v/checker/tests/chan_args.vv | 15 ++++++++++++ 7 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 vlib/v/checker/tests/chan_args.out create mode 100644 vlib/v/checker/tests/chan_args.vv diff --git a/doc/docs.md b/doc/docs.md index 3daa934acd..f3417d2aec 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -2904,7 +2904,7 @@ println(l) println(c) mut b := Abc{} ch2 := chan Abc{} -res2 := ch2.try_pop(b) // try to perform `b = <-ch2` +res2 := ch2.try_pop(mut b) // try to perform `b = <-ch2` ``` The `try_push/pop()` methods will return immediately with one of the results diff --git a/vlib/sync/channel_polling_test.v b/vlib/sync/channel_polling_test.v index ba8e1d5c03..f1a7329c7c 100644 --- a/vlib/sync/channel_polling_test.v +++ b/vlib/sync/channel_polling_test.v @@ -18,7 +18,7 @@ fn do_rec(ch chan int, resch chan i64, n int) { mut sum := i64(0) for _ in 0 .. n { mut r := 0 - for ch.try_pop(r) != .success { + for ch.try_pop(mut r) != .success { } sum += r } diff --git a/vlib/sync/channel_try_buf_test.v b/vlib/sync/channel_try_buf_test.v index 6a0a88dfa3..0d718386aa 100644 --- a/vlib/sync/channel_try_buf_test.v +++ b/vlib/sync/channel_try_buf_test.v @@ -7,7 +7,7 @@ fn test_channel_try_buffered() { } } mut obj := int(0) - for ch.try_pop(obj) == .success { + for ch.try_pop(mut obj) == .success { println(obj) } assert obj == 6 diff --git a/vlib/sync/channel_try_unbuf_test.v b/vlib/sync/channel_try_unbuf_test.v index 45dbe33443..9b710f20fc 100644 --- a/vlib/sync/channel_try_unbuf_test.v +++ b/vlib/sync/channel_try_unbuf_test.v @@ -8,6 +8,6 @@ fn test_channel_try_unbuffered() { panic('push on non-ready channel not detected') } mut obj := -17 - for ch.try_pop(obj) == .success {} + for ch.try_pop(mut obj) == .success {} assert obj == -17 } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index e584f21712..6f3da5837f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1612,22 +1612,44 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type { } else { call_expr.return_type = method.return_type } - + mut exp_arg_typ := ast.Type(0) // type of 1st arg for special builtin methods + mut param_is_mut := false + mut no_type_promotion := false + if left_type_sym.kind == .chan { + elem_typ := (left_type_sym.info as ast.Chan).elem_type + if method_name == 'try_push' { + exp_arg_typ = elem_typ.to_ptr() + } else if method_name == 'try_pop' { + exp_arg_typ = elem_typ + param_is_mut = true + no_type_promotion = true + } + } // if method_name == 'clone' { // println('CLONE nr args=$method.args.len') // } // call_expr.args << method.args[0].typ // call_expr.exp_arg_types << method.args[0].typ for i, arg in call_expr.args { - exp_arg_typ := if method.is_variadic && i >= method.params.len - 1 { - method.params[method.params.len - 1].typ - } else { - method.params[i + 1].typ + if i > 0 || exp_arg_typ == ast.Type(0) { + exp_arg_typ = if method.is_variadic && i >= method.params.len - 1 { + method.params[method.params.len - 1].typ + } else { + method.params[i + 1].typ + } + param_is_mut = false + no_type_promotion = false } exp_arg_sym := c.table.get_type_symbol(exp_arg_typ) c.expected_type = exp_arg_typ got_arg_typ := c.check_expr_opt_call(arg.expr, c.expr(arg.expr)) call_expr.args[i].typ = got_arg_typ + if no_type_promotion { + if got_arg_typ != exp_arg_typ { + c.error('cannot use `${c.table.get_type_symbol(got_arg_typ).name}` as argument for `$method.name` (`$exp_arg_sym.name` expected)', + arg.pos) + } + } if method.is_variadic && got_arg_typ.has_flag(.variadic) && call_expr.args.len - 1 > i { c.error('when forwarding a variadic variable, it must be the final argument', arg.pos) @@ -1656,6 +1678,7 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type { } else { method.params[i + 1] } + param_is_mut = param_is_mut || param.is_mut param_share := param.typ.share() if param_share == .shared_t && (c.locked_names.len > 0 || c.rlocked_names.len > 0) { c.error('method with `shared` arguments cannot be called inside `lock`/`rlock` block', @@ -1663,12 +1686,12 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type { } if arg.is_mut { to_lock, pos := c.fail_if_immutable(arg.expr) - if !param.is_mut { + if !param_is_mut { tok := arg.share.str() c.error('`$call_expr.name` parameter `$param.name` is not `$tok`, `$tok` is not needed`', arg.expr.position()) } else { - if param.typ.share() != arg.share { + if param_share != arg.share { c.error('wrong shared type', arg.expr.position()) } if to_lock != '' && param_share != .shared_t { @@ -1677,7 +1700,7 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type { } } } else { - if param.is_mut { + if param_is_mut { tok := arg.share.str() c.error('`$call_expr.name` parameter `$param.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${ i + 1}`', arg.expr.position()) diff --git a/vlib/v/checker/tests/chan_args.out b/vlib/v/checker/tests/chan_args.out new file mode 100644 index 0000000000..605bcfe790 --- /dev/null +++ b/vlib/v/checker/tests/chan_args.out @@ -0,0 +1,28 @@ +vlib/v/checker/tests/chan_args.vv:4:19: error: cannot use `int` as `&f64` in argument 1 to `chan f64.try_push` + 2 | ch := chan f64{cap: 5} + 3 | a := 2 + 4 | _ := ch.try_push(a) + | ^ + 5 | _ := ch.try_push(2.5) + 6 | b := 2.5 +vlib/v/checker/tests/chan_args.vv:5:19: error: cannot use `float literal` as `&f64` in argument 1 to `chan f64.try_push` + 3 | a := 2 + 4 | _ := ch.try_push(a) + 5 | _ := ch.try_push(2.5) + | ~~~ + 6 | b := 2.5 + 7 | _ := ch.try_pop(b) +vlib/v/checker/tests/chan_args.vv:7:18: error: `try_pop` parameter `obj` is `mut`, you need to provide `mut` e.g. `mut arg1` + 5 | _ := ch.try_push(2.5) + 6 | b := 2.5 + 7 | _ := ch.try_pop(b) + | ^ + 8 | // this should work: + 9 | _ := ch.try_push(b) +vlib/v/checker/tests/chan_args.vv:11:22: error: cannot use `int` as argument for `try_pop` (`f64` expected) + 9 | _ := ch.try_push(b) + 10 | mut c := 7 + 11 | _ := ch.try_pop(mut c) + | ^ + 12 | mut x := 12.5 + 13 | // this should work: diff --git a/vlib/v/checker/tests/chan_args.vv b/vlib/v/checker/tests/chan_args.vv new file mode 100644 index 0000000000..ce16cd8afe --- /dev/null +++ b/vlib/v/checker/tests/chan_args.vv @@ -0,0 +1,15 @@ +fn main() { + ch := chan f64{cap: 5} + a := 2 + _ := ch.try_push(a) + _ := ch.try_push(2.5) + b := 2.5 + _ := ch.try_pop(b) + // this should work: + _ := ch.try_push(b) + mut c := 7 + _ := ch.try_pop(mut c) + mut x := 12.5 + // this should work: + _ := ch.try_pop(mut x) +}