checker: add fntype casting validations (#23872)

This commit is contained in:
Felipe Pena 2025-03-13 10:38:12 -03:00 committed by GitHub
parent e006b65944
commit 734fde89e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 121 additions and 2 deletions

View File

@ -14,7 +14,7 @@ type FN_NTSuspendResume = fn (voidptr) u64
fn ntdll_fn(name &char) FN_NTSuspendResume {
ntdll := C.GetModuleHandleA(c'NTDLL')
if ntdll == 0 {
return FN_NTSuspendResume(0)
return unsafe { FN_NTSuspendResume(0) }
}
the_fn := FN_NTSuspendResume(C.GetProcAddress(ntdll, voidptr(name)))
return the_fn

View File

@ -3593,6 +3593,25 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
|| c.file.is_translated) && !c.check_matching_function_symbols(final_from_sym, final_to_sym) {
c.error('casting a function value from one function signature, to another function signature, should be done inside `unsafe{}` blocks',
node.pos)
} else if final_to_sym.kind == .function && final_from_sym.kind != .function {
if to_type.has_flag(.option) && node.expr !is ast.None {
c.error('casting number to Option function is not allowed, only compatible function or `none`',
node.pos)
} else if !(c.inside_unsafe || c.file.is_translated) {
if node.expr is ast.IntegerLiteral {
c.warn('casting number to function value should be done inside `unsafe{}` blocks',
node.pos)
} else if node.expr is ast.Nil {
c.warn('casting `nil` to function value should be done inside `unsafe{}` blocks',
node.pos)
} else if node.expr is ast.None {
if from_type.has_flag(.option) {
c.warn('cannot pass `none` to a non Option function type', node.pos)
}
} else if final_from_sym.kind != .voidptr {
c.error('invalid casting value to function', node.pos)
}
}
}
if to_type.is_ptr() && to_sym.kind == .alias && from_sym.kind == .map {
c.error('cannot cast to alias pointer `${c.table.type_to_str(to_type)}` because `${c.table.type_to_str(from_type)}` is a value',

View File

@ -0,0 +1,69 @@
vlib/v/checker/tests/cast_fn_err.vv:24:7: warning: casting `nil` to function value should be done inside `unsafe{}` blocks
22 | // wrong ones
23 | _ := FnType(foo)
24 | _ := FnType(nil)
| ~~~~~~~~~~~
25 | _ := FnType(0)
26 | _ := FnType('foo')
vlib/v/checker/tests/cast_fn_err.vv:25:7: warning: casting number to function value should be done inside `unsafe{}` blocks
23 | _ := FnType(foo)
24 | _ := FnType(nil)
25 | _ := FnType(0)
| ~~~~~~~~~
26 | _ := FnType('foo')
27 | _ := FnType(none)
vlib/v/checker/tests/cast_fn_err.vv:23:7: error: casting a function value from one function signature, to another function signature, should be done inside `unsafe{}` blocks
21 |
22 | // wrong ones
23 | _ := FnType(foo)
| ~~~~~~~~~~~
24 | _ := FnType(nil)
25 | _ := FnType(0)
vlib/v/checker/tests/cast_fn_err.vv:24:14: error: `nil` is only allowed in `unsafe` code
22 | // wrong ones
23 | _ := FnType(foo)
24 | _ := FnType(nil)
| ~~~
25 | _ := FnType(0)
26 | _ := FnType('foo')
vlib/v/checker/tests/cast_fn_err.vv:26:7: error: invalid casting value to function
24 | _ := FnType(nil)
25 | _ := FnType(0)
26 | _ := FnType('foo')
| ~~~~~~~~~~~~~
27 | _ := FnType(none)
28 | _ := ?FnType(0)
vlib/v/checker/tests/cast_fn_err.vv:27:7: error: cannot cast `none` to `fn () bool`
25 | _ := FnType(0)
26 | _ := FnType('foo')
27 | _ := FnType(none)
| ~~~~~~~~~~~~
28 | _ := ?FnType(0)
29 | _ := ?FnType(nil)
vlib/v/checker/tests/cast_fn_err.vv:28:8: error: casting number to Option function is not allowed, only compatible function or `none`
26 | _ := FnType('foo')
27 | _ := FnType(none)
28 | _ := ?FnType(0)
| ~~~~~~~~~
29 | _ := ?FnType(nil)
30 | _ := ?FnType(foo)
vlib/v/checker/tests/cast_fn_err.vv:29:15: error: `nil` is only allowed in `unsafe` code
27 | _ := FnType(none)
28 | _ := ?FnType(0)
29 | _ := ?FnType(nil)
| ~~~
30 | _ := ?FnType(foo)
31 | }
vlib/v/checker/tests/cast_fn_err.vv:29:8: error: casting number to Option function is not allowed, only compatible function or `none`
27 | _ := FnType(none)
28 | _ := ?FnType(0)
29 | _ := ?FnType(nil)
| ~~~~~~~~~~~
30 | _ := ?FnType(foo)
31 | }
vlib/v/checker/tests/cast_fn_err.vv:30:8: error: casting a function value from one function signature, to another function signature, should be done inside `unsafe{}` blocks
28 | _ := ?FnType(0)
29 | _ := ?FnType(nil)
30 | _ := ?FnType(foo)
| ~~~~~~~~~~~
31 | }

View File

@ -0,0 +1,31 @@
type FnType = fn () bool
fn foo() int {
return 0
}
fn bar() bool {
return true
}
fn main() {
// acceptable ones
_ := unsafe { FnType(nil) }
_ := unsafe { FnType(0) }
_ := unsafe { FnType(foo) }
_ := unsafe { FnType(bar) }
_ := FnType(bar)
_ := ?FnType(none)
_ := ?FnType(bar)
_ := unsafe { ?FnType(foo) }
// wrong ones
_ := FnType(foo)
_ := FnType(nil)
_ := FnType(0)
_ := FnType('foo')
_ := FnType(none)
_ := ?FnType(0)
_ := ?FnType(nil)
_ := ?FnType(foo)
}

View File

@ -16,7 +16,7 @@ pub:
fn window_resized(mut w &Window) {
window_width, window_height := 200, 100
if w.resize_fn != WindowResizeFn(0) {
if w.resize_fn != unsafe { WindowResizeFn(0) } {
println('fn present ${window_width} ${window_height}')
w.resize_fn(w, window_width, window_height)
}