From 3c0a368af358aaa378554722b6e2994c442abb97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kr=C3=BCger?= <45282134+UweKrueger@users.noreply.github.com> Date: Sun, 25 Apr 2021 20:40:38 +0200 Subject: [PATCH] all: automatically move (some) referenced objects to heap (#9873) --- examples/sokol/02_cubes_glsl/cube_glsl.v | 2 +- .../sokol/03_march_tracing_glsl/rt_glsl.v | 2 +- examples/sokol/04_multi_shader_glsl/rt_glsl.v | 4 +- examples/sokol/05_instancing_glsl/rt_glsl.v | 6 +- examples/sokol/06_obj_viewer/obj/rend.v | 2 +- examples/sokol/06_obj_viewer/show_obj.v | 4 +- examples/term.ui/pong.v | 6 +- examples/ttf_font/example_ttf.v | 6 +- vlib/cli/help.v | 2 +- vlib/context/cancel.v | 2 +- vlib/net/http/backend_nix.c.v | 2 +- vlib/sync/channels.v | 10 +-- vlib/term/ui/input_nix.c.v | 2 +- vlib/v/ast/ast.v | 7 +- vlib/v/ast/table.v | 1 + vlib/v/checker/checker.v | 85 ++++++++++++++++++- .../tests/function_missing_return_type.out | 7 ++ vlib/v/fmt/fmt.v | 2 +- vlib/v/gen/c/cgen.v | 68 +++++++++++++-- vlib/v/gen/c/fn.v | 4 +- vlib/v/gen/js/js.v | 6 +- vlib/v/parser/assign.v | 1 + vlib/v/parser/fn.v | 34 ++++++++ vlib/v/parser/for.v | 3 + vlib/v/parser/if_match.v | 1 + vlib/v/parser/parser.v | 1 + vlib/v/parser/pratt.v | 2 + .../mut_receiver_returned_as_reference_test.v | 2 +- vlib/v/tests/mut_test.v | 8 +- vlib/v/tests/ptr_arithmetic_test.v | 6 +- vlib/v/tests/reference_return_test.v | 25 ++++++ vlib/v/tests/type_voidptr_test.v | 2 +- 32 files changed, 264 insertions(+), 51 deletions(-) create mode 100644 vlib/v/tests/reference_return_test.v diff --git a/examples/sokol/02_cubes_glsl/cube_glsl.v b/examples/sokol/02_cubes_glsl/cube_glsl.v index 2c4289d211..ffdb4ae206 100644 --- a/examples/sokol/02_cubes_glsl/cube_glsl.v +++ b/examples/sokol/02_cubes_glsl/cube_glsl.v @@ -400,7 +400,7 @@ fn draw_cube_glsl(app App) { 0 /* padding 4 Bytes == 1 f32 */, ]! fs_uniforms_range := C.sg_range{ - ptr: &text_res + ptr: unsafe { &text_res } size: size_t(4 * 4) } gfx.apply_uniforms(C.SG_SHADERSTAGE_FS, C.SLOT_fs_params, &fs_uniforms_range) diff --git a/examples/sokol/03_march_tracing_glsl/rt_glsl.v b/examples/sokol/03_march_tracing_glsl/rt_glsl.v index cdafec9d08..fc8ba310a0 100644 --- a/examples/sokol/03_march_tracing_glsl/rt_glsl.v +++ b/examples/sokol/03_march_tracing_glsl/rt_glsl.v @@ -302,7 +302,7 @@ fn draw_cube_glsl(app App) { 0 // padding bytes , see "fs_params" struct paddings in rt_glsl.h ]! fs_uniforms_range := C.sg_range{ - ptr: &tmp_fs_params + ptr: unsafe { &tmp_fs_params } size: size_t(sizeof(tmp_fs_params)) } gfx.apply_uniforms(C.SG_SHADERSTAGE_FS, C.SLOT_fs_params, &fs_uniforms_range) diff --git a/examples/sokol/04_multi_shader_glsl/rt_glsl.v b/examples/sokol/04_multi_shader_glsl/rt_glsl.v index 8299831f43..bb6109c682 100644 --- a/examples/sokol/04_multi_shader_glsl/rt_glsl.v +++ b/examples/sokol/04_multi_shader_glsl/rt_glsl.v @@ -417,7 +417,7 @@ fn draw_cube_glsl_m(app App) { 0 // padding bytes , see "fs_params" struct paddings in rt_glsl.h ]! fs_uniforms_range := C.sg_range{ - ptr: &tmp_fs_params + ptr: unsafe { &tmp_fs_params } size: size_t(sizeof(tmp_fs_params)) } gfx.apply_uniforms(C.SG_SHADERSTAGE_FS, C.SLOT_fs_params_p, &fs_uniforms_range) @@ -469,7 +469,7 @@ fn draw_cube_glsl_p(app App) { 0 // padding bytes , see "fs_params" struct paddings in rt_glsl.h ]! fs_uniforms_range := C.sg_range{ - ptr: &tmp_fs_params + ptr: unsafe { &tmp_fs_params } size: size_t(sizeof(tmp_fs_params)) } gfx.apply_uniforms(C.SG_SHADERSTAGE_FS, C.SLOT_fs_params_p, &fs_uniforms_range) diff --git a/examples/sokol/05_instancing_glsl/rt_glsl.v b/examples/sokol/05_instancing_glsl/rt_glsl.v index 90a6163c4b..2196748571 100644 --- a/examples/sokol/05_instancing_glsl/rt_glsl.v +++ b/examples/sokol/05_instancing_glsl/rt_glsl.v @@ -331,7 +331,7 @@ fn draw_cube_glsl_i(mut app App){ app.inst_pos[index] = m4.Vec4{e:[f32((x - cx - app.camera_x) * cube_size),y ,f32( (z - cz - app.camera_z) * cube_size),spare_param]!} } range := C.sg_range{ - ptr: &app.inst_pos + ptr: unsafe { &app.inst_pos } size: size_t(num_inst * int(sizeof(m4.Vec4))) } gfx.update_buffer(app.bind['inst'].vertex_buffers[1], &range ) @@ -341,7 +341,7 @@ fn draw_cube_glsl_i(mut app App){ // passing the view matrix as uniform // res is a 4x4 matrix of f32 thus: 4*16 byte of size vs_uniforms_range := C.sg_range{ - ptr: &tr_matrix + ptr: unsafe { &tr_matrix } size: size_t(4 * 16) } gfx.apply_uniforms(C.SG_SHADERSTAGE_VS, C.SLOT_vs_params_i, &vs_uniforms_range) @@ -359,7 +359,7 @@ fn draw_cube_glsl_i(mut app App){ 0,0 // padding bytes , see "fs_params" struct paddings in rt_glsl.h ]! fs_uniforms_range := C.sg_range{ - ptr: &tmp_fs_params + ptr: unsafe { &tmp_fs_params } size: size_t(sizeof(tmp_fs_params)) } gfx.apply_uniforms(C.SG_SHADERSTAGE_FS, C.SLOT_fs_params, &fs_uniforms_range) diff --git a/examples/sokol/06_obj_viewer/obj/rend.v b/examples/sokol/06_obj_viewer/obj/rend.v index be1e9b524d..c637a552f0 100644 --- a/examples/sokol/06_obj_viewer/obj/rend.v +++ b/examples/sokol/06_obj_viewer/obj/rend.v @@ -235,7 +235,7 @@ pub fn (obj_part ObjPart) bind_and_draw(rend_data_index int, in_data Shader_data size: size_t(in_data.vs_len) } fs_uniforms_range := C.sg_range{ - ptr: &tmp_fs_params + ptr: unsafe { &tmp_fs_params } size: size_t(in_data.fs_len) } diff --git a/examples/sokol/06_obj_viewer/show_obj.v b/examples/sokol/06_obj_viewer/show_obj.v index f68e88fa0b..dbf95ac7bf 100644 --- a/examples/sokol/06_obj_viewer/show_obj.v +++ b/examples/sokol/06_obj_viewer/show_obj.v @@ -153,9 +153,9 @@ fn draw_model(app App, model_pos m4.Vec4) u32 { tmp_fs_params.ligth = m4.vec3(x_light, radius_light, z_light) sd := obj.Shader_data{ - vs_data: &tmp_vs_param + vs_data: unsafe { &tmp_vs_param } vs_len: int(sizeof(tmp_vs_param)) - fs_data: &tmp_fs_params + fs_data: unsafe { &tmp_fs_params } fs_len: int(sizeof(tmp_fs_params)) } diff --git a/examples/term.ui/pong.v b/examples/term.ui/pong.v index 170ac2d1e5..38a0020715 100644 --- a/examples/term.ui/pong.v +++ b/examples/term.ui/pong.v @@ -210,7 +210,7 @@ fn (mut p Player) update() { return } // dt := p.game.app.dt - ball := &p.game.ball + ball := unsafe { &p.game.ball } // Evil AI that eventually will take over the world p.pos.y = ball.pos.y - int(f32(p.racket_size) * 0.5) } @@ -246,7 +246,7 @@ mut: } fn (mut g Game) move_player(id int, x int, y int) { - mut p := &g.players[id] + mut p := unsafe { &g.players[id] } if p.ai { // disable AI when moved p.ai = false } @@ -288,7 +288,7 @@ fn (mut g Game) new_round() { fn (mut g Game) update() { dt := g.app.dt - mut b := &g.ball + mut b := unsafe { &g.ball } for mut p in g.players { p.update() // Keep rackets within the game area diff --git a/examples/ttf_font/example_ttf.v b/examples/ttf_font/example_ttf.v index 85eb26b00a..216440a75d 100644 --- a/examples/ttf_font/example_ttf.v +++ b/examples/ttf_font/example_ttf.v @@ -53,7 +53,7 @@ fn draw_frame(mut app App_data) { sgl.v2f(510, 400) sgl.end() // update the text - mut txt1 := &app.ttf_render[0] + mut txt1 := unsafe { &app.ttf_render[0] } if app.frame_c % 2 == 0 { txt1.destroy_texture() txt1.create_text(cframe_txt, 43) @@ -71,7 +71,7 @@ Frame: $app.frame_c But Vwill prevail for sure, V is the way!! òàèì@ò!£$%& " - txt1 = &app.ttf_render[1] + txt1 = unsafe { &app.ttf_render[1] } if app.frame_c % 2 == 0 { txt1.bmp.justify = false if (app.frame_c >> 6) % 2 == 0 { @@ -93,7 +93,7 @@ But Vwill prevail for sure, V is the way!! txt1.draw_text_bmp(app.gg, 30 + (app.frame_c >> 1) & 0xFF, 200) // draw mouse position if app.mouse_x >= 0 { - txt1 = &app.ttf_render[2] + txt1 = unsafe { &app.ttf_render[2] } txt1.destroy_texture() txt1.create_text('$app.mouse_x,$app.mouse_y', 25) txt1.create_texture() diff --git a/vlib/cli/help.v b/vlib/cli/help.v index a84385ea63..6ce3e4156b 100644 --- a/vlib/cli/help.v +++ b/vlib/cli/help.v @@ -35,7 +35,7 @@ fn print_help_for_command(help_cmd Command) ? { mut found := false for sub_cmd in cmd.commands { if sub_cmd.name == arg { - cmd = &sub_cmd + cmd = unsafe { &sub_cmd } found = true break } diff --git a/vlib/context/cancel.v b/vlib/context/cancel.v index e5aaf5f64c..023b1a8e29 100644 --- a/vlib/context/cancel.v +++ b/vlib/context/cancel.v @@ -80,7 +80,7 @@ pub fn (mut ctx CancelContext) err() IError { pub fn (ctx CancelContext) value(key string) ?voidptr { if key == cancel_context_key { - return voidptr(&ctx) + return voidptr(unsafe { &ctx }) } return ctx.context.value(key) } diff --git a/vlib/net/http/backend_nix.c.v b/vlib/net/http/backend_nix.c.v index bfcec7c57a..47631e9e1e 100644 --- a/vlib/net/http/backend_nix.c.v +++ b/vlib/net/http/backend_nix.c.v @@ -44,7 +44,7 @@ fn (req &Request) ssl_do(port int, method Method, host_name string, path string) C.BIO_puts(web, &char(req_headers.str)) mut content := strings.new_builder(100) mut buff := [bufsize]byte{} - bp := &buff[0] + bp := unsafe { &buff[0] } mut readcounter := 0 for { readcounter++ diff --git a/vlib/sync/channels.v b/vlib/sync/channels.v index 574a11e1e1..1f1e630862 100644 --- a/vlib/sync/channels.v +++ b/vlib/sync/channels.v @@ -571,19 +571,19 @@ pub fn channel_select(mut channels []&Channel, dir []Direction, mut objrefs []vo mut sem := unsafe { Semaphore{} } sem.init(0) for i, ch in channels { - subscr[i].sem = &sem + subscr[i].sem = unsafe { &sem } if dir[i] == .push { mut null16 := u16(0) for !C.atomic_compare_exchange_weak_u16(&ch.write_sub_mtx, &null16, u16(1)) { null16 = u16(0) } - subscr[i].prev = &ch.write_subscriber + subscr[i].prev = unsafe { &ch.write_subscriber } unsafe { subscr[i].nxt = C.atomic_exchange_ptr(&voidptr(&ch.write_subscriber), &subscr[i]) } if voidptr(subscr[i].nxt) != voidptr(0) { - subscr[i].nxt.prev = &subscr[i].nxt + subscr[i].nxt.prev = unsafe { &subscr[i].nxt } } C.atomic_store_u16(&ch.write_sub_mtx, u16(0)) } else { @@ -591,13 +591,13 @@ pub fn channel_select(mut channels []&Channel, dir []Direction, mut objrefs []vo for !C.atomic_compare_exchange_weak_u16(&ch.read_sub_mtx, &null16, u16(1)) { null16 = u16(0) } - subscr[i].prev = &ch.read_subscriber + subscr[i].prev = unsafe { &ch.read_subscriber } unsafe { subscr[i].nxt = C.atomic_exchange_ptr(&voidptr(&ch.read_subscriber), &subscr[i]) } if voidptr(subscr[i].nxt) != voidptr(0) { - subscr[i].nxt.prev = &subscr[i].nxt + subscr[i].nxt.prev = unsafe { &subscr[i].nxt } } C.atomic_store_u16(&ch.read_sub_mtx, u16(0)) } diff --git a/vlib/term/ui/input_nix.c.v b/vlib/term/ui/input_nix.c.v index fcd073c3e1..e806fb8c1a 100644 --- a/vlib/term/ui/input_nix.c.v +++ b/vlib/term/ui/input_nix.c.v @@ -62,7 +62,7 @@ fn (mut ctx Context) shift(len int) { // TODO: don't actually do this, lmao [inline] fn (mut ctx Context) resize_arr(size int) { - mut l := &ctx.read_buf.len + mut l := unsafe { &ctx.read_buf.len } unsafe { *l = size _ = l diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 5da5674852..dbd1707af4 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -493,8 +493,11 @@ pub mut: is_changed bool // to detect mutable vars that are never changed // // (for setting the position after the or block for autofree) - is_or bool // `x := foo() or { ... }` - is_tmp bool // for tmp for loop vars, so that autofree can skip them + is_or bool // `x := foo() or { ... }` + is_tmp bool // for tmp for loop vars, so that autofree can skip them + is_auto_heap bool // value whoes address goes out of scope + is_heap_ref bool // *known* to be pointer to heap memory (ptr to [heap] struct) + is_stack_obj bool // may be pointer to stack value (`mut` or `&` arg and not [heap] struct) } // used for smartcasting only diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index e0fd2402ba..6acb88292e 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -7,6 +7,7 @@ import v.cflag import v.token import v.util +[heap] pub struct Table { pub mut: type_symbols []TypeSymbol diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 6c09926407..b44b2f7f1c 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -61,6 +61,7 @@ pub mut: inside_const bool inside_anon_fn bool inside_ref_lit bool + inside_fn_arg bool // `a`, `b` in `a.f(b)` skip_flags bool // should `#flag` and `#include` be skipped mut: files []ast.File @@ -1318,7 +1319,10 @@ pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) ast.Type { } */ // Now call `method_call` or `fn_call` for specific checks. + old_inside_fn_arg := c.inside_fn_arg + c.inside_fn_arg = true typ := if call_expr.is_method { c.method_call(mut call_expr) } else { c.fn_call(mut call_expr) } + c.inside_fn_arg = old_inside_fn_arg // autofree: mark args that have to be freed (after saving them in tmp exprs) free_tmp_arg_vars := c.pref.autofree && !c.is_builtin_mod && call_expr.args.len > 0 && !call_expr.args[0].typ.has_flag(.optional) @@ -3564,7 +3568,7 @@ fn (mut c Checker) stmt(node ast.Stmt) { ast.DeferStmt { if node.idx_in_fn < 0 { node.idx_in_fn = c.cur_fn.defer_stmts.len - c.cur_fn.defer_stmts << &node + c.cur_fn.defer_stmts << unsafe { &node } } c.stmts(node.stmts) } @@ -4176,7 +4180,7 @@ pub fn (mut c Checker) expr(node ast.Expr) ast.Type { ast.AnonFn { c.inside_anon_fn = true keep_fn := c.cur_fn - c.cur_fn = &node.decl + c.cur_fn = unsafe { &node.decl } c.stmts(node.decl.stmts) c.fn_decl(mut node.decl) c.cur_fn = keep_fn @@ -5014,6 +5018,24 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { // node.expected_type = c.expected_type // } node.return_type = ret_type + cond_var := c.get_base_name(&node.cond) + if cond_var != '' { + mut cond_is_auto_heap := false + for branch in node.branches { + if v := branch.scope.find_var(cond_var) { + if v.is_auto_heap { + cond_is_auto_heap = true + break + } + } + } + if cond_is_auto_heap { + for branch in node.branches { + mut v := branch.scope.find_var(cond_var) or { continue } + v.is_auto_heap = true + } + } + } return ret_type } @@ -5830,6 +5852,53 @@ pub fn (mut c Checker) postfix_expr(mut node ast.PostfixExpr) ast.Type { return typ } +pub fn (mut c Checker) mark_as_referenced(mut node ast.Expr) { + match mut node { + ast.Ident { + if mut node.obj is ast.Var { + mut obj := unsafe { &node.obj } + if c.fn_scope != voidptr(0) { + obj = c.fn_scope.find_var(node.obj.name) or { unsafe { &node.obj } } + } + type_sym := c.table.get_type_symbol(obj.typ) + if obj.is_stack_obj { + c.error('`$node.name` cannot be referenced outside `unsafe` blocks as it might be stored on stack. Consider declaring `$type_sym.name` as `[heap]`.', + node.pos) + } else if type_sym.kind == .array_fixed { + c.error('cannot reference fixed array `$node.name` outside `unsafe` blocks as it is supposed to be stored on stack', + node.pos) + } else { + node.obj.is_auto_heap = true + } + } + } + ast.SelectorExpr { + c.mark_as_referenced(mut &node.expr) + } + ast.IndexExpr { + c.mark_as_referenced(mut &node.left) + } + else {} + } +} + +pub fn (mut c Checker) get_base_name(node &ast.Expr) string { + match node { + ast.Ident { + return node.name + } + ast.SelectorExpr { + return c.get_base_name(&node.expr) + } + ast.IndexExpr { + return c.get_base_name(&node.left) + } + else { + return '' + } + } +} + pub fn (mut c Checker) prefix_expr(mut node ast.PrefixExpr) ast.Type { old_inside_ref_lit := c.inside_ref_lit c.inside_ref_lit = c.inside_ref_lit || node.op == .amp @@ -5871,9 +5940,19 @@ pub fn (mut c Checker) prefix_expr(mut node ast.PrefixExpr) ast.Type { } } } + if !c.inside_fn_arg && !c.inside_unsafe { + c.mark_as_referenced(mut &node.right) + } return right_type.to_ptr() } else if node.op == .amp && node.right !is ast.CastExpr { - return right_type.to_ptr() + if !c.inside_fn_arg && !c.inside_unsafe { + c.mark_as_referenced(mut &node.right) + } + if node.right.is_auto_deref_var() { + return right_type + } else { + return right_type.to_ptr() + } } if node.op == .mul { if right_type.is_ptr() { diff --git a/vlib/v/checker/tests/function_missing_return_type.out b/vlib/v/checker/tests/function_missing_return_type.out index 73292af5ea..cf9872854b 100644 --- a/vlib/v/checker/tests/function_missing_return_type.out +++ b/vlib/v/checker/tests/function_missing_return_type.out @@ -3,6 +3,13 @@ vlib/v/checker/tests/function_missing_return_type.vv:1:1: error: missing return | ~~~~~~~~~~ 2 | } 3 | +vlib/v/checker/tests/function_missing_return_type.vv:14:11: error: `s` cannot be referenced outside `unsafe` blocks as it might be stored on stack. Consider declaring `Abc` as `[heap]`. + 12 | fn (s Abc) abc() &int { + 13 | if true { + 14 | return &s.x + | ^ + 15 | } + 16 | s.panic(@FN) vlib/v/checker/tests/function_missing_return_type.vv:12:1: error: missing return at end of function `abc` 10 | } 11 | diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 6f46bdb04d..e892c736cf 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -147,7 +147,7 @@ pub struct RemoveNewLineConfig { } pub fn (mut f Fmt) remove_new_line(cfg RemoveNewLineConfig) { - mut buffer := if cfg.imports_buffer { &f.out_imports } else { &f.out } + mut buffer := if cfg.imports_buffer { unsafe { &f.out_imports } } else { unsafe { &f.out } } mut i := 0 for i = buffer.len - 1; i >= 0; i-- { if !buffer.buf[i].is_space() { // != `\n` { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 8fbac4be7b..c282a96416 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -2145,21 +2145,36 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { g.expr(assign_stmt.right[0]) g.writeln(';') for i, lx in assign_stmt.left { + mut is_auto_heap := false if lx is ast.Ident { if lx.kind == .blank_ident { continue } + if lx.obj is ast.Var { + is_auto_heap = lx.obj.is_auto_heap + } } styp := g.typ(assign_stmt.left_types[i]) if assign_stmt.op == .decl_assign { g.write('$styp ') + if is_auto_heap { + g.write('*') + } } g.expr(lx) if is_opt { mr_base_styp := g.base_type(return_type) - g.writeln(' = (*($mr_base_styp*)${mr_var_name}.data).arg$i;') + if is_auto_heap { + g.writeln(' = HEAP($mr_base_styp, *($mr_base_styp*)${mr_var_name}.data).arg$i);') + } else { + g.writeln(' = (*($mr_base_styp*)${mr_var_name}.data).arg$i;') + } } else { - g.writeln(' = ${mr_var_name}.arg$i;') + if is_auto_heap { + g.writeln(' = HEAP($styp, ${mr_var_name}.arg$i);') + } else { + g.writeln(' = ${mr_var_name}.arg$i;') + } } } return @@ -2256,6 +2271,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { } // `a := 1` | `a,b := 1,2` for i, left in assign_stmt.left { + mut is_auto_heap := false mut var_type := assign_stmt.left_types[i] mut val_type := assign_stmt.right_types[i] val := assign_stmt.right[i] @@ -2281,6 +2297,9 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { var_type = var_type.set_flag(.atomic_f) } } + if left.obj is ast.Var { + is_auto_heap = left.obj.is_auto_heap + } } styp := g.typ(var_type) mut is_fixed_array_init := false @@ -2432,6 +2451,9 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { g.out.write_string(util.tabs(g.indent - g.inside_ternary)) } g.write('$styp ') + if is_auto_heap { + g.write('*') + } } if left is ast.Ident || left is ast.SelectorExpr { g.prevent_sum_type_unwrapping_once = true @@ -2511,10 +2533,16 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { g.write('{0}') } } else { + if is_auto_heap { + g.write('HEAP($styp, (') + } if val.is_auto_deref_var() { g.write('*') } g.expr(val) + if is_auto_heap { + g.write('))') + } } } else { if assign_stmt.has_cross_var { @@ -2842,9 +2870,9 @@ fn (mut g Gen) map_fn_ptrs(key_typ ast.TypeSymbol) (string, string, string, stri } .voidptr { ts := if g.pref.m64 { - &g.table.type_symbols[ast.u64_type_idx] + unsafe { &g.table.type_symbols[ast.u64_type_idx] } } else { - &g.table.type_symbols[ast.u32_type_idx] + unsafe { &g.table.type_symbols[ast.u32_type_idx] } } return g.map_fn_ptrs(ts) } @@ -3123,7 +3151,9 @@ fn (mut g Gen) expr(node ast.Expr) { } } else { // g.write('/*pref*/') - g.write(node.op.str()) + if !(g.is_amp && node.right.is_auto_deref_var()) { + g.write(node.op.str()) + } // g.write('(') g.expr(node.right) } @@ -4355,6 +4385,7 @@ fn (mut g Gen) ident(node ast.Ident) { mut name := c_name(node.name) // TODO: temporary, remove this node_info := node.info + mut is_auto_heap := false if node_info is ast.IdentVar { // x ?int // `x = 10` => `x.data = 10` (g.right_is_opt == false) @@ -4372,12 +4403,16 @@ fn (mut g Gen) ident(node ast.Ident) { } scope := g.file.scope.innermost(node.pos.pos) if v := scope.find_var(node.name) { + is_auto_heap = v.is_auto_heap && (!g.is_assign_lhs || g.assign_op != .decl_assign) + if is_auto_heap { + g.write('(*(') + } if v.smartcasts.len > 0 { v_sym := g.table.get_type_symbol(v.typ) if !prevent_sum_type_unwrapping_once { for _ in v.smartcasts { g.write('(') - if v_sym.kind == .sum_type { + if v_sym.kind == .sum_type && !is_auto_heap { g.write('*') } } @@ -4390,7 +4425,7 @@ fn (mut g Gen) ident(node ast.Ident) { is_ptr = true } } - dot := if is_ptr { '->' } else { '.' } + dot := if is_ptr || is_auto_heap { '->' } else { '.' } if mut cast_sym.info is ast.Aggregate { sym := g.table.get_type_symbol(cast_sym.info.types[g.aggregate_type_idx]) g.write('${dot}_$sym.cname') @@ -4399,6 +4434,9 @@ fn (mut g Gen) ident(node ast.Ident) { } g.write(')') } + if is_auto_heap { + g.write('))') + } return } } @@ -4413,6 +4451,9 @@ fn (mut g Gen) ident(node ast.Ident) { } } g.write(g.get_ternary_name(name)) + if is_auto_heap { + g.write('))') + } } fn (mut g Gen) cast_expr(node ast.CastExpr) { @@ -4623,7 +4664,18 @@ fn (mut g Gen) if_expr(node ast.IfExpr) { g.expr(branch.cond.expr) g.writeln(';') } else { - g.writeln('\t$base_type $branch.cond.var_name = *($base_type*)${var_name}.data;') + mut is_auto_heap := false + if branch.stmts.len > 0 { + scope := g.file.scope.innermost(ast.Node(branch.stmts[branch.stmts.len - 1]).position().pos) + if v := scope.find_var(branch.cond.var_name) { + is_auto_heap = v.is_auto_heap + } + } + if is_auto_heap { + g.writeln('\t$base_type* $branch.cond.var_name = HEAP($base_type, *($base_type*)${var_name}.data);') + } else { + g.writeln('\t$base_type $branch.cond.var_name = *($base_type*)${var_name}.data;') + } } } } diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 0bade0c423..29e9bdd4ae 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -69,7 +69,9 @@ fn (mut g Gen) process_fn_decl(node ast.FnDecl) { } } keep_fn_decl := g.fn_decl - g.fn_decl = &node + unsafe { + g.fn_decl = &node + } if node.name == 'main.main' { g.has_main = true } diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index bad8ee6130..96e4905172 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -387,7 +387,7 @@ fn (mut g JsGen) stmt(node ast.Stmt) { g.gen_expr_stmt(node) } ast.FnDecl { - g.fn_decl = &node + g.fn_decl = unsafe { &node } g.gen_fn_decl(node) } ast.ForCStmt { @@ -813,7 +813,9 @@ fn fn_has_go(node ast.FnDecl) bool { } fn (mut g JsGen) gen_method_decl(it ast.FnDecl) { - g.fn_decl = &it + unsafe { + g.fn_decl = &it + } has_go := fn_has_go(it) is_main := it.name == 'main.main' g.gen_attrs(it.attrs) diff --git a/vlib/v/parser/assign.v b/vlib/v/parser/assign.v index d893b9f7d7..7e6d1ce32b 100644 --- a/vlib/v/parser/assign.v +++ b/vlib/v/parser/assign.v @@ -152,6 +152,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme share: share is_mut: lx.is_mut || p.inside_for pos: lx.pos + is_stack_obj: p.inside_for } if p.pref.autofree { if r0 is ast.CallExpr { diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 0f3ab8311a..468251ae59 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -291,11 +291,28 @@ fn (mut p Parser) fn_decl() ast.FnDecl { scope: 0 } } + mut is_heap_ref := false // args are only borrowed, so assume maybe on stack + mut is_stack_obj := true + nr_muls := param.typ.nr_muls() + if nr_muls == 1 { // mut a St, b &St + base_type_sym := p.table.get_type_symbol(param.typ.set_nr_muls(0)) + if base_type_sym.kind == .struct_ { + info := base_type_sym.info as ast.Struct + is_heap_ref = info.is_heap // if type is declared as [heap] we can assume this, too + is_stack_obj = !is_heap_ref + } + } + if param.typ.has_flag(.shared_f) { + is_heap_ref = true + is_stack_obj = false + } p.scope.register(ast.Var{ name: param.name typ: param.typ is_mut: param.is_mut is_auto_deref: param.is_mut || param.is_auto_rec + is_heap_ref: is_heap_ref + is_stack_obj: is_stack_obj pos: param.pos is_used: true is_arg: true @@ -580,6 +597,21 @@ fn (mut p Parser) anon_fn() ast.AnonFn { if arg.name.len == 0 { p.error_with_pos('use `_` to name an unused parameter', arg.pos) } + mut is_heap_ref := false // args are only borrowed, so assume maybe on stack + mut is_stack_obj := true + nr_muls := arg.typ.nr_muls() + if nr_muls == 1 { // mut a St, b &St + base_type_sym := p.table.get_type_symbol(arg.typ.set_nr_muls(0)) + if base_type_sym.kind == .struct_ { + info := base_type_sym.info as ast.Struct + is_heap_ref = info.is_heap // if type is declared as [heap] we can assume this, too + is_stack_obj = !is_heap_ref + } + } + if arg.typ.has_flag(.shared_f) { + is_heap_ref = true + is_stack_obj = false + } p.scope.register(ast.Var{ name: arg.name typ: arg.typ @@ -587,6 +619,8 @@ fn (mut p Parser) anon_fn() ast.AnonFn { pos: arg.pos is_used: true is_arg: true + is_heap_ref: is_heap_ref + is_stack_obj: is_stack_obj }) } mut same_line := p.tok.line_nr == p.prev_tok.line_nr diff --git a/vlib/v/parser/for.v b/vlib/v/parser/for.v index 23986ab7bc..36605c3ca0 100644 --- a/vlib/v/parser/for.v +++ b/vlib/v/parser/for.v @@ -122,6 +122,7 @@ fn (mut p Parser) for_stmt() ast.Stmt { typ: ast.int_type pos: key_var_pos is_tmp: true + is_stack_obj: true }) } else if p.scope.known_var(val_var_name) { return p.error('redefinition of value iteration variable `$val_var_name`') @@ -146,6 +147,7 @@ fn (mut p Parser) for_stmt() ast.Stmt { typ: ast.int_type pos: val_var_pos is_tmp: true + is_stack_obj: true }) if key_var_name.len > 0 { return p.error_with_pos('cannot declare index variable with range `for`', @@ -159,6 +161,7 @@ fn (mut p Parser) for_stmt() ast.Stmt { is_mut: val_is_mut is_auto_deref: val_is_mut is_tmp: true + is_stack_obj: true }) } p.inside_for = false diff --git a/vlib/v/parser/if_match.v b/vlib/v/parser/if_match.v index 69707af67b..424a538f46 100644 --- a/vlib/v/parser/if_match.v +++ b/vlib/v/parser/if_match.v @@ -54,6 +54,7 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr { typ: ast.error_type pos: p.tok.position() is_used: true + is_stack_obj: true }) } branches << ast.IfBranch{ diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index c8116050d8..528e203abe 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2258,6 +2258,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { typ: ast.error_type pos: p.tok.position() is_used: true + is_stack_obj: true }) or_kind = .block or_stmts = p.parse_block_no_scope(false) diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index 8e78842305..bc92cc9c0d 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -471,6 +471,7 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr { typ: ast.error_type pos: p.tok.position() is_used: true + is_stack_obj: true }) or_kind = .block or_stmts = p.parse_block_no_scope(false) @@ -553,6 +554,7 @@ fn (mut p Parser) prefix_expr() ast.PrefixExpr { typ: ast.error_type pos: p.tok.position() is_used: true + is_stack_obj: true }) or_kind = .block or_stmts = p.parse_block_no_scope(false) diff --git a/vlib/v/tests/mut_receiver_returned_as_reference_test.v b/vlib/v/tests/mut_receiver_returned_as_reference_test.v index fcf4fba9b0..30a5da4b05 100644 --- a/vlib/v/tests/mut_receiver_returned_as_reference_test.v +++ b/vlib/v/tests/mut_receiver_returned_as_reference_test.v @@ -18,7 +18,7 @@ fn (mut p Player) set_position(x int, y int) &Player { // TODO: from the point of view of the V programmer, // `p` has still type &Player. // assert typeof(p).name == 'Player' - return &p + return unsafe { &p } } fn test_mut_receiver() { diff --git a/vlib/v/tests/mut_test.v b/vlib/v/tests/mut_test.v index fa8c1fc347..b8acb6610a 100644 --- a/vlib/v/tests/mut_test.v +++ b/vlib/v/tests/mut_test.v @@ -268,7 +268,7 @@ mut: } fn foo4(mut f Foo) { - f2 := &f + f2 := unsafe { &f } f.foo = 100 println(f.foo) println(f2.foo) @@ -284,7 +284,7 @@ fn test_mut_13() { } fn foo5(mut arr []int) { - arr2 := &arr + arr2 := unsafe { &arr } arr[0] = 0 println(arr[0]) // 0 assert arr[0] == 0 @@ -300,7 +300,7 @@ fn test_mut_14() { } fn foo6(mut arr [3]int) { - arr2 := &arr + arr2 := unsafe { &arr } arr[0] = 0 println(arr[0]) // 0 assert arr[0] == 0 @@ -316,7 +316,7 @@ fn test_mut_15() { } fn foo7(mut m map[string]int) { - m2 := &m + m2 := unsafe { &m } m['one'] = 1 println(m['one']) // 1 assert m['one'] == 1 diff --git a/vlib/v/tests/ptr_arithmetic_test.v b/vlib/v/tests/ptr_arithmetic_test.v index 462235b614..dcb681b5c6 100644 --- a/vlib/v/tests/ptr_arithmetic_test.v +++ b/vlib/v/tests/ptr_arithmetic_test.v @@ -48,8 +48,8 @@ fn test_ptr_arithmetic_over_struct() { a[0].x = 10 a[1].x = 100 a[2].x = 1000 - mut pa := &a[0] - assert pa == &a[0] + mut pa := unsafe { &a[0] } + assert pa == unsafe { &a[0] } unsafe { assert pa.x == 10 pa++ @@ -66,5 +66,5 @@ fn test_ptr_arithmetic_over_struct() { assert pa.x == 1000 pa -= 2 } - assert pa == &a[0] + assert pa == unsafe { &a[0] } } diff --git a/vlib/v/tests/reference_return_test.v b/vlib/v/tests/reference_return_test.v new file mode 100644 index 0000000000..b5b7f66628 --- /dev/null +++ b/vlib/v/tests/reference_return_test.v @@ -0,0 +1,25 @@ +struct Qwe { +mut: + a f64 +} + +fn f() &Qwe { + q := Qwe{ + a: 12.5 + } + return &q +} + +fn g() f64 { + a := -0.125 + b := 3 + c := a * b + return c +} + +fn test_reference_return() { + x := f() + y := g() + assert x.a == 12.5 + assert y == -0.375 +} diff --git a/vlib/v/tests/type_voidptr_test.v b/vlib/v/tests/type_voidptr_test.v index 2ea5765828..8fc8254cae 100644 --- a/vlib/v/tests/type_voidptr_test.v +++ b/vlib/v/tests/type_voidptr_test.v @@ -15,7 +15,7 @@ fn memcpy(mut dest voidptr, src voidptr, len u32) voidptr { fn test_mut_voidptr_arg() { mut a := [1, 2]! b := [3, 4]! - mut aptr := voidptr(&a[0]) + mut aptr := voidptr(unsafe { &a[0] }) unsafe { memcpy(mut aptr, &b[0], sizeof(int)) } assert a == [3, 2]! }