all: various fixes for [heap]/auto-heap handling (#10033)

pull/10046/head
Uwe Krüger 2021-05-07 14:58:48 +02:00 committed by GitHub
parent 5b4eef8010
commit d26ac5692e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 279 additions and 149 deletions

View File

@ -43,7 +43,7 @@ fn (mut ctx Context) println(s string) {
} }
fn do_timeout(c &Context) { fn do_timeout(c &Context) {
mut ctx := c mut ctx := unsafe { c }
time.sleep(ctx.timeout_ms * time.millisecond) time.sleep(ctx.timeout_ms * time.millisecond)
exit(ctx.exitcode) exit(ctx.exitcode)
} }

View File

@ -171,7 +171,7 @@ fn (mut context Context) get_changed_vfiles() int {
} }
fn change_detection_loop(ocontext &Context) { fn change_detection_loop(ocontext &Context) {
mut context := ocontext mut context := unsafe { ocontext }
for { for {
if context.v_cycles >= max_v_cycles || context.scan_cycles >= max_scan_cycles { if context.v_cycles >= max_v_cycles || context.scan_cycles >= max_scan_cycles {
context.is_exiting = true context.is_exiting = true

View File

@ -13,7 +13,7 @@ mut:
} }
fn my_audio_stream_callback(buffer &f32, num_frames int, num_channels int, mut acontext AppState) { 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++ { for frame := 0; frame < num_frames; frame++ {
t := int(f32(acontext.frame_0 + frame) * 0.245) t := int(f32(acontext.frame_0 + frame) * 0.245)
// "Techno" by Gabriel Miceli // "Techno" by Gabriel Miceli

View File

@ -16,6 +16,7 @@ const (
orange = ui.Color{255, 140, 0} orange = ui.Color{255, 140, 0}
) )
[heap]
struct App { struct App {
mut: mut:
tui &ui.Context = 0 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 b.pos.y += b.vel.y * b.acc.y * dt
} }
[heap]
struct Game { struct Game {
mut: mut:
app &App = 0 app &App = 0

View File

@ -256,6 +256,7 @@ fn (mut r Rat) randomize() {
r.app.height - block_size - buffer) r.app.height - block_size - buffer)
} }
[heap]
struct App { struct App {
mut: mut:
termui &termui.Context = 0 termui &termui.Context = 0

View File

@ -9,7 +9,7 @@ pub fn memcpy(dest &C.void, src &C.void, n size_t) &C.void {
dest_[i] = src_[i] dest_[i] = src_[i]
} }
} }
return dest return unsafe { dest }
} }
[export: 'malloc'] [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))) } old_size := unsafe { *(&u64(old_area - sizeof(u64))) }
if u64(new_size) <= old_size { if u64(new_size) <= old_size {
return old_area return unsafe { old_area }
} else { } else {
new_area := unsafe { malloc(int(new_size)) } new_area := unsafe { malloc(int(new_size)) }
unsafe { memmove(new_area, old_area, size_t(old_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) s_[i] = char(c)
} }
} }
return s return unsafe { s }
} }
[unsafe] [unsafe]
@ -74,7 +74,7 @@ fn memmove(dest &C.void, src &C.void, n size_t) &C.void {
} }
} }
unsafe { free(temp_buf) } unsafe { free(temp_buf) }
return dest return unsafe { dest }
} }
[export: 'calloc'] [export: 'calloc']

View File

