From eb65ad078d86208fbf4befb9a52c9f630ea9aec6 Mon Sep 17 00:00:00 2001 From: shadowninja55 <49539636+shadowninja55@users.noreply.github.com> Date: Sun, 18 Jul 2021 08:04:16 -0400 Subject: [PATCH] checker: prohibit passing non-lvalue as `voidptr` (#10838) --- .gitignore | 1 + vlib/v/ast/types.v | 5 +++++ vlib/v/checker/checker.v | 13 +++++++++++++ vlib/v/checker/tests/non_lvalue_as_voidptr.out | 5 +++++ vlib/v/checker/tests/non_lvalue_as_voidptr.vv | 5 +++++ .../tests/passing_expr_to_fn_expecting_voidptr.out | 5 +++++ .../tests/passing_expr_to_fn_expecting_voidptr.vv | 3 +++ 7 files changed, 37 insertions(+) create mode 100644 vlib/v/checker/tests/non_lvalue_as_voidptr.out create mode 100644 vlib/v/checker/tests/non_lvalue_as_voidptr.vv create mode 100644 vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.out create mode 100644 vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.vv diff --git a/.gitignore b/.gitignore index e2843c891e..fd4a7cc440 100644 --- a/.gitignore +++ b/.gitignore @@ -93,6 +93,7 @@ test.bin # ignore codespace env .venv/ +.direnv/ shell.nix default.nix flake.nix diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index af9de52afe..67aa88a22a 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -157,6 +157,11 @@ pub fn (t Type) is_ptr() bool { return (int(t) >> 16) & 0xff > 0 } +[inline] +pub fn (t Type) is_any_kind_of_pointer() bool { + return (int(t) >> 16) & 0xff > 0 || (u16(t) & 0xffff) in ast.pointer_type_idxs +} + // set nr_muls on `t` and return it [inline] pub fn (t Type) set_nr_muls(nr_muls int) Type { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 60c81db30f..213451f79f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2825,6 +2825,19 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type { if func.is_variadic && param_typ_sym.info is ast.Array { final_param_sym = c.table.get_type_symbol(param_typ_sym.array_info().elem_type) } + // NB: Casting to voidptr is used as an escape mechanism, so: + // 1. allow passing *explicit* voidptr (native or through cast) to functions + // expecting voidptr or ...voidptr + // ... but 2. disallow passing non-pointers - that is very rarely what the user wanted, + // it can lead to codegen errors (except for 'magic' functions like `json.encode` that, + // the compiler has special codegen support for), so it should be opt in, that is it + // shoould require an explicit voidptr(x) cast (and probably unsafe{} ?) . + if call_arg.typ != param.typ + && (param.typ == ast.voidptr_type || final_param_sym.idx == ast.voidptr_type_idx) + && !call_arg.typ.is_any_kind_of_pointer() && func.language == .v + && !call_arg.expr.is_lvalue() && func.name != 'json.encode' { + c.error('expression cannot be passed as `voidptr`', call_arg.expr.position()) + } // Handle expected interface if final_param_sym.kind == .interface_ { if c.type_implements(typ, param.typ, call_arg.expr.position()) { diff --git a/vlib/v/checker/tests/non_lvalue_as_voidptr.out b/vlib/v/checker/tests/non_lvalue_as_voidptr.out new file mode 100644 index 0000000000..a885eb7f2e --- /dev/null +++ b/vlib/v/checker/tests/non_lvalue_as_voidptr.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/non_lvalue_as_voidptr.vv:5:13: error: expression cannot be passed as `voidptr` + 3 | } + 4 | + 5 | println(add(5, 10)) + | ^ diff --git a/vlib/v/checker/tests/non_lvalue_as_voidptr.vv b/vlib/v/checker/tests/non_lvalue_as_voidptr.vv new file mode 100644 index 0000000000..55de9f3172 --- /dev/null +++ b/vlib/v/checker/tests/non_lvalue_as_voidptr.vv @@ -0,0 +1,5 @@ +fn add(a voidptr, b voidptr) int { + return int(a) + int(b) +} + +println(add(5, 10)) diff --git a/vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.out b/vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.out new file mode 100644 index 0000000000..1acbdb5e34 --- /dev/null +++ b/vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.vv:3:31: error: expression cannot be passed as `voidptr` + 1 | import strconv + 2 | + 3 | strconv.v_printf('%02.02f\n', 1.1) + | ~~~ diff --git a/vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.vv b/vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.vv new file mode 100644 index 0000000000..1b3bc39885 --- /dev/null +++ b/vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.vv @@ -0,0 +1,3 @@ +import strconv + +strconv.v_printf('%02.02f\n', 1.1)