From e5d1881e1a88ef5e58793a19fec08f54c7ae9603 Mon Sep 17 00:00:00 2001 From: shove Date: Wed, 2 Nov 2022 01:05:34 +0800 Subject: [PATCH] checker: fix multiple pointer check of fn and method args (fix #16261 #16262 #16263) (#16275) --- vlib/v/checker/check_types.v | 16 ++++++++ vlib/v/checker/fn.v | 28 +++++++++++++ .../tests/fn_call_arg_ptr_mismatch_err.out | 20 ++++++++++ .../tests/fn_call_arg_ptr_mismatch_err.vv | 40 +++++++++++++++++++ 4 files changed, 104 insertions(+) create mode 100644 vlib/v/checker/tests/fn_call_arg_ptr_mismatch_err.out create mode 100644 vlib/v/checker/tests/fn_call_arg_ptr_mismatch_err.vv diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index bd5e850b38..022286710b 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -168,6 +168,22 @@ pub fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool { return true } +fn (c Checker) check_multiple_ptr_match(got ast.Type, expected ast.Type, param ast.Param, arg ast.CallArg) bool { + param_nr_muls := if param.is_mut && !expected.is_ptr() { 1 } else { expected.nr_muls() } + if got.is_ptr() && got.nr_muls() > 1 && got.nr_muls() != param_nr_muls { + if arg.expr is ast.PrefixExpr && (arg.expr as ast.PrefixExpr).op == .amp { + return false + } + if arg.expr is ast.UnsafeExpr { + expr := (arg.expr as ast.UnsafeExpr).expr + if expr is ast.PrefixExpr && (expr as ast.PrefixExpr).op == .amp { + return false + } + } + } + return true +} + pub fn (mut c Checker) check_expected_call_arg(got ast.Type, expected_ ast.Type, language ast.Language, arg ast.CallArg) ? { if got == 0 { return error('unexpected 0 type') diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index b7a7f48d03..b2185a0b07 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1047,6 +1047,13 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) c.mark_as_referenced(mut &call_arg.expr, true) } } + + if arg_typ !in [ast.voidptr_type, ast.nil_type] + && !c.check_multiple_ptr_match(arg_typ, param.typ, param, call_arg) { + got_typ_str, expected_typ_str := c.get_string_names_of(arg_typ, param.typ) + c.error('cannot use `$got_typ_str` as `$expected_typ_str` in argument ${i + 1} to `$fn_name`', + call_arg.pos) + } continue } if param.typ.is_ptr() && !param.is_mut && !call_arg.typ.is_real_pointer() @@ -1150,6 +1157,12 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) } c.error('$err.msg() in argument ${i + 1} to `$fn_name`', call_arg.pos) } + if final_param_sym.kind == .struct_ && arg_typ !in [ast.voidptr_type, ast.nil_type] + && !c.check_multiple_ptr_match(arg_typ, param.typ, param, call_arg) { + got_typ_str, expected_typ_str := c.get_string_names_of(arg_typ, param.typ) + c.error('cannot use `$got_typ_str` as `$expected_typ_str` in argument ${i + 1} to `$fn_name`', + call_arg.pos) + } // Warn about automatic (de)referencing, which will be removed soon. if func.language != .c && !c.inside_unsafe && arg_typ.nr_muls() != param.typ.nr_muls() && !(call_arg.is_mut && param.is_mut) && !(!call_arg.is_mut && !param.is_mut) @@ -1665,6 +1678,14 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } } } + + if got_arg_typ !in [ast.voidptr_type, ast.nil_type] + && !c.check_multiple_ptr_match(got_arg_typ, param.typ, param, arg) { + got_typ_str, expected_typ_str := c.get_string_names_of(got_arg_typ, + param.typ) + c.error('cannot use `$got_typ_str` as `$expected_typ_str` in argument ${i + 1} to `$method_name`', + arg.pos) + } continue } if param.typ.is_ptr() && !arg.typ.is_real_pointer() && arg.expr.is_literal() @@ -1693,6 +1714,13 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { c.error('$err.msg() in argument ${i + 1} to `${left_sym.name}.$method_name`', arg.pos) } + param_typ_sym := c.table.sym(exp_arg_typ) + if param_typ_sym.kind == .struct_ && got_arg_typ !in [ast.voidptr_type, ast.nil_type] + && !c.check_multiple_ptr_match(got_arg_typ, param.typ, param, arg) { + got_typ_str, expected_typ_str := c.get_string_names_of(got_arg_typ, param.typ) + c.error('cannot use `$got_typ_str` as `$expected_typ_str` in argument ${i + 1} to `$method_name`', + arg.pos) + } } if method.is_unsafe && !c.inside_unsafe { c.warn('method `${left_sym.name}.$method_name` must be called from an `unsafe` block', diff --git a/vlib/v/checker/tests/fn_call_arg_ptr_mismatch_err.out b/vlib/v/checker/tests/fn_call_arg_ptr_mismatch_err.out new file mode 100644 index 0000000000..5cdf306c7a --- /dev/null +++ b/vlib/v/checker/tests/fn_call_arg_ptr_mismatch_err.out @@ -0,0 +1,20 @@ +vlib/v/checker/tests/fn_call_arg_ptr_mismatch_err.vv:31:29: error: cannot use `&&SomethingImplemented` as `&ISomething` in argument 1 to `get_value_implemented` + 29 | } + 30 | // fn call + 31 | _ := get_value_implemented(&s1) + | ~~~ + 32 | // method call + 33 | _ := s1.get_value_implemented(&s1) +vlib/v/checker/tests/fn_call_arg_ptr_mismatch_err.vv:33:32: error: cannot use `&&SomethingImplemented` as `&ISomething` in argument 1 to `get_value_implemented` + 31 | _ := get_value_implemented(&s1) + 32 | // method call + 33 | _ := s1.get_value_implemented(&s1) + | ~~~ + 34 | + 35 | s2 := &SomethingNotImplemented{ +vlib/v/checker/tests/fn_call_arg_ptr_mismatch_err.vv:39:33: error: cannot use `&&SomethingNotImplemented` as `&SomethingNotImplemented` in argument 1 to `get_value_not_implemented` + 37 | } + 38 | // fn call + 39 | _ := get_value_not_implemented(&s2) + | ~~~ + 40 | } diff --git a/vlib/v/checker/tests/fn_call_arg_ptr_mismatch_err.vv b/vlib/v/checker/tests/fn_call_arg_ptr_mismatch_err.vv new file mode 100644 index 0000000000..dd9147f838 --- /dev/null +++ b/vlib/v/checker/tests/fn_call_arg_ptr_mismatch_err.vv @@ -0,0 +1,40 @@ +interface ISomething { + value int + get_value_implemented(s &ISomething) int +} + +struct SomethingImplemented { + value int +} + +fn (s SomethingImplemented) get_value_implemented(s_ &ISomething) int { + return s.value +} + +fn get_value_implemented(s &ISomething) int { + return s.value +} + +struct SomethingNotImplemented { + value int +} + +fn get_value_not_implemented(s &SomethingNotImplemented) int { + return s.value +} + +fn main() { + s1 := &SomethingImplemented{ + value: 1 + } + // fn call + _ := get_value_implemented(&s1) + // method call + _ := s1.get_value_implemented(&s1) + + s2 := &SomethingNotImplemented{ + value: 1 + } + // fn call + _ := get_value_not_implemented(&s2) +}