diff --git a/cmd/tools/test_os_process.v b/cmd/tools/test_os_process.v index 4fb446f47b..bc275dd533 100644 --- a/cmd/tools/test_os_process.v +++ b/cmd/tools/test_os_process.v @@ -43,7 +43,7 @@ fn (mut ctx Context) println(s string) { } fn do_timeout(c &Context) { - mut ctx := c + mut ctx := unsafe { c } time.sleep(ctx.timeout_ms * time.millisecond) exit(ctx.exitcode) } diff --git a/cmd/tools/vwatch.v b/cmd/tools/vwatch.v index 563995b5ed..17f96023f5 100644 --- a/cmd/tools/vwatch.v +++ b/cmd/tools/vwatch.v @@ -171,7 +171,7 @@ fn (mut context Context) get_changed_vfiles() int { } fn change_detection_loop(ocontext &Context) { - mut context := ocontext + mut context := unsafe { ocontext } for { if context.v_cycles >= max_v_cycles || context.scan_cycles >= max_scan_cycles { context.is_exiting = true diff --git a/examples/sokol/sounds/melody.v b/examples/sokol/sounds/melody.v index 04cd7a4427..b5f230caed 100644 --- a/examples/sokol/sounds/melody.v +++ b/examples/sokol/sounds/melody.v @@ -13,7 +13,7 @@ mut: } fn my_audio_stream_callback(buffer &f32, num_frames int, num_channels int, mut acontext AppState) { - mut soundbuffer := buffer + mut soundbuffer := unsafe { buffer } for frame := 0; frame < num_frames; frame++ { t := int(f32(acontext.frame_0 + frame) * 0.245) // "Techno" by Gabriel Miceli diff --git a/examples/term.ui/pong.v b/examples/term.ui/pong.v index 38a0020715..375261f769 100644 --- a/examples/term.ui/pong.v +++ b/examples/term.ui/pong.v @@ -16,6 +16,7 @@ const ( orange = ui.Color{255, 140, 0} ) +[heap] struct App { mut: tui &ui.Context = 0 @@ -238,6 +239,7 @@ fn (mut b Ball) update(dt f32) { b.pos.y += b.vel.y * b.acc.y * dt } +[heap] struct Game { mut: app &App = 0 diff --git a/examples/term.ui/vyper.v b/examples/term.ui/vyper.v index 72f9453119..c5712c3b5d 100644 --- a/examples/term.ui/vyper.v +++ b/examples/term.ui/vyper.v @@ -256,6 +256,7 @@ fn (mut r Rat) randomize() { r.app.height - block_size - buffer) } +[heap] struct App { mut: termui &termui.Context = 0 diff --git a/vlib/builtin/linux_bare/libc_impl.v b/vlib/builtin/linux_bare/libc_impl.v index 516f984402..fb7cceda6b 100644 --- a/vlib/builtin/linux_bare/libc_impl.v +++ b/vlib/builtin/linux_bare/libc_impl.v @@ -9,7 +9,7 @@ pub fn memcpy(dest &C.void, src &C.void, n size_t) &C.void { dest_[i] = src_[i] } } - return dest + return unsafe { dest } } [export: 'malloc'] @@ -37,7 +37,7 @@ fn realloc(old_area &C.void, new_size size_t) &C.void { } old_size := unsafe { *(&u64(old_area - sizeof(u64))) } if u64(new_size) <= old_size { - return old_area + return unsafe { old_area } } else { new_area := unsafe { malloc(int(new_size)) } unsafe { memmove(new_area, old_area, size_t(old_size)) } @@ -54,7 +54,7 @@ fn memset(s &C.void, c int, n size_t) &C.void { s_[i] = char(c) } } - return s + return unsafe { s } } [unsafe] @@ -74,7 +74,7 @@ fn memmove(dest &C.void, src &C.void, n size_t) &C.void { } } unsafe { free(temp_buf) } - return dest + return unsafe { dest } } [export: 'calloc'] diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index 57616b6750..d3520c48f8 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -77,7 +77,7 @@ pub fn tos(s &byte, len int) string { panic('tos(): nil string') } return string{ - str: s + str: unsafe { s } len: len } } @@ -96,7 +96,7 @@ pub fn tos2(s &byte) string { panic('tos2: nil string') } return string{ - str: s + str: unsafe { s } len: unsafe { vstrlen(s) } } } @@ -146,7 +146,7 @@ pub fn tos_lit(s &char) string { [unsafe] pub fn (bp &byte) vstring() string { return string{ - str: bp + str: unsafe { bp } len: unsafe { C.strlen(&char(bp)) } } } @@ -156,7 +156,7 @@ pub fn (bp &byte) vstring() string { [unsafe] pub fn (bp &byte) vstring_with_len(len int) string { return string{ - str: bp + str: unsafe { bp } len: len is_lit: 0 } @@ -194,7 +194,7 @@ pub fn (cp &char) vstring_with_len(len int) string { [unsafe] pub fn (bp &byte) vstring_literal() string { return string{ - str: bp + str: unsafe { bp } len: unsafe { C.strlen(&char(bp)) } is_lit: 1 } @@ -205,7 +205,7 @@ pub fn (bp &byte) vstring_literal() string { [unsafe] pub fn (bp &byte) vstring_literal_with_len(len int) string { return string{ - str: bp + str: unsafe { bp } len: len is_lit: 1 } diff --git a/vlib/cli/command.v b/vlib/cli/command.v index 6a66490b10..43940d19b4 100644 --- a/vlib/cli/command.v +++ b/vlib/cli/command.v @@ -94,7 +94,7 @@ pub fn (mut cmd Command) add_command(command Command) { println('Command with the name `$subcmd.name` already exists') exit(1) } - subcmd.parent = cmd + subcmd.parent = unsafe { cmd } cmd.commands << subcmd } @@ -102,7 +102,7 @@ pub fn (mut cmd Command) add_command(command Command) { // is linked as a chain. pub fn (mut cmd Command) setup() { for mut subcmd in cmd.commands { - subcmd.parent = cmd + subcmd.parent = unsafe { cmd } subcmd.setup() } } diff --git a/vlib/gg/gg.v b/vlib/gg/gg.v index 73ebffa8aa..127789e36b 100644 --- a/vlib/gg/gg.v +++ b/vlib/gg/gg.v @@ -100,6 +100,7 @@ pub: native_rendering bool // Cocoa on macOS/iOS, GDI+ on Windows } +[heap] pub struct Context { render_text bool mut: diff --git a/vlib/gg/image.v b/vlib/gg/image.v index 4ce733dea0..302673615e 100644 --- a/vlib/gg/image.v +++ b/vlib/gg/image.v @@ -10,6 +10,7 @@ import sokol import sokol.sgl import stbi +[heap] pub struct Image { pub mut: id int diff --git a/vlib/glm/glm.v b/vlib/glm/glm.v index 3263634cdb..5836176458 100644 --- a/vlib/glm/glm.v +++ b/vlib/glm/glm.v @@ -43,7 +43,7 @@ pub fn vec3(x f32, y f32, z f32) Vec3 { fn mat4(f &f32) Mat4 { res := Mat4{ - data: f + data: unsafe { f } } return res } diff --git a/vlib/picohttpparser/response.v b/vlib/picohttpparser/response.v index 3fa46abf3e..6198ae463c 100644 --- a/vlib/picohttpparser/response.v +++ b/vlib/picohttpparser/response.v @@ -20,7 +20,7 @@ pub fn (mut r Response) write_string(s string) { [inline] pub fn (mut r Response) http_ok() &Response { r.write_string('HTTP/1.1 200 OK\r\n') - return r + return unsafe { r } } [inline] @@ -29,7 +29,7 @@ pub fn (mut r Response) header(k string, v string) &Response { r.write_string(': ') r.write_string(v) r.write_string('\r\n') - return r + return unsafe { r } } [inline] @@ -39,13 +39,13 @@ pub fn (mut r Response) header_date() &Response { r.buf += cpy(r.buf, r.date, 29) } r.write_string('\r\n') - return r + return unsafe { r } } [inline] pub fn (mut r Response) header_server() &Response { r.write_string('Server: V\r\n') - return r + return unsafe { r } } [inline] @@ -53,25 +53,25 @@ pub fn (mut r Response) content_type(s string) &Response { r.write_string('Content-Type: ') r.write_string(s) r.write_string('\r\n') - return r + return unsafe { r } } [inline] pub fn (mut r Response) html() &Response { r.write_string('Content-Type: text/html\r\n') - return r + return unsafe { r } } [inline] pub fn (mut r Response) plain() &Response { r.write_string('Content-Type: text/plain\r\n') - return r + return unsafe { r } } [inline] pub fn (mut r Response) json() &Response { r.write_string('Content-Type: application/json\r\n') - return r + return unsafe { r } } [inline] diff --git a/vlib/rand/rand.v b/vlib/rand/rand.v index 5bed8b3e53..19be11ab34 100644 --- a/vlib/rand/rand.v +++ b/vlib/rand/rand.v @@ -68,7 +68,7 @@ pub fn get_current_rng() &PRNG { // should be restored if work with the custom RNG is complete. It is not necessary to restore if the // program terminates soon afterwards. pub fn set_rng(rng &PRNG) { - default_rng = rng + default_rng = unsafe { rng } } // seed sets the given array of `u32` values as the seed for the `default_rng`. The default_rng is diff --git a/vlib/sokol/gfx/gfx_structs.v b/vlib/sokol/gfx/gfx_structs.v index e40384ac27..fe2c24b2ca 100644 --- a/vlib/sokol/gfx/gfx_structs.v +++ b/vlib/sokol/gfx/gfx_structs.v @@ -150,6 +150,7 @@ pub fn (b &C.sg_bindings) append_index_buffer(data voidptr, element_size int, el return C.sg_append_buffer(b.index_buffer, &range) } +[heap] pub struct C.sg_shader_desc { pub mut: _start_canary u32 diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 8b57fcdc3a..5bc0ec5c2f 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -510,7 +510,6 @@ pub mut: 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) } diff --git a/vlib/v/ast/scope.v b/vlib/v/ast/scope.v index 0e1607d01b..fecb8c6f56 100644 --- a/vlib/v/ast/scope.v +++ b/vlib/v/ast/scope.v @@ -3,6 +3,7 @@ // that can be found in the LICENSE file. module ast +[heap] pub struct Scope { pub mut: // mut: diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 1b364ea526..85afd06dd4 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -282,7 +282,7 @@ pub fn (t &Table) type_has_method(s &TypeSymbol, name string) bool { // search from current type up through each parent looking for method pub fn (t &Table) type_find_method(s &TypeSymbol, name string) ?Fn { // println('type_find_method($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx') - mut ts := s + mut ts := unsafe { s } for { if method := ts.find_method(name) { return method @@ -337,7 +337,7 @@ pub fn (t &Table) struct_has_field(s &TypeSymbol, name string) bool { // search from current type up through each parent looking for field pub fn (t &Table) find_field(s &TypeSymbol, name string) ?StructField { // println('find_field($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx') - mut ts := s + mut ts := unsafe { s } for { match mut ts.info { Struct { @@ -401,7 +401,7 @@ pub fn (t &Table) find_field_with_embeds(sym &TypeSymbol, field_name string) ?St } pub fn (t &Table) resolve_common_sumtype_fields(sym_ &TypeSymbol) { - mut sym := sym_ + mut sym := unsafe { sym_ } mut info := sym.info as SumType if info.found_fields { return diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index fcaf5b408a..ddeba5341b 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -64,6 +64,7 @@ pub fn pref_arch_to_table_language(pref_arch pref.Arch) Language { // * Table.type_kind(typ) not TypeSymbol.kind. // Each TypeSymbol is entered into `Table.types`. // See also: Table.get_type_symbol. + pub struct TypeSymbol { pub: parent_idx int @@ -557,6 +558,15 @@ pub fn (t &TypeSymbol) sumtype_info() SumType { } } +pub fn (t &TypeSymbol) is_heap() bool { + if t.kind == .struct_ { + info := t.info as Struct + return info.is_heap + } else { + return false + } +} + /* pub fn (t TypeSymbol) str() string { return t.name diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 602c7ece6c..0eac696069 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -152,7 +152,7 @@ pub fn (mut c Checker) check2(ast_file &ast.File) []errors.Error { } pub fn (mut c Checker) change_current_file(file &ast.File) { - c.file = file + c.file = unsafe { file } c.vmod_file_content = '' c.mod = file.mod.name } @@ -666,11 +666,6 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type { c.error('struct `$type_sym.name` is declared with a `[noinit]` attribute, so ' + 'it cannot be initialized with `$type_sym.name{}`', struct_init.pos) } - if info.is_heap && c.inside_decl_rhs && !c.inside_ref_lit && !c.inside_unsafe - && !struct_init.typ.is_ptr() { - c.error('`$type_sym.name` type can only be used as a reference `&$type_sym.name` or inside a `struct` reference', - struct_init.pos) - } } if type_sym.name.len == 1 && c.cur_fn.generic_names.len == 0 { c.error('unknown struct `$type_sym.name`', struct_init.pos) @@ -754,9 +749,12 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type { continue } } + mut expr_type := ast.Type(0) + mut expected_type := ast.Type(0) if is_embed { - c.expected_type = embed_type - expr_type := c.expr(field.expr) + expected_type = embed_type + c.expected_type = expected_type + expr_type = c.expr(field.expr) expr_type_sym := c.table.get_type_symbol(expr_type) if expr_type != ast.void_type && expr_type_sym.kind != .placeholder { c.check_expected(expr_type, embed_type) or { @@ -769,8 +767,9 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type { } else { inited_fields << field_name field_type_sym := c.table.get_type_symbol(info_field.typ) - c.expected_type = info_field.typ - mut expr_type := c.expr(field.expr) + expected_type = info_field.typ + c.expected_type = expected_type + expr_type = c.expr(field.expr) if !info_field.typ.has_flag(.optional) { expr_type = c.check_expr_opt_call(field.expr, expr_type) } @@ -798,6 +797,28 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) ast.Type { struct_init.fields[i].typ = expr_type struct_init.fields[i].expected_type = info_field.typ } + if expr_type.is_ptr() && expected_type.is_ptr() { + if mut field.expr is ast.Ident { + if mut field.expr.obj is ast.Var { + mut obj := unsafe { &field.expr.obj } + if c.fn_scope != voidptr(0) { + obj = c.fn_scope.find_var(obj.name) or { obj } + } + if obj.is_stack_obj && !c.inside_unsafe { + sym := c.table.get_type_symbol(obj.typ.set_nr_muls(0)) + if !sym.is_heap() { + suggestion := if sym.kind == .struct_ { + 'declaring `$sym.name` as `[heap]`' + } else { + 'wrapping the `$sym.name` object in a `struct` declared as `[heap]`' + } + c.error('`$field.expr.name` cannot be assigned outside `unsafe` blocks as it might refer to an object stored on stack. Consider ${suggestion}.', + field.expr.pos) + } + } + } + } + } } // Check uninitialized refs/sum types for field in info.fields { @@ -2954,6 +2975,29 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) { c.error('fn `$c.cur_fn.name` expects you to return a reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead', pos) } + if exp_type.is_ptr() && got_typ.is_ptr() { + mut r_expr := &return_stmt.exprs[i] + if mut r_expr is ast.Ident { + if mut r_expr.obj is ast.Var { + mut obj := unsafe { &r_expr.obj } + if c.fn_scope != voidptr(0) { + obj = c.fn_scope.find_var(r_expr.obj.name) or { obj } + } + if obj.is_stack_obj && !c.inside_unsafe { + type_sym := c.table.get_type_symbol(obj.typ.set_nr_muls(0)) + if !type_sym.is_heap() { + suggestion := if type_sym.kind == .struct_ { + 'declaring `$type_sym.name` as `[heap]`' + } else { + 'wrapping the `$type_sym.name` object in a `struct` declared as `[heap]`' + } + c.error('`$r_expr.name` cannot be returned outside `unsafe` blocks as it might refer to an object stored on stack. Consider ${suggestion}.', + r_expr.pos) + } + } + } + } + } } if exp_is_optional && return_stmt.exprs.len > 0 { expr0 := return_stmt.exprs[0] @@ -3176,6 +3220,28 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { c.fail_if_immutable(left) // left_type = c.expr(left) } + if right_type.is_ptr() && left_type.is_ptr() { + if mut right is ast.Ident { + if mut right.obj is ast.Var { + mut obj := unsafe { &right.obj } + if c.fn_scope != voidptr(0) { + obj = c.fn_scope.find_var(right.obj.name) or { obj } + } + if obj.is_stack_obj && !c.inside_unsafe { + type_sym := c.table.get_type_symbol(obj.typ.set_nr_muls(0)) + if !type_sym.is_heap() { + suggestion := if type_sym.kind == .struct_ { + 'declaring `$type_sym.name` as `[heap]`' + } else { + 'wrapping the `$type_sym.name` object in a `struct` declared as `[heap]`' + } + c.error('`$right.name` cannot be assigned outside `unsafe` blocks as it might refer to an object stored on stack. Consider ${suggestion}.', + right.pos) + } + } + } + } + } assign_stmt.left_types << left_type match mut left { ast.Ident { @@ -3216,6 +3282,11 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { if left.obj.is_auto_deref { left.obj.is_used = true } + if !left_type.is_ptr() { + if c.table.get_type_symbol(left_type).is_heap() { + left.obj.is_auto_heap = true + } + } } ast.GlobalField { left.obj.typ = left_type @@ -6046,17 +6117,27 @@ pub fn (mut c Checker) mark_as_referenced(mut node ast.Expr) { 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 } } + obj = c.fn_scope.find_var(node.obj.name) or { 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]`.', + type_sym := c.table.get_type_symbol(obj.typ.set_nr_muls(0)) + if obj.is_stack_obj && !type_sym.is_heap() { + suggestion := if type_sym.kind == .struct_ { + 'declaring `$type_sym.name` as `[heap]`' + } else { + 'wrapping the `$type_sym.name` object in a `struct` declared as `[heap]`' + } + c.error('`$node.name` cannot be referenced outside `unsafe` blocks as it might be stored on stack. Consider ${suggestion}.', 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 + if type_sym.kind == .struct_ { + info := type_sym.info as ast.Struct + if !info.is_heap { + node.obj.is_auto_heap = true + } + } } } } @@ -6930,7 +7011,7 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { } } c.expected_type = ast.void_type - c.cur_fn = node + c.cur_fn = unsafe { node } // Add return if `fn(...) ? {...}` have no return at end if node.return_type != ast.void_type && node.return_type.has_flag(.optional) && (node.stmts.len == 0 || node.stmts[node.stmts.len - 1] !is ast.Return) { diff --git a/vlib/v/checker/tests/heap_struct.out b/vlib/v/checker/tests/heap_struct.out deleted file mode 100644 index 183cc015a0..0000000000 --- a/vlib/v/checker/tests/heap_struct.out +++ /dev/null @@ -1,35 +0,0 @@ -vlib/v/checker/tests/heap_struct.vv:18:7: error: `Abc` type can only be used as a reference `&Abc` or inside a `struct` reference - 16 | - 17 | fn main() { - 18 | a := Abc{ n: 3 } - | ~~~~~~~~~~~ - 19 | b := St{ - 20 | Abc{ n: 7 } -vlib/v/checker/tests/heap_struct.vv:19:7: error: `St` type can only be used as a reference `&St` or inside a `struct` reference - 17 | fn main() { - 18 | a := Abc{ n: 3 } - 19 | b := St{ - | ~~~ - 20 | Abc{ n: 7 } - 21 | } -vlib/v/checker/tests/heap_struct.vv:20:3: error: `Abc` type can only be used as a reference `&Abc` or inside a `struct` reference - 18 | a := Abc{ n: 3 } - 19 | b := St{ - 20 | Abc{ n: 7 } - | ~~~~~~~~~~~ - 21 | } - 22 | x := Qwe{ -vlib/v/checker/tests/heap_struct.vv:22:7: error: `Qwe` type can only be used as a reference `&Qwe` or inside a `struct` reference - 20 | Abc{ n: 7 } - 21 | } - 22 | x := Qwe{ - | ~~~~ - 23 | f: 12.25 - 24 | a: Abc{ n: 23 } -vlib/v/checker/tests/heap_struct.vv:24:6: error: `Abc` type can only be used as a reference `&Abc` or inside a `struct` reference - 22 | x := Qwe{ - 23 | f: 12.25 - 24 | a: Abc{ n: 23 } - | ~~~~~~~~~~~~ - 25 | } - 26 | println('$a.n $b.n $x.a.n') diff --git a/vlib/v/checker/tests/heap_struct.vv b/vlib/v/checker/tests/heap_struct.vv deleted file mode 100644 index c870c60ca1..0000000000 --- a/vlib/v/checker/tests/heap_struct.vv +++ /dev/null @@ -1,27 +0,0 @@ -[heap] -struct Abc { -mut: - n int -} - -struct St { - Abc -} - -struct Qwe { -mut: - f f64 - a Abc -} - -fn main() { - a := Abc{ n: 3 } - b := St{ - Abc{ n: 7 } - } - x := Qwe{ - f: 12.25 - a: Abc{ n: 23 } - } - println('$a.n $b.n $x.a.n') -} diff --git a/vlib/v/checker/tests/no_heap_struct.out b/vlib/v/checker/tests/no_heap_struct.out new file mode 100644 index 0000000000..8a996a1a00 --- /dev/null +++ b/vlib/v/checker/tests/no_heap_struct.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/no_heap_struct.vv:13:6: error: `x` cannot be assigned outside `unsafe` blocks as it might refer to an object stored on stack. Consider declaring `Abc` as `[heap]`. + 11 | fn f(x &Abc) St { + 12 | s := St{ + 13 | a: x + | ^ + 14 | } + 15 | return s +vlib/v/checker/tests/no_heap_struct.vv:19:9: error: `x` cannot be returned outside `unsafe` blocks as it might refer to an object stored on stack. Consider declaring `Abc` as `[heap]`. + 17 | + 18 | fn g(mut x Abc) &Abc { + 19 | return x + | ^ + 20 | } + 21 | +vlib/v/checker/tests/no_heap_struct.vv:23:7: error: `x` cannot be assigned outside `unsafe` blocks as it might refer to an object stored on stack. Consider declaring `Abc` as `[heap]`. + 21 | + 22 | fn h(x &Abc) &Abc { + 23 | y := x + | ^ + 24 | return y + 25 | } diff --git a/vlib/v/checker/tests/no_heap_struct.vv b/vlib/v/checker/tests/no_heap_struct.vv new file mode 100644 index 0000000000..ce1d47408c --- /dev/null +++ b/vlib/v/checker/tests/no_heap_struct.vv @@ -0,0 +1,25 @@ +struct Abc { +mut: + n int +} + +struct St { +mut: + a &Abc +} + +fn f(x &Abc) St { + s := St{ + a: x + } + return s +} + +fn g(mut x Abc) &Abc { + return x +} + +fn h(x &Abc) &Abc { + y := x + return y +} diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 5a2ef22ac1..289ca8bca2 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -32,6 +32,7 @@ mut: methods map[string][]ast.FnDecl } +[heap] struct JsGen { table &ast.Table pref &pref.Preferences diff --git a/vlib/v/gen/native/gen.v b/vlib/v/gen/native/gen.v index 32e54c644b..92b0b30610 100644 --- a/vlib/v/gen/native/gen.v +++ b/vlib/v/gen/native/gen.v @@ -20,6 +20,7 @@ interface CodeGen { // XXX WHY gen_exit fn (expr ast.Expr) } +[heap] pub struct Gen { out_name string pref &pref.Preferences // Preferences shared from V struct @@ -67,7 +68,7 @@ fn (g &Gen) get_backend() ?CodeGen { } pub fn gen(files []ast.File, table &ast.Table, out_name string, pref &pref.Preferences) (int, int) { - mut g := Gen{ + mut g := &Gen{ table: table sect_header_name_pos: 0 out_name: out_name diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 6c028a3ee3..e3cf22d809 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -292,19 +292,8 @@ 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{ @@ -312,7 +301,6 @@ fn (mut p Parser) fn_decl() ast.FnDecl { 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 @@ -601,19 +589,8 @@ 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{ @@ -623,7 +600,6 @@ 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 }) } diff --git a/vlib/v/tests/defer_return_test.v b/vlib/v/tests/defer_return_test.v index da5052f496..a04e41309d 100644 --- a/vlib/v/tests/defer_return_test.v +++ b/vlib/v/tests/defer_return_test.v @@ -1,18 +1,19 @@ -struct Qwe { +[heap] +struct Hwe { mut: n int } -fn mut_x(mut x Qwe) &Qwe { +fn mut_x(mut x Hwe) &Hwe { n := x.n // defer statement should not have run, yet assert n == 10 x.n += 5 - return unsafe { &x } + return &x } -fn deferer() &Qwe { - mut s := &Qwe{ +fn deferer() &Hwe { + mut s := &Hwe{ n: 10 } defer { @@ -28,6 +29,15 @@ fn test_defer_in_return() { assert q.n == 17 } +struct Qwe { +mut: + n int +} + +fn ret_ref(mut x Qwe) &Qwe { + return unsafe { x } +} + fn defer_multi_ret(mut a Qwe) (int, f64) { defer { a.n *= 2 diff --git a/vlib/v/tests/generics_call_with_reference_arg_test.v b/vlib/v/tests/generics_call_with_reference_arg_test.v index c80543b196..9373a8cd12 100644 --- a/vlib/v/tests/generics_call_with_reference_arg_test.v +++ b/vlib/v/tests/generics_call_with_reference_arg_test.v @@ -5,7 +5,7 @@ mut: } fn (mut s MyStruct) add(e &T) bool { - s.buffer[0] = e + s.buffer[0] = unsafe { e } return true } diff --git a/vlib/v/tests/heap_reference_test.v b/vlib/v/tests/heap_reference_test.v index ff9eecba8d..24da82c358 100644 --- a/vlib/v/tests/heap_reference_test.v +++ b/vlib/v/tests/heap_reference_test.v @@ -26,7 +26,7 @@ pub fn (mut gitstructure GitStructure) repo_get(name string) ?&GitRepo { } fn test_opt_ref_return() { - mut gitstruct := &GitStructure{ + mut gitstruct := GitStructure{ root: 'r' repos: [ &GitRepo{ diff --git a/vlib/v/tests/heap_struct_test.v b/vlib/v/tests/heap_struct_test.v new file mode 100644 index 0000000000..e3a488c13c --- /dev/null +++ b/vlib/v/tests/heap_struct_test.v @@ -0,0 +1,61 @@ +[heap] +struct Abc { +mut: + n int +} + +struct St { + Abc +} + +struct Qwe { +mut: + f f64 + a Abc +} + +fn pass_abc(q &Abc) &Abc { + return q +} + +fn pass_st(q &St) &St { + return q +} + +fn pass_qwe(q &Qwe) &Qwe { + return q +} + +fn get_ref_structs() (&Abc, &St, &Qwe) { + a := Abc{ + n: 3 + } + b := St{Abc{ + n: 7 + }} + x := Qwe{ + f: 12.25 + a: Abc{ + n: 23 + } + } + aa := pass_abc(&a) + bb := pass_st(&b) + xx := pass_qwe(&x) + return aa, bb, xx +} + +fn owerwrite_stack() f64 { + a := 12.5 + b := 3.5 + c := a + b + return c +} + +fn test_ref_struct() { + u, v, w := get_ref_structs() + d := owerwrite_stack() + assert u.n == 3 + assert v.n == 7 + assert w.a.n == 23 +} diff --git a/vlib/v/tests/method_call_chain_test.v b/vlib/v/tests/method_call_chain_test.v index 844c36117a..b062f4b747 100644 --- a/vlib/v/tests/method_call_chain_test.v +++ b/vlib/v/tests/method_call_chain_test.v @@ -11,17 +11,17 @@ fn new(x int) &Test { fn (mut t Test) inc() &Test { t.val++ - return t + return unsafe { t } } fn (mut t Test) add(x int) &Test { t.val += x - return t + return unsafe { t } } fn (mut t Test) div(x int) &Test { t.val /= x - return t + return unsafe { t } } fn test_method_call_chains() { 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 30a5da4b05..f89e7f9757 100644 --- a/vlib/v/tests/mut_receiver_returned_as_reference_test.v +++ b/vlib/v/tests/mut_receiver_returned_as_reference_test.v @@ -7,7 +7,7 @@ mut: fn (mut p Player) set_name(name string) &Player { p.name = name - return p // because of automatic (de)reference of return values + return unsafe { p } // because of automatic (de)reference of return values } // NB: `p` is declared as a `mut` parameter, diff --git a/vlib/v/tests/option_test.v b/vlib/v/tests/option_test.v index e73934b44b..f09aa82932 100644 --- a/vlib/v/tests/option_test.v +++ b/vlib/v/tests/option_test.v @@ -231,7 +231,7 @@ fn opt_ptr(a &int) ?&int { if isnil(a) { return none } - return a + return unsafe { a } } fn test_opt_ptr() { diff --git a/vlib/v/tests/struct_test.v b/vlib/v/tests/struct_test.v index 0abf360395..e639ed26f2 100644 --- a/vlib/v/tests/struct_test.v +++ b/vlib/v/tests/struct_test.v @@ -264,7 +264,7 @@ fn bar_config(c Config, def int) { fn mut_bar_config(mut c Config, def int) &Config { c.n = c.def assert c.n == def - return c + return unsafe { c } } fn foo_user(u User) {} diff --git a/vlib/vweb/sse/sse.v b/vlib/vweb/sse/sse.v index 5c2fac2819..76d7d7822c 100644 --- a/vlib/vweb/sse/sse.v +++ b/vlib/vweb/sse/sse.v @@ -37,7 +37,7 @@ pub struct SSEMessage { pub fn new_connection(conn &net.TcpConn) &SSEConnection { return &SSEConnection{ - conn: conn + conn: unsafe { conn } } } diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v index 2a2242a84d..6840437eb6 100644 --- a/vlib/vweb/vweb.v +++ b/vlib/vweb/vweb.v @@ -329,7 +329,7 @@ fn handle_conn(mut conn net.TcpConn, mut app T) { } app.Context = Context{ req: req - conn: conn + conn: unsafe { conn } form: map[string]string{} static_files: app.static_files static_mime_types: app.static_mime_types diff --git a/vlib/x/websocket/handshake.v b/vlib/x/websocket/handshake.v index eac1ffbff1..9f3ab000e0 100644 --- a/vlib/x/websocket/handshake.v +++ b/vlib/x/websocket/handshake.v @@ -98,8 +98,8 @@ fn (mut s Server) parse_client_handshake(client_handshake string, mut c Client) server_client := &ServerClient{ resource_name: get_tokens[1] client_key: key - client: c - server: s + client: unsafe { c } + server: unsafe { s } } unsafe { lines.free()