@ -77,7 +77,7 @@ pub fn tos(s &byte, len int) string {
panic('tos(): nil string') panic('tos(): nil string')
} }
return string{ return string{
str: s str: unsafe { s }
len: len len: len
} }
} }
@ -96,7 +96,7 @@ pub fn tos2(s &byte) string {
panic('tos2: nil string') panic('tos2: nil string')
} }
return string{ return string{
str: s str: unsafe { s }
len: unsafe { vstrlen(s) } len: unsafe { vstrlen(s) }
} }
} }
@ -146,7 +146,7 @@ pub fn tos_lit(s &char) string {
[unsafe] [unsafe]
pub fn (bp &byte) vstring() string { pub fn (bp &byte) vstring() string {
return string{ return string{
str: bp str: unsafe { bp }
len: unsafe { C.strlen(&char(bp)) } len: unsafe { C.strlen(&char(bp)) }
} }
} }
@ -156,7 +156,7 @@ pub fn (bp &byte) vstring() string {
[unsafe] [unsafe]
pub fn (bp &byte) vstring_with_len(len int) string { pub fn (bp &byte) vstring_with_len(len int) string {
return string{ return string{
str: bp str: unsafe { bp }
len: len len: len
is_lit: 0 is_lit: 0
} }
@ -194,7 +194,7 @@ pub fn (cp &char) vstring_with_len(len int) string {
[unsafe] [unsafe]
pub fn (bp &byte) vstring_literal() string { pub fn (bp &byte) vstring_literal() string {
return string{ return string{
str: bp str: unsafe { bp }
len: unsafe { C.strlen(&char(bp)) } len: unsafe { C.strlen(&char(bp)) }
is_lit: 1 is_lit: 1
} }
@ -205,7 +205,7 @@ pub fn (bp &byte) vstring_literal() string {
[unsafe] [unsafe]
pub fn (bp &byte) vstring_literal_with_len(len int) string { pub fn (bp &byte) vstring_literal_with_len(len int) string {
return string{ return string{
str: bp str: unsafe { bp }
len: len len: len
is_lit: 1 is_lit: 1
} }

View File

@ -94,7 +94,7 @@ pub fn (mut cmd Command) add_command(command Command) {
println('Command with the name `$subcmd.name` already exists') println('Command with the name `$subcmd.name` already exists')
exit(1) exit(1)
} }
subcmd.parent = cmd subcmd.parent = unsafe { cmd }
cmd.commands << subcmd cmd.commands << subcmd
} }
@ -102,7 +102,7 @@ pub fn (mut cmd Command) add_command(command Command) {
// is linked as a chain. // is linked as a chain.
pub fn (mut cmd Command) setup() { pub fn (mut cmd Command) setup() {
for mut subcmd in cmd.commands { for mut subcmd in cmd.commands {
subcmd.parent = cmd subcmd.parent = unsafe { cmd }
subcmd.setup() subcmd.setup()
} }
} }

View File

@ -100,6 +100,7 @@ pub:
native_rendering bool // Cocoa on macOS/iOS, GDI+ on Windows native_rendering bool // Cocoa on macOS/iOS, GDI+ on Windows
} }
[heap]
pub struct Context { pub struct Context {
render_text bool render_text bool
mut: mut:

View File

@ -10,6 +10,7 @@ import sokol
import sokol.sgl import sokol.sgl
import stbi import stbi
[heap]
pub struct Image { pub struct Image {
pub mut: pub mut:
id int id int

View File

@ -43,7 +43,7 @@ pub fn vec3(x f32, y f32, z f32) Vec3 {
fn mat4(f &f32) Mat4 { fn mat4(f &f32) Mat4 {
res := Mat4{ res := Mat4{
data: f data: unsafe { f }
} }
return res return res
} }

View File

@ -20,7 +20,7 @@ pub fn (mut r Response) write_string(s string) {
[inline] [inline]
pub fn (mut r Response) http_ok() &Response { pub fn (mut r Response) http_ok() &Response {
r.write_string('HTTP/1.1 200 OK\r\n') r.write_string('HTTP/1.1 200 OK\r\n')
return r return unsafe { r }
} }
[inline] [inline]
@ -29,7 +29,7 @@ pub fn (mut r Response) header(k string, v string) &Response {
r.write_string(': ') r.write_string(': ')
r.write_string(v) r.write_string(v)
r.write_string('\r\n') r.write_string('\r\n')
return r return unsafe { r }
} }
[inline] [inline]
@ -39,13 +39,13 @@ pub fn (mut r Response) header_date() &Response {
r.buf += cpy(r.buf, r.date, 29) r.buf += cpy(r.buf, r.date, 29)
} }
r.write_string('\r\n') r.write_string('\r\n')
return r return unsafe { r }
} }
[inline] [inline]
pub fn (mut r Response) header_server() &Response { pub fn (mut r Response) header_server() &Response {
r.write_string('Server: V\r\n') r.write_string('Server: V\r\n')
return r return unsafe { r }
} }
[inline] [inline]
@ -53,25 +53,25 @@ pub fn (mut r Response) content_type(s string) &Response {
r.write_string('Content-Type: ') r.write_string('Content-Type: ')
r.write_string(s) r.write_string(s)
r.write_string('\r\n') r.write_string('\r\n')
return r return unsafe { r }
} }
[inline] [inline]
pub fn (mut r Response) html() &Response { pub fn (mut r Response) html() &Response {
r.write_string('Content-Type: text/html\r\n') r.write_string('Content-Type: text/html\r\n')
return r return unsafe { r }
} }
[inline] [inline]
pub fn (mut r Response) plain() &Response { pub fn (mut r Response) plain() &Response {
r.write_string('Content-Type: text/plain\r\n') r.write_string('Content-Type: text/plain\r\n')
return r return unsafe { r }
} }
[inline] [inline]
pub fn (mut r Response) json() &Response { pub fn (mut r Response) json() &Response {
r.write_string('Content-Type: application/json\r\n') r.write_string('Content-Type: application/json\r\n')
return r return unsafe { r }
} }
[inline] [inline]

View File

@ -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 // should be restored if work with the custom RNG is complete. It is not necessary to restore if the
// program terminates soon afterwards. // program terminates soon afterwards.
pub fn set_rng(rng &PRNG) { 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 // seed sets the given array of `u32` values as the seed for the `default_rng`. The default_rng is

View File

@ -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) return C.sg_append_buffer(b.index_buffer, &range)
} }
[heap]
pub struct C.sg_shader_desc { pub struct C.sg_shader_desc {
pub mut: pub mut:
_start_canary u32 _start_canary u32

View File

@ -510,7 +510,6 @@ pub mut:
is_or bool // `x := foo() or { ... }` is_or bool // `x := foo() or { ... }`
is_tmp bool // for tmp for loop vars, so that autofree can skip them 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_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) is_stack_obj bool // may be pointer to stack value (`mut` or `&` arg and not [heap] struct)
} }

View File

@ -3,6 +3,7 @@
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module ast module ast
[heap]
pub struct Scope { pub struct Scope {
pub mut: pub mut:
// mut: // mut:

View File

@ -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 // search from current type up through each parent looking for method
pub fn (t &Table) type_find_method(s &TypeSymbol, name string) ?Fn { 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') // 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 { for {
if method := ts.find_method(name) { if method := ts.find_method(name) {
return method 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 // search from current type up through each parent looking for field
pub fn (t &Table) find_field(s &TypeSymbol, name string) ?StructField { 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') // 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 { for {
match mut ts.info { match mut ts.info {
Struct { 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) { pub fn (t &Table) resolve_common_sumtype_fields(sym_ &TypeSymbol) {
mut sym := sym_ mut sym := unsafe { sym_ }
mut info := sym.info as SumType mut info := sym.info as SumType
if info.found_fields { if info.found_fields {
return return

View File

@ -64,6 +64,7 @@ pub fn pref_arch_to_table_language(pref_arch pref.Arch) Language {
// * Table.type_kind(typ) not TypeSymbol.kind. // * Table.type_kind(typ) not TypeSymbol.kind.
// Each TypeSymbol is entered into `Table.types`. // Each TypeSymbol is entered into `Table.types`.
// See also: Table.get_type_symbol. // See also: Table.get_type_symbol.
pub struct TypeSymbol { pub struct TypeSymbol {
pub: pub:
parent_idx int 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 { pub fn (t TypeSymbol) str() string {
return t.name return t.name

View File

@ -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) { pub fn (mut c Checker) change_current_file(file &ast.File) {
c.file = file c.file = unsafe { file }
c.vmod_file_content = '' c.vmod_file_content = ''
c.mod = file.mod.name 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 ' + c.error('struct `$type_sym.name` is declared with a `[noinit]` attribute, so ' +
'it cannot be initialized with `$type_sym.name{}`', struct_init.pos) '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 { if type_sym.name.len == 1 && c.cur_fn.generic_names.len == 0 {
c.error('unknown struct `$type_sym.name`', struct_init.pos) 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 continue
} }
} }
mut expr_type := ast.Type(0)
mut expected_type := ast.Type(0)
if is_embed { if is_embed {
c.expected_type = embed_type expected_type = embed_type
expr_type := c.expr(field.expr) c.expected_type = expected_type
expr_type = c.expr(field.expr)
expr_type_sym := c.table.get_type_symbol(expr_type) expr_type_sym := c.table.get_type_symbol(expr_type)
if expr_type != ast.void_type && expr_type_sym.kind != .placeholder { if expr_type != ast.void_type && expr_type_sym.kind != .placeholder {
c.check_expected(expr_type, embed_type) or { 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 { } else {
inited_fields << field_name inited_fields << field_name
field_type_sym := c.table.get_type_symbol(info_field.typ) field_type_sym := c.table.get_type_symbol(info_field.typ)
c.expected_type = info_field.typ expected_type = info_field.typ
mut expr_type := c.expr(field.expr) c.expected_type = expected_type
expr_type = c.expr(field.expr)
if !info_field.typ.has_flag(.optional) { if !info_field.typ.has_flag(.optional) {
expr_type = c.check_expr_opt_call(field.expr, expr_type) 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].typ = expr_type
struct_init.fields[i].expected_type = info_field.typ 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 // Check uninitialized refs/sum types
for field in info.fields { 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', 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) 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 { if exp_is_optional && return_stmt.exprs.len > 0 {
expr0 := return_stmt.exprs[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) c.fail_if_immutable(left)
// left_type = c.expr(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 assign_stmt.left_types << left_type
match mut left { match mut left {
ast.Ident { ast.Ident {
@ -3216,6 +3282,11 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
if left.obj.is_auto_deref { if left.obj.is_auto_deref {
left.obj.is_used = true 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 { ast.GlobalField {
left.obj.typ = left_type left.obj.typ = left_type
@ -6046,20 +6117,30 @@ pub fn (mut c Checker) mark_as_referenced(mut node ast.Expr) {
if mut node.obj is ast.Var { if mut node.obj is ast.Var {
mut obj := unsafe { &node.obj } mut obj := unsafe { &node.obj }
if c.fn_scope != voidptr(0) { 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) type_sym := c.table.get_type_symbol(obj.typ.set_nr_muls(0))
if obj.is_stack_obj { if obj.is_stack_obj && !type_sym.is_heap() {
c.error('`$node.name` cannot be referenced outside `unsafe` blocks as it might be stored on stack. Consider declaring `$type_sym.name` as `[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) node.pos)
} else if type_sym.kind == .array_fixed { } 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', c.error('cannot reference fixed array `$node.name` outside `unsafe` blocks as it is supposed to be stored on stack',
node.pos) node.pos)
} else { } else {
if type_sym.kind == .struct_ {
info := type_sym.info as ast.Struct
if !info.is_heap {
node.obj.is_auto_heap = true node.obj.is_auto_heap = true
} }
} }
} }
}
}
ast.SelectorExpr { ast.SelectorExpr {
if !node.expr_type.is_ptr() { if !node.expr_type.is_ptr() {
c.mark_as_referenced(mut &node.expr) c.mark_as_referenced(mut &node.expr)
@ -6930,7 +7011,7 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
} }
} }
c.expected_type = ast.void_type c.expected_type = ast.void_type
c.cur_fn = node c.cur_fn = unsafe { node }
// Add return if `fn(...) ? {...}` have no return at end // Add return if `fn(...) ? {...}` have no return at end
if node.return_type != ast.void_type && node.return_type.has_flag(.optional) 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) { && (node.stmts.len == 0 || node.stmts[node.stmts.len - 1] !is ast.Return) {

View File

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

View File

@ -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')
}

View File

@ -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 | }

View File

@ -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
}

View File

@ -32,6 +32,7 @@ mut:
methods map[string][]ast.FnDecl methods map[string][]ast.FnDecl
} }
[heap]
struct JsGen { struct JsGen {
table &ast.Table table &ast.Table
pref &pref.Preferences pref &pref.Preferences

View File

@ -20,6 +20,7 @@ interface CodeGen {
// XXX WHY gen_exit fn (expr ast.Expr) // XXX WHY gen_exit fn (expr ast.Expr)
} }
[heap]
pub struct Gen { pub struct Gen {
out_name string out_name string
pref &pref.Preferences // Preferences shared from V struct 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) { 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 table: table
sect_header_name_pos: 0 sect_header_name_pos: 0
out_name: out_name out_name: out_name

View File

@ -292,19 +292,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
scope: 0 scope: 0
} }
} }
mut is_heap_ref := false // args are only borrowed, so assume maybe on stack
mut is_stack_obj := true 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) { if param.typ.has_flag(.shared_f) {
is_heap_ref = true
is_stack_obj = false is_stack_obj = false
} }
p.scope.register(ast.Var{ p.scope.register(ast.Var{
@ -312,7 +301,6 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
typ: param.typ typ: param.typ
is_mut: param.is_mut is_mut: param.is_mut
is_auto_deref: param.is_mut || param.is_auto_rec is_auto_deref: param.is_mut || param.is_auto_rec
is_heap_ref: is_heap_ref
is_stack_obj: is_stack_obj is_stack_obj: is_stack_obj
pos: param.pos pos: param.pos
is_used: true is_used: true
@ -601,19 +589,8 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
if arg.name.len == 0 { if arg.name.len == 0 {
p.error_with_pos('use `_` to name an unused parameter', arg.pos) 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 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) { if arg.typ.has_flag(.shared_f) {
is_heap_ref = true
is_stack_obj = false is_stack_obj = false
} }
p.scope.register(ast.Var{ p.scope.register(ast.Var{
@ -623,7 +600,6 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
pos: arg.pos pos: arg.pos
is_used: true is_used: true
is_arg: true is_arg: true
is_heap_ref: is_heap_ref
is_stack_obj: is_stack_obj is_stack_obj: is_stack_obj
}) })
} }

View File

@ -1,18 +1,19 @@
struct Qwe { [heap]
struct Hwe {
mut: mut:
n int n int
} }
fn mut_x(mut x Qwe) &Qwe { fn mut_x(mut x Hwe) &Hwe {
n := x.n n := x.n
// defer statement should not have run, yet // defer statement should not have run, yet
assert n == 10 assert n == 10
x.n += 5 x.n += 5
return unsafe { &x } return &x
} }
fn deferer() &Qwe { fn deferer() &Hwe {
mut s := &Qwe{ mut s := &Hwe{
n: 10 n: 10
} }
defer { defer {
@ -28,6 +29,15 @@ fn test_defer_in_return() {
assert q.n == 17 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) { fn defer_multi_ret(mut a Qwe) (int, f64) {
defer { defer {
a.n *= 2 a.n *= 2

View File

@ -5,7 +5,7 @@ mut:
} }
fn (mut s MyStruct<T>) add(e &T) bool { fn (mut s MyStruct<T>) add(e &T) bool {
s.buffer[0] = e s.buffer[0] = unsafe { e }
return true return true
} }

View File

@ -26,7 +26,7 @@ pub fn (mut gitstructure GitStructure) repo_get(name string) ?&GitRepo {
} }
fn test_opt_ref_return() { fn test_opt_ref_return() {
mut gitstruct := &GitStructure{ mut gitstruct := GitStructure{
root: 'r' root: 'r'
repos: [ repos: [
&GitRepo{ &GitRepo{

View File

@ -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
}

View File

@ -11,17 +11,17 @@ fn new(x int) &Test {
fn (mut t Test) inc() &Test { fn (mut t Test) inc() &Test {
t.val++ t.val++
return t return unsafe { t }
} }
fn (mut t Test) add(x int) &Test { fn (mut t Test) add(x int) &Test {
t.val += x t.val += x
return t return unsafe { t }
} }
fn (mut t Test) div(x int) &Test { fn (mut t Test) div(x int) &Test {
t.val /= x t.val /= x
return t return unsafe { t }
} }
fn test_method_call_chains() { fn test_method_call_chains() {

View File

@ -7,7 +7,7 @@ mut:
fn (mut p Player) set_name(name string) &Player { fn (mut p Player) set_name(name string) &Player {
p.name = name 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, // NB: `p` is declared as a `mut` parameter,

View File

@ -231,7 +231,7 @@ fn opt_ptr(a &int) ?&int {
if isnil(a) { if isnil(a) {
return none return none
} }
return a return unsafe { a }
} }
fn test_opt_ptr() { fn test_opt_ptr() {

View File

@ -264,7 +264,7 @@ fn bar_config(c Config, def int) {
fn mut_bar_config(mut c Config, def int) &Config { fn mut_bar_config(mut c Config, def int) &Config {
c.n = c.def c.n = c.def
assert c.n == def assert c.n == def
return c return unsafe { c }
} }
fn foo_user(u User) {} fn foo_user(u User) {}

View File

@ -37,7 +37,7 @@ pub struct SSEMessage {
pub fn new_connection(conn &net.TcpConn) &SSEConnection { pub fn new_connection(conn &net.TcpConn) &SSEConnection {
return &SSEConnection{ return &SSEConnection{
conn: conn conn: unsafe { conn }
} }
} }

View File

@ -329,7 +329,7 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T) {
} }
app.Context = Context{ app.Context = Context{
req: req req: req
conn: conn conn: unsafe { conn }
form: map[string]string{} form: map[string]string{}
static_files: app.static_files static_files: app.static_files
static_mime_types: app.static_mime_types static_mime_types: app.static_mime_types

View File

@ -98,8 +98,8 @@ fn (mut s Server) parse_client_handshake(client_handshake string, mut c Client)
server_client := &ServerClient{ server_client := &ServerClient{
resource_name: get_tokens[1] resource_name: get_tokens[1]
client_key: key client_key: key
client: c client: unsafe { c }
server: s server: unsafe { s }
} }
unsafe { unsafe {
lines.free() lines.free()