checker: prohibit passing non-lvalue as `voidptr` (#10838)

pull/10842/head
shadowninja55 2021-07-18 08:04:16 -04:00 committed by GitHub
parent 1a6a7a678a
commit eb65ad078d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 37 additions and 0 deletions

1
.gitignore vendored
View File

@ -93,6 +93,7 @@ test.bin
# ignore codespace env # ignore codespace env
.venv/ .venv/
.direnv/
shell.nix shell.nix
default.nix default.nix
flake.nix flake.nix

View File

@ -157,6 +157,11 @@ pub fn (t Type) is_ptr() bool {
return (int(t) >> 16) & 0xff > 0 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 // set nr_muls on `t` and return it
[inline] [inline]
pub fn (t Type) set_nr_muls(nr_muls int) Type { pub fn (t Type) set_nr_muls(nr_muls int) Type {

View File

@ -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 { 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) 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 // Handle expected interface
if final_param_sym.kind == .interface_ { if final_param_sym.kind == .interface_ {
if c.type_implements(typ, param.typ, call_arg.expr.position()) { if c.type_implements(typ, param.typ, call_arg.expr.position()) {

View File

@ -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))
| ^

View File

@ -0,0 +1,5 @@
fn add(a voidptr, b voidptr) int {
return int(a) + int(b)
}
println(add(5, 10))

View File

@ -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)
| ~~~

View File

@ -0,0 +1,3 @@
import strconv
strconv.v_printf('%02.02f\n', 1.1)