all: promote value type function arguments to heap if necessary (#10528)

pull/10531/head
Uwe Krüger 2021-06-20 17:40:24 +02:00 committed by GitHub
parent 6c8182cc25
commit f32a76b268
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 130 additions and 35 deletions

View File

@ -6411,9 +6411,14 @@ pub fn (mut c Checker) mark_as_referenced(mut node ast.Expr) {
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_ { match type_sym.kind {
info := type_sym.info as ast.Struct .struct_ {
if !info.is_heap { info := type_sym.info as ast.Struct
if !info.is_heap {
node.obj.is_auto_heap = true
}
}
else {
node.obj.is_auto_heap = true node.obj.is_auto_heap = true
} }
} }
@ -7291,6 +7296,16 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
// Make sure all types are valid // Make sure all types are valid
for arg in node.params { for arg in node.params {
c.ensure_type_exists(arg.typ, arg.type_pos) or { return } c.ensure_type_exists(arg.typ, arg.type_pos) or { return }
if !arg.typ.is_ptr() { // value parameter, i.e. on stack - check for `[heap]`
arg_typ_sym := c.table.get_type_symbol(arg.typ)
if arg_typ_sym.kind == .struct_ {
info := arg_typ_sym.info as ast.Struct
if info.is_heap { // set auto_heap to promote value parameter
mut v := node.scope.find_var(arg.name) or { continue }
v.is_auto_heap = true
}
}
}
} }
} }
if node.language == .v && node.name.after_char(`.`) == 'init' && !node.is_method if node.language == .v && node.name.after_char(`.`) == 'init' && !node.is_method

View File

