all: automatically move (some) referenced objects to heap (#9873)

pull/9882/head
Uwe Krüger 2021-04-25 20:40:38 +02:00 committed by GitHub
parent 00261afbc1
commit 3c0a368af3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 264 additions and 51 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -495,6 +495,9 @@ pub mut:
// (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_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

View File

@ -7,6 +7,7 @@ import v.cflag
import v.token
import v.util
[heap]
pub struct Table {
pub mut:
type_symbols []TypeSymbol

View File

@ -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,10 +5940,20 @@ 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 {
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() {
return right_type.deref()

View File

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

View File

@ -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` {

View File

@ -2145,23 +2145,38 @@ 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)
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 {
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*/')
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) {
@ -4622,11 +4663,22 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
g.write('\t$base_type $cond_var_name = ')
g.expr(branch.cond.expr)
g.writeln(';')
} else {
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;')
}
}
}
}
else {
g.write('if (')
g.expr(branch.cond)

View File

@ -69,7 +69,9 @@ fn (mut g Gen) process_fn_decl(node ast.FnDecl) {
}
}
keep_fn_decl := g.fn_decl
unsafe {
g.fn_decl = &node
}
if node.name == 'main.main' {
g.has_main = true
}

View File

@ -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) {
unsafe {
g.fn_decl = &it
}
has_go := fn_has_go(it)
is_main := it.name == 'main.main'
g.gen_attrs(it.attrs)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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() {

View File

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

View File

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

View File

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

View File

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