From 868908b80dccc59aca9b65b0ca7b68e11a21f3e9 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Fri, 6 Jan 2023 09:28:01 +0300 Subject: [PATCH] checker: require unsafe for Struct(voidptr) casts --- cmd/tools/modules/testing/common.v | 4 ++-- examples/sokol/drawing.v | 3 +-- examples/sokol/fonts.v | 3 +-- examples/sokol/freetype_raven.v | 6 ++---- examples/sokol/particles/particles.v | 9 +++------ examples/term.ui/cursor_chaser.v | 8 ++------ examples/term.ui/event_viewer.v | 3 +-- examples/term.ui/pong.v | 12 ++++-------- examples/term.ui/rectangles.v | 6 ++---- examples/term.ui/term_drawing.v | 6 ++---- examples/term.ui/text_editor.v | 11 ++++------- examples/term.ui/vyper.v | 9 +++------ vlib/builtin/string_interpolation.v | 3 +-- vlib/gg/image.c.v | 1 - vlib/mysql/stmt.c.v | 4 ++-- vlib/os/process_windows.c.v | 16 ++++++++-------- vlib/sync/pool/pool.v | 6 +++--- vlib/v/ast/table.v | 2 +- vlib/v/checker/checker.v | 4 ++++ vlib/v/checker/comptime.v | 2 +- vlib/v/checker/for.v | 3 ++- vlib/v/checker/tests/for_in_mut_val_type.out | 2 +- vlib/v/embed_file/embed_file.v | 14 +++++++------- vlib/v/gen/c/cgen.v | 2 +- vlib/v/markused/walker.v | 8 ++++++-- 25 files changed, 64 insertions(+), 83 deletions(-) diff --git a/cmd/tools/modules/testing/common.v b/cmd/tools/modules/testing/common.v index 62eb196ad..21fb4acf2 100644 --- a/cmd/tools/modules/testing/common.v +++ b/cmd/tools/modules/testing/common.v @@ -376,7 +376,7 @@ pub fn (mut ts TestSession) test() { } fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr { - mut ts := &TestSession(p.get_shared_context()) + mut ts := unsafe { &TestSession(p.get_shared_context()) } if ts.fail_fast { if ts.failed_cmds.len > 0 { return pool.no_result @@ -384,7 +384,7 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr { } tmpd := ts.vtmp_dir // tls_bench is used to format the step messages/timings - mut tls_bench := &benchmark.Benchmark(p.get_thread_context(idx)) + mut tls_bench := unsafe { &benchmark.Benchmark(p.get_thread_context(idx)) } if isnil(tls_bench) { tls_bench = benchmark.new_benchmark_pointer() tls_bench.set_total_expected_steps(ts.benchmark.nexpected_steps) diff --git a/examples/sokol/drawing.v b/examples/sokol/drawing.v index 836ab5433..02d0f4744 100644 --- a/examples/sokol/drawing.v +++ b/examples/sokol/drawing.v @@ -35,9 +35,8 @@ fn init(user_data voidptr) { sgl.setup(&sgl_desc) } -fn frame(user_data voidptr) { +fn frame(state &AppState) { // println('frame') - state := &AppState(user_data) draw() gfx.begin_default_pass(&state.pass_action, sapp.width(), sapp.height()) sgl.draw() diff --git a/examples/sokol/fonts.v b/examples/sokol/fonts.v index 2487e8137..05e562ed2 100644 --- a/examples/sokol/fonts.v +++ b/examples/sokol/fonts.v @@ -55,8 +55,7 @@ fn init(mut state AppState) { } } -fn frame(user_data voidptr) { - mut state := &AppState(user_data) +fn frame(mut state AppState) { state.render_font() gfx.begin_default_pass(&state.pass_action, sapp.width(), sapp.height()) sgl.draw() diff --git a/examples/sokol/freetype_raven.v b/examples/sokol/freetype_raven.v index b7aedbc08..d523d89b8 100644 --- a/examples/sokol/freetype_raven.v +++ b/examples/sokol/freetype_raven.v @@ -91,8 +91,7 @@ fn main() { sapp.run(&desc) } -fn init(user_data voidptr) { - mut state := &AppState(user_data) +fn init(mut state AppState) { desc := sapp.create_desc() gfx.setup(&desc) s := &sgl.Desc{} @@ -107,8 +106,7 @@ fn init(user_data voidptr) { } } -fn frame(user_data voidptr) { - mut state := &AppState(user_data) +fn frame(mut state AppState) { state.render_font() gfx.begin_default_pass(&state.pass_action, sapp.width(), sapp.height()) sgl.draw() diff --git a/examples/sokol/particles/particles.v b/examples/sokol/particles/particles.v index f4fc2d63b..9e2632ece 100644 --- a/examples/sokol/particles/particles.v +++ b/examples/sokol/particles/particles.v @@ -73,8 +73,7 @@ fn (a App) draw() { a.ps.draw() } -fn init(user_data voidptr) { - mut app := &App(user_data) +fn init(mut app App) { desc := sapp.create_desc() gfx.setup(&desc) sgl_desc := sgl.Desc{ @@ -96,14 +95,12 @@ fn init(user_data voidptr) { app.alpha_pip = sgl.make_pipeline(&pipdesc) } -fn cleanup(user_data voidptr) { - mut app := &App(user_data) +fn cleanup(mut app App) { app.cleanup() gfx.shutdown() } -fn frame(user_data voidptr) { - mut app := &App(user_data) +fn frame(mut app App) { app.width = sapp.width() app.height = sapp.height() t := time.ticks() diff --git a/examples/term.ui/cursor_chaser.v b/examples/term.ui/cursor_chaser.v index affb702ef..80eb16aba 100644 --- a/examples/term.ui/cursor_chaser.v +++ b/examples/term.ui/cursor_chaser.v @@ -25,9 +25,7 @@ mut: cut_rate f64 = 5 } -fn frame(x voidptr) { - mut app := &App(x) - +fn frame(mut app App) { app.tui.clear() if app.points.len > 0 { @@ -55,9 +53,7 @@ fn frame(x voidptr) { app.tui.flush() } -fn event(e &tui.Event, x voidptr) { - mut app := &App(x) - +fn event(e &tui.Event, mut app App) { match e.typ { .key_down { match e.code { diff --git a/examples/term.ui/event_viewer.v b/examples/term.ui/event_viewer.v index 77304e935..16e604e56 100644 --- a/examples/term.ui/event_viewer.v +++ b/examples/term.ui/event_viewer.v @@ -5,8 +5,7 @@ mut: tui &tui.Context = unsafe { nil } } -fn event(e &tui.Event, x voidptr) { - mut app := &App(x) +fn event(e &tui.Event, mut app App) { app.tui.clear() app.tui.set_cursor_position(0, 0) app.tui.write('V term.input event viewer (press `esc` to exit)\n\n') diff --git a/examples/term.ui/pong.v b/examples/term.ui/pong.v index cf576ddb4..86e84d4d7 100644 --- a/examples/term.ui/pong.v +++ b/examples/term.ui/pong.v @@ -458,18 +458,15 @@ fn (mut g Game) free() { } // TODO Remove these wrapper functions when we can assign methods as callbacks -fn init(x voidptr) { - mut app := &App(x) +fn init(mut app App) { app.init() } -fn frame(x voidptr) { - mut app := &App(x) +fn frame(mut app App) { app.frame() } -fn cleanup(x voidptr) { - mut app := &App(x) +fn cleanup(mut app App) { unsafe { app.free() } @@ -479,8 +476,7 @@ fn fail(error string) { eprintln(error) } -fn event(e &ui.Event, x voidptr) { - mut app := &App(x) +fn event(e &ui.Event, mut app App) { app.event(e) } diff --git a/examples/term.ui/rectangles.v b/examples/term.ui/rectangles.v index 97da3b4bf..541ca9183 100644 --- a/examples/term.ui/rectangles.v +++ b/examples/term.ui/rectangles.v @@ -27,8 +27,7 @@ fn random_color() tui.Color { } } -fn event(e &tui.Event, x voidptr) { - mut app := &App(x) +fn event(e &tui.Event, mut app App) { match e.typ { .mouse_down { app.is_drag = true @@ -60,8 +59,7 @@ fn event(e &tui.Event, x voidptr) { app.redraw = true } -fn frame(x voidptr) { - mut app := &App(x) +fn frame(mut app App) { if !app.redraw { return } diff --git a/examples/term.ui/term_drawing.v b/examples/term.ui/term_drawing.v index 55a65e4eb..afd6773b0 100644 --- a/examples/term.ui/term_drawing.v +++ b/examples/term.ui/term_drawing.v @@ -128,8 +128,7 @@ fn main() { app.ui.run()? } -fn frame(x voidptr) { - mut app := &App(x) +fn frame(mut app App) { mut redraw := app.should_redraw if app.msg != '' && app.ui.frame_count >= app.msg_hide_tick { app.msg = '' @@ -141,8 +140,7 @@ fn frame(x voidptr) { } } -fn event(event &ui.Event, x voidptr) { - mut app := &App(x) +fn event(event &ui.Event, mut app App) { match event.typ { .mouse_down { app.is_dragging = true diff --git a/examples/term.ui/text_editor.v b/examples/term.ui/text_editor.v index 7b999f2f5..e19aafb9c 100644 --- a/examples/term.ui/text_editor.v +++ b/examples/term.ui/text_editor.v @@ -473,9 +473,8 @@ fn (c Cursor) xy() (int, int) { } // App callbacks -fn init(x voidptr) { - mut a := &App(x) - a.init_file() +fn init(mut app App) { + app.init_file() } fn (mut a App) init_file() { @@ -523,8 +522,7 @@ fn (mut a App) magnet_cursor_x() { } } -fn frame(x voidptr) { - mut a := &App(x) +fn frame(mut a App) { mut ed := a.ed a.tui.clear() scroll_limit := a.view_height() @@ -551,8 +549,7 @@ fn frame(x voidptr) { a.tui.flush() } -fn event(e &tui.Event, x voidptr) { - mut a := &App(x) +fn event(e &tui.Event, mut a App) { mut buffer := a.ed if e.typ == .key_down { match e.code { diff --git a/examples/term.ui/vyper.v b/examples/term.ui/vyper.v index 4f5da524c..cba2e935a 100644 --- a/examples/term.ui/vyper.v +++ b/examples/term.ui/vyper.v @@ -286,8 +286,7 @@ fn (mut a App) new_game() { } // initialize the app and record the width and height of the window -fn init(x voidptr) { - mut app := &App(x) +fn init(mut app App) { w, h := app.termui.window_width, app.termui.window_height app.width = w app.height = h @@ -295,8 +294,7 @@ fn init(x voidptr) { } // event handles different events for the app as they occur -fn event(e &termui.Event, x voidptr) { - mut app := &App(x) +fn event(e &termui.Event, mut app App) { match e.typ { .mouse_down {} .mouse_drag {} @@ -324,8 +322,7 @@ fn event(e &termui.Event, x voidptr) { } // frame perform actions on every tick -fn frame(x voidptr) { - mut app := &App(x) +fn frame(mut app App) { app.update() app.draw() } diff --git a/vlib/builtin/string_interpolation.v b/vlib/builtin/string_interpolation.v index 00995d175..c980263e1 100644 --- a/vlib/builtin/string_interpolation.v +++ b/vlib/builtin/string_interpolation.v @@ -656,9 +656,8 @@ pub: // interpolation function [direct_array_access; manualfree] -pub fn str_intp(data_len int, in_data voidptr) string { +pub fn str_intp(data_len int, input_base &StrIntpData) string { mut res := strings.new_builder(256) - input_base := &StrIntpData(in_data) for i := 0; i < data_len; i++ { data := unsafe { &input_base[i] } // avoid empty strings diff --git a/vlib/gg/image.c.v b/vlib/gg/image.c.v index 5b4aada1b..f5f421766 100644 --- a/vlib/gg/image.c.v +++ b/vlib/gg/image.c.v @@ -285,7 +285,6 @@ pub fn (ctx &Context) draw_image_with_config(config DrawImageConfig) { y := config.img_rect.y C.darwin_draw_image(x, ctx.height - (y + config.img_rect.height), config.img_rect.width, config.img_rect.height, img) - println('ok') return } } diff --git a/vlib/mysql/stmt.c.v b/vlib/mysql/stmt.c.v index f54dc457f..1306acbec 100644 --- a/vlib/mysql/stmt.c.v +++ b/vlib/mysql/stmt.c.v @@ -86,7 +86,7 @@ pub fn (stmt Stmt) prepare() ! { } pub fn (stmt Stmt) bind_params() ! { - res := C.mysql_stmt_bind_param(stmt.stmt, &C.MYSQL_BIND(stmt.binds.data)) + res := C.mysql_stmt_bind_param(stmt.stmt, unsafe { &C.MYSQL_BIND(stmt.binds.data) }) if res && stmt.get_error_msg() != '' { return stmt.error(1) } @@ -223,7 +223,7 @@ pub fn (mut stmt Stmt) bind_res(fields &C.MYSQL_FIELD, dataptr []&u8, lens []u32 } pub fn (mut stmt Stmt) bind_result_buffer() ! { - res := C.mysql_stmt_bind_result(stmt.stmt, &C.MYSQL_BIND(stmt.res.data)) + res := C.mysql_stmt_bind_result(stmt.stmt, unsafe { &C.MYSQL_BIND(stmt.res.data) }) if res && stmt.get_error_msg() != '' { return stmt.error(1) } diff --git a/vlib/os/process_windows.c.v b/vlib/os/process_windows.c.v index 85cb66af7..6f894e321 100644 --- a/vlib/os/process_windows.c.v +++ b/vlib/os/process_windows.c.v @@ -117,7 +117,7 @@ fn (mut p Process) win_stop_process() { if voidptr(the_fn) == 0 { return } - wdata := &WProcess(p.wdata) + wdata := unsafe { &WProcess(p.wdata) } the_fn(wdata.proc_info.h_process) } @@ -126,17 +126,17 @@ fn (mut p Process) win_resume_process() { if voidptr(the_fn) == 0 { return } - wdata := &WProcess(p.wdata) + wdata := unsafe { &WProcess(p.wdata) } the_fn(wdata.proc_info.h_process) } fn (mut p Process) win_kill_process() { - wdata := &WProcess(p.wdata) + wdata := unsafe { &WProcess(p.wdata) } C.TerminateProcess(wdata.proc_info.h_process, 3) } fn (mut p Process) win_kill_pgroup() { - wdata := &WProcess(p.wdata) + wdata := unsafe { &WProcess(p.wdata) } C.GenerateConsoleCtrlEvent(C.CTRL_BREAK_EVENT, wdata.proc_info.dw_process_id) C.Sleep(20) C.TerminateProcess(wdata.proc_info.h_process, 3) @@ -144,7 +144,7 @@ fn (mut p Process) win_kill_pgroup() { fn (mut p Process) win_wait() { exit_code := u32(1) - mut wdata := &WProcess(p.wdata) + mut wdata := unsafe { &WProcess(p.wdata) } if p.wdata != 0 { C.WaitForSingleObject(wdata.proc_info.h_process, C.INFINITE) C.GetExitCodeProcess(wdata.proc_info.h_process, voidptr(&exit_code)) @@ -160,7 +160,7 @@ fn (mut p Process) win_wait() { fn (mut p Process) win_is_alive() bool { exit_code := u32(0) - wdata := &WProcess(p.wdata) + wdata := unsafe { &WProcess(p.wdata) } C.GetExitCodeProcess(wdata.proc_info.h_process, voidptr(&exit_code)) if exit_code == C.STILL_ACTIVE { return true @@ -175,7 +175,7 @@ fn (mut p Process) win_write_string(idx int, s string) { } fn (mut p Process) win_read_string(idx int, maxbytes int) (string, int) { - mut wdata := &WProcess(p.wdata) + mut wdata := unsafe { &WProcess(p.wdata) } if unsafe { wdata == 0 } { return '', 0 } @@ -207,7 +207,7 @@ fn (mut p Process) win_read_string(idx int, maxbytes int) (string, int) { } fn (mut p Process) win_slurp(idx int) string { - mut wdata := &WProcess(p.wdata) + mut wdata := unsafe { &WProcess(p.wdata) } if unsafe { wdata == 0 } { return '' } diff --git a/vlib/sync/pool/pool.v b/vlib/sync/pool/pool.v index 966b67404..028a9e6be 100644 --- a/vlib/sync/pool/pool.v +++ b/vlib/sync/pool/pool.v @@ -118,13 +118,13 @@ fn process_in_thread(mut pool PoolProcessor, task_id int) { // get_item - called by the worker callback. // Retrieves a type safe instance of the currently processed item pub fn (pool &PoolProcessor) get_item[T](idx int) T { - return *(&T(pool.items[idx])) + return unsafe { *(&T(pool.items[idx])) } } // get_result - called by the main thread to get a specific result. // Retrieves a type safe instance of the produced result. pub fn (pool &PoolProcessor) get_result[T](idx int) T { - return *(&T(pool.results[idx])) + return unsafe { *(&T(pool.results[idx])) } } // get_results - get a list of type safe results in the main thread. @@ -140,7 +140,7 @@ pub fn (pool &PoolProcessor) get_results[T]() []T { pub fn (pool &PoolProcessor) get_results_ref[T]() []&T { mut res := []&T{cap: pool.results.len} for i in 0 .. pool.results.len { - res << &T(pool.results[i]) + res << unsafe { &T(pool.results[i]) } } return res } diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index ce0fe60a6..81f820294 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -116,7 +116,7 @@ pub mut: receiver_type Type // != 0, when .is_method == true name string params []Param - source_fn voidptr // set in the checker, while processing fn declarations + source_fn voidptr // set in the checker, while processing fn declarations // TODO get rid of voidptr usages int generic_names []string dep_names []string // globals or consts dependent names diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 1e39b6d44..aeac5345f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2695,6 +2695,10 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type { if from_sym.kind == .alias { from_type = (from_sym.info as ast.Alias).parent_type.derive_add_muls(from_type) } + if from_type == ast.voidptr_type_idx && !c.inside_unsafe { + // TODO make this an error + c.warn('cannot cast voidptr to a struct outside `unsafe`', node.pos) + } if !from_type.is_int() && final_from_sym.kind != .enum_ && !from_type.is_pointer() && !from_type.is_ptr() { ft := c.table.type_to_str(from_type) diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 0370e0ab1..9f551f21a 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -421,7 +421,7 @@ fn (mut c Checker) verify_all_vweb_routes() { if m.return_type == typ_vweb_result { is_ok, nroute_attributes, nargs := c.verify_vweb_params_for_method(m) if !is_ok { - f := &ast.FnDecl(m.source_fn) + f := unsafe { &ast.FnDecl(m.source_fn) } if f == unsafe { nil } { continue } diff --git a/vlib/v/checker/for.v b/vlib/v/checker/for.v index 939a1fffa..e96e5d3c5 100644 --- a/vlib/v/checker/for.v +++ b/vlib/v/checker/for.v @@ -180,7 +180,8 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) { root_ident := node.cond.root_ident() or { node.cond.expr as ast.Ident } if root_ident.kind != .unresolved { if !(root_ident.obj as ast.Var).is_mut { - c.error('field `${node.cond.field_name}` is immutable, it cannot be changed', + sym2 := c.table.sym(root_ident.obj.typ) + c.error('field `${sym2.name}.${node.cond.field_name}` is immutable, it cannot be changed', node.cond.pos) } } diff --git a/vlib/v/checker/tests/for_in_mut_val_type.out b/vlib/v/checker/tests/for_in_mut_val_type.out index 935df54be..42542cf1e 100644 --- a/vlib/v/checker/tests/for_in_mut_val_type.out +++ b/vlib/v/checker/tests/for_in_mut_val_type.out @@ -40,7 +40,7 @@ vlib/v/checker/tests/for_in_mut_val_type.vv:20:18: error: map literal is immutab | ~~~~~~~~~~~~~~~~~~ 21 | j *= 2 22 | } -vlib/v/checker/tests/for_in_mut_val_type.vv:30:17: error: field `a` is immutable, it cannot be changed +vlib/v/checker/tests/for_in_mut_val_type.vv:30:17: error: field `Test.a` is immutable, it cannot be changed 28 | 29 | fn foo(t Test) { 30 | for mut e in t.a { diff --git a/vlib/v/embed_file/embed_file.v b/vlib/v/embed_file/embed_file.v index 73c49cbd7..57c34eafb 100644 --- a/vlib/v/embed_file/embed_file.v +++ b/vlib/v/embed_file/embed_file.v @@ -104,14 +104,14 @@ pub struct EmbedFileIndexEntry { // find_index_entry_by_path is used internally by the V compiler: pub fn find_index_entry_by_path(start voidptr, path string, algo string) &EmbedFileIndexEntry { - mut x := &EmbedFileIndexEntry(start) - for x.id >= 0 && x.data != 0 && (x.algo != algo || x.path != path) { - unsafe { + unsafe { + mut x := &EmbedFileIndexEntry(start) + for x.id >= 0 && x.data != 0 && (x.algo != algo || x.path != path) { x++ } + $if trace_embed_file ? { + eprintln('>> v.embed_file find_index_entry_by_path ${ptr_str(start)}, id: ${x.id}, path: "${path}", algo: "${algo}" => ${ptr_str(x)}') + } + return x } - $if trace_embed_file ? { - eprintln('>> v.embed_file find_index_entry_by_path ${ptr_str(start)}, id: ${x.id}, path: "${path}", algo: "${algo}" => ${ptr_str(x)}') - } - return x } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 373e9a751..6cbeafe00 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -613,7 +613,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) (string, fn cgen_process_one_file_cb(mut p pool.PoolProcessor, idx int, wid int) &Gen { file := p.get_item[&ast.File](idx) - mut global_g := &Gen(p.get_shared_context()) + mut global_g := unsafe { &Gen(p.get_shared_context()) } mut g := &Gen{ file: file out: strings.new_builder(512000) diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index 3647c8957..55f80c41b 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -152,7 +152,9 @@ pub fn (mut w Walker) stmt(node_ ast.Stmt) { // the .next() method of the struct will be used for iteration: cond_type_sym := w.table.sym(node.cond_type) if next_fn := cond_type_sym.find_method('next') { - w.fn_decl(mut &ast.FnDecl(next_fn.source_fn)) + unsafe { + w.fn_decl(mut &ast.FnDecl(next_fn.source_fn)) + } } } } @@ -303,7 +305,9 @@ fn (mut w Walker) expr(node_ ast.Expr) { sym := w.table.sym(node.left_type) if sym.kind == .struct_ { if opmethod := sym.find_method(node.op.str()) { - w.fn_decl(mut &ast.FnDecl(opmethod.source_fn)) + unsafe { + w.fn_decl(mut &ast.FnDecl(opmethod.source_fn)) + } } } if node.right_type == 0 { -- 2.30.2