@ -3,13 +3,6 @@ vlib/v/checker/tests/function_missing_return_type.vv:1:1: error: missing return
| ~~~~~~~~~~ | ~~~~~~~~~~
2 | } 2 | }
3 | 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` vlib/v/checker/tests/function_missing_return_type.vv:12:1: error: missing return at end of function `abc`
10 | } 10 | }
11 | 11 |

View File

@ -2444,7 +2444,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
ret_styp := g.typ(val.decl.return_type) ret_styp := g.typ(val.decl.return_type)
g.write('$ret_styp (*$ident.name) (') g.write('$ret_styp (*$ident.name) (')
def_pos := g.definitions.len def_pos := g.definitions.len
g.fn_args(val.decl.params, val.decl.is_variadic) g.fn_args(val.decl.params, val.decl.is_variadic, voidptr(0))
g.definitions.go_back(g.definitions.len - def_pos) g.definitions.go_back(g.definitions.len - def_pos)
g.write(') = ') g.write(') = ')
} else { } else {
@ -2572,7 +2572,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
ret_styp := g.typ(func.func.return_type) ret_styp := g.typ(func.func.return_type)
g.write('$ret_styp (*${g.get_ternary_name(ident.name)}) (') g.write('$ret_styp (*${g.get_ternary_name(ident.name)}) (')
def_pos := g.definitions.len def_pos := g.definitions.len
g.fn_args(func.func.params, func.func.is_variadic) g.fn_args(func.func.params, func.func.is_variadic, voidptr(0))
g.definitions.go_back(g.definitions.len - def_pos) g.definitions.go_back(g.definitions.len - def_pos)
g.write(')') g.write(')')
} else { } else {
@ -6150,7 +6150,7 @@ static inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype
...params[0] ...params[0]
typ: params[0].typ.set_nr_muls(1) typ: params[0].typ.set_nr_muls(1)
} }
fargs, _ := g.fn_args(params, false) // second argument is ignored anyway fargs, _, _ := g.fn_args(params, false, voidptr(0)) // second argument is ignored anyway
methods_wrapper.write_string(g.out.cut_last(g.out.len - params_start_pos)) methods_wrapper.write_string(g.out.cut_last(g.out.len - params_start_pos))
methods_wrapper.writeln(') {') methods_wrapper.writeln(') {')
methods_wrapper.write_string('\t') methods_wrapper.write_string('\t')

View File

@ -264,7 +264,7 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
g.write(fn_header) g.write(fn_header)
} }
arg_start_pos := g.out.len arg_start_pos := g.out.len
fargs, fargtypes := g.fn_args(node.params, node.is_variadic) fargs, fargtypes, heap_promoted := g.fn_args(node.params, node.is_variadic, node.scope)
arg_str := g.out.after(arg_start_pos) arg_str := g.out.after(arg_start_pos)
if node.no_body || ((g.pref.use_cache && g.pref.build_mode != .build_module) && node.is_builtin if node.no_body || ((g.pref.use_cache && g.pref.build_mode != .build_module) && node.is_builtin
&& !g.is_test) || skip { && !g.is_test) || skip {
@ -275,6 +275,11 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
} }
g.definitions.writeln(');') g.definitions.writeln(');')
g.writeln(') {') g.writeln(') {')
for i, is_promoted in heap_promoted {
if is_promoted {
g.writeln('${fargtypes[i]}* ${fargs[i]} = HEAP(${fargtypes[i]}, _v_toheap_${fargs[i]});')
}
}
for defer_stmt in node.defer_stmts { for defer_stmt in node.defer_stmts {
g.writeln('bool ${g.defer_flag_var(defer_stmt)} = false;') g.writeln('bool ${g.defer_flag_var(defer_stmt)} = false;')
for var in defer_stmt.defer_vars { for var in defer_stmt.defer_vars {
@ -386,15 +391,16 @@ fn (mut g Gen) write_defer_stmts_when_needed() {
} }
// fn decl args // fn decl args
fn (mut g Gen) fn_args(args []ast.Param, is_variadic bool) ([]string, []string) { fn (mut g Gen) fn_args(args []ast.Param, is_variadic bool, scope &ast.Scope) ([]string, []string, []bool) {
mut fargs := []string{} mut fargs := []string{}
mut fargtypes := []string{} mut fargtypes := []string{}
mut heap_promoted := []bool{}
if args.len == 0 { if args.len == 0 {
// in C, `()` is untyped, unlike `(void)` // in C, `()` is untyped, unlike `(void)`
g.write('void') g.write('void')
} }
for i, arg in args { for i, arg in args {
caname := if arg.name == '_' { g.new_tmp_var() } else { c_name(arg.name) } mut caname := if arg.name == '_' { g.new_tmp_var() } else { c_name(arg.name) }
typ := g.unwrap_generic(arg.typ) typ := g.unwrap_generic(arg.typ)
arg_type_sym := g.table.get_type_symbol(typ) arg_type_sym := g.table.get_type_symbol(typ)
mut arg_type_name := g.typ(typ) // util.no_dots(arg_type_sym.name) mut arg_type_name := g.typ(typ) // util.no_dots(arg_type_sym.name)
@ -403,22 +409,34 @@ fn (mut g Gen) fn_args(args []ast.Param, is_variadic bool) ([]string, []string)
func := info.func func := info.func
g.write('${g.typ(func.return_type)} (*$caname)(') g.write('${g.typ(func.return_type)} (*$caname)(')
g.definitions.write_string('${g.typ(func.return_type)} (*$caname)(') g.definitions.write_string('${g.typ(func.return_type)} (*$caname)(')
g.fn_args(func.params, func.is_variadic) g.fn_args(func.params, func.is_variadic, voidptr(0))
g.write(')') g.write(')')
g.definitions.write_string(')') g.definitions.write_string(')')
} else { } else {
s := '$arg_type_name $caname' mut heap_prom := false
if scope != voidptr(0) {
if arg.name != '_' {
if v := scope.find_var(arg.name) {
if !v.is_stack_obj && v.is_auto_heap {
heap_prom = true
}
}
}
}
var_name_prefix := if heap_prom { '_v_toheap_' } else { '' }
s := '$arg_type_name $var_name_prefix$caname'
g.write(s) g.write(s)
g.definitions.write_string(s) g.definitions.write_string(s)
fargs << caname fargs << caname
fargtypes << arg_type_name fargtypes << arg_type_name
heap_promoted << heap_prom
} }
if i < args.len - 1 { if i < args.len - 1 {
g.write(', ') g.write(', ')
g.definitions.write_string(', ') g.definitions.write_string(', ')
} }
} }
return fargs, fargtypes return fargs, fargtypes, heap_promoted
} }
fn (mut g Gen) call_expr(node ast.CallExpr) { fn (mut g Gen) call_expr(node ast.CallExpr) {

View File

@ -310,10 +310,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
scope: 0 scope: 0
} }
} }
mut is_stack_obj := true is_stack_obj := !param.typ.has_flag(.shared_f) && (param.is_mut || param.typ.is_ptr())
if param.typ.has_flag(.shared_f) {
is_stack_obj = false
}
p.scope.register(ast.Var{ p.scope.register(ast.Var{
name: param.name name: param.name
typ: param.typ typ: param.typ
@ -628,10 +625,7 @@ 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_stack_obj := true is_stack_obj := !arg.typ.has_flag(.shared_f) && (arg.is_mut || arg.typ.is_ptr())
if arg.typ.has_flag(.shared_f) {
is_stack_obj = false
}
p.scope.register(ast.Var{ p.scope.register(ast.Var{
name: arg.name name: arg.name
typ: arg.typ typ: arg.typ

View File

@ -6,7 +6,8 @@ mut:
} }
const ( const (
sleep_time = time.millisecond * 50 run_time = time.millisecond * 200 // must be big enough to ensure threads have started
sleep_time = time.millisecond * 250 // some tolerance added
) )
fn test_return_lock() { fn test_return_lock() {
@ -16,12 +17,12 @@ fn test_return_lock() {
go fn (shared s AA, start time.Time) { go fn (shared s AA, start time.Time) {
for { for {
reader(shared s) reader(shared s)
if time.now() - start > sleep_time { if time.now() - start > run_time {
exit(0) exit(0)
} }
} }
}(shared s, start) }(shared s, start)
time.sleep(sleep_time * 2) time.sleep(sleep_time)
assert false assert false
} }
@ -30,7 +31,7 @@ fn printer(shared s AA, start time.Time) {
lock s { lock s {
assert s.b in ['0', '1', '2', '3', '4', '5'] assert s.b in ['0', '1', '2', '3', '4', '5']
} }
if time.now() - start > time.millisecond * 50 { if time.now() - start > run_time {
exit(0) exit(0)
} }
} }

View File

@ -58,4 +58,77 @@ fn test_ref_struct() {
assert u.n == 3 assert u.n == 3
assert v.n == 7 assert v.n == 7
assert w.a.n == 23 assert w.a.n == 23
assert d == 16.0
}
fn return_heap_obj_value_as_ref(qpast Qwe) &Qwe {
return &qpast
}
fn test_value_ref_heap_struct() {
mut x := Qwe{
f: -13.25
a: Abc{
n: -129
}
}
y := return_heap_obj_value_as_ref(x)
x.f = 22.0625
d := owerwrite_stack()
assert typeof(y).name == '&Qwe'
assert x.f == 22.0625
assert x.a.n == -129
assert y.f == -13.25
assert y.a.n == -129
assert d == 16.0
}
struct NotHeap {
mut:
f f64
}
fn return_struct_value_as_ref(q NotHeap) &NotHeap {
return &q
}
fn test_value_ref_struct() {
mut x := NotHeap{
f: -17.125
}
y := return_struct_value_as_ref(x)
x.f = 91.0625
d := owerwrite_stack()
assert typeof(y).name == '&NotHeap'
assert y.f == -17.125
assert x.f == 91.0625
assert d == 16.0
}
fn get_int_ref() &int {
i := 49154
return &i
}
fn test_int_ref() {
iptr := get_int_ref()
assert typeof(iptr).name == '&int'
d := owerwrite_stack()
assert *iptr == 49154
assert d == 16.0
}
fn pass_f64_as_ref(f f64) &f64 {
return &f
}
fn test_value_as_ref() {
mut x := -31.75
y := pass_f64_as_ref(x)
assert typeof(y).name == '&f64'
x = 23.0625
d := owerwrite_stack()
assert x == 23.0625
assert *y == -31.75
assert d == 16.0
} }

View File

@ -6,7 +6,8 @@ mut:
} }
const ( const (
sleep_time = time.millisecond * 50 run_time = time.millisecond * 200 // must be big enough to ensure threads have started
sleep_time = time.millisecond * 250 // some tolerance added
) )
fn test_return_lock() { fn test_return_lock() {
@ -16,12 +17,12 @@ fn test_return_lock() {
go fn (shared s AA, start time.Time) { go fn (shared s AA, start time.Time) {
for { for {
reader(shared s) reader(shared s)
if time.now() - start > sleep_time { if time.now() - start > run_time {
exit(0) exit(0)
} }
} }
}(shared s, start) }(shared s, start)
time.sleep(sleep_time * 2) time.sleep(sleep_time)
assert false assert false
} }
@ -30,7 +31,7 @@ fn printer(shared s AA, start time.Time) {
lock s { lock s {
assert s.b in ['0', '1', '2', '3', '4', '5'] assert s.b in ['0', '1', '2', '3', '4', '5']
} }
if time.now() - start > time.millisecond * 50 { if time.now() - start > run_time {
exit(0) exit(0)
} }
} }