checker,gen: allow using methods as function pointers (#14407)
parent
c2bc9f4960
commit
c01a8a1737
|
@ -1850,6 +1850,27 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type {
|
||||||
node.typ = field.typ
|
node.typ = field.typ
|
||||||
return field.typ
|
return field.typ
|
||||||
}
|
}
|
||||||
|
if mut method := c.table.find_method(sym, field_name) {
|
||||||
|
receiver := method.params[0].typ
|
||||||
|
if receiver.nr_muls() > 0 {
|
||||||
|
if !c.inside_unsafe {
|
||||||
|
rec_sym := c.table.sym(receiver.set_nr_muls(0))
|
||||||
|
if !rec_sym.is_heap() {
|
||||||
|
suggestion := if rec_sym.kind == .struct_ {
|
||||||
|
'declaring `$rec_sym.name` as `[heap]`'
|
||||||
|
} else {
|
||||||
|
'wrapping the `$rec_sym.name` object in a `struct` declared as `[heap]`'
|
||||||
|
}
|
||||||
|
c.error('method `${c.table.type_to_str(receiver.idx())}.$method.name` cannot be used as a variable outside `unsafe` blocks as its receiver might refer to an object stored on stack. Consider ${suggestion}.',
|
||||||
|
node.expr.pos().extend(node.pos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
method.params = method.params[1..]
|
||||||
|
fn_type := ast.new_type(c.table.find_or_register_fn_type(c.mod, method, false,
|
||||||
|
true))
|
||||||
|
return fn_type
|
||||||
|
}
|
||||||
if sym.kind !in [.struct_, .aggregate, .interface_, .sum_type] {
|
if sym.kind !in [.struct_, .aggregate, .interface_, .sum_type] {
|
||||||
if sym.kind != .placeholder {
|
if sym.kind != .placeholder {
|
||||||
unwrapped_sym := c.table.sym(c.unwrap_generic(typ))
|
unwrapped_sym := c.table.sym(c.unwrap_generic(typ))
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
vlib/v/checker/tests/unsafe_method_as_field.vv:23:7: error: method `Foo.ref` cannot be used as a variable outside `unsafe` blocks as its receiver might refer to an object stored on stack. Consider declaring `Foo` as `[heap]`.
|
||||||
|
21 | f := Foo{}
|
||||||
|
22 | _ := f.no_ref // no error
|
||||||
|
23 | _ := f.ref // error
|
||||||
|
| ~~~~~
|
||||||
|
24 |
|
||||||
|
25 | b := Bar{}
|
|
@ -0,0 +1,27 @@
|
||||||
|
struct Foo {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f Foo) no_ref() int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f &Foo) ref() int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[heap]
|
||||||
|
struct Bar {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f &Bar) ref() int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
f := Foo{}
|
||||||
|
_ := f.no_ref // no error
|
||||||
|
_ := f.ref // error
|
||||||
|
|
||||||
|
b := Bar{}
|
||||||
|
_ := b.ref // no error
|
||||||
|
}
|
|
@ -211,7 +211,11 @@ fn (mut g Gen) gen_assign_stmt(node_ ast.AssignStmt) {
|
||||||
} else if g.inside_for_c_stmt {
|
} else if g.inside_for_c_stmt {
|
||||||
g.expr(val)
|
g.expr(val)
|
||||||
} else {
|
} else {
|
||||||
g.write('{$styp _ = ')
|
if left_sym.kind == .function {
|
||||||
|
g.write('{void* _ = ')
|
||||||
|
} else {
|
||||||
|
g.write('{$styp _ = ')
|
||||||
|
}
|
||||||
g.expr(val)
|
g.expr(val)
|
||||||
g.writeln(';}')
|
g.writeln(';}')
|
||||||
}
|
}
|
||||||
|
|
|
@ -3339,6 +3339,64 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if m := g.table.find_method(sym, node.field_name) {
|
||||||
|
mut has_embeds := false
|
||||||
|
if sym.info in [ast.Struct, ast.Aggregate] {
|
||||||
|
if node.from_embed_types.len > 0 {
|
||||||
|
has_embeds = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !has_embeds {
|
||||||
|
receiver := m.params[0]
|
||||||
|
expr_styp := g.typ(node.expr_type.idx())
|
||||||
|
data_styp := g.typ(receiver.typ.idx())
|
||||||
|
mut sb := strings.new_builder(256)
|
||||||
|
name := '_V_closure_${expr_styp}_${m.name}_$node.pos.pos'
|
||||||
|
sb.write_string('${g.typ(m.return_type)} ${name}(')
|
||||||
|
for i in 1 .. m.params.len {
|
||||||
|
param := m.params[i]
|
||||||
|
if i != 1 {
|
||||||
|
sb.write_string(', ')
|
||||||
|
}
|
||||||
|
sb.write_string('${g.typ(param.typ)} a$i')
|
||||||
|
}
|
||||||
|
sb.writeln(') {')
|
||||||
|
sb.writeln('\t$data_styp* a0 = *($data_styp**)(__RETURN_ADDRESS() - __CLOSURE_DATA_OFFSET);')
|
||||||
|
if m.return_type != ast.void_type {
|
||||||
|
sb.write_string('\treturn ')
|
||||||
|
} else {
|
||||||
|
sb.write_string('\t')
|
||||||
|
}
|
||||||
|
sb.write_string('${expr_styp}_${m.name}(')
|
||||||
|
if !receiver.typ.is_ptr() {
|
||||||
|
sb.write_string('*')
|
||||||
|
}
|
||||||
|
for i in 0 .. m.params.len {
|
||||||
|
if i != 0 {
|
||||||
|
sb.write_string(', ')
|
||||||
|
}
|
||||||
|
sb.write_string('a$i')
|
||||||
|
}
|
||||||
|
sb.writeln(');')
|
||||||
|
sb.writeln('}')
|
||||||
|
|
||||||
|
g.anon_fn_definitions << sb.str()
|
||||||
|
g.nr_closures++
|
||||||
|
|
||||||
|
g.write('__closure_create($name, ')
|
||||||
|
if !receiver.typ.is_ptr() {
|
||||||
|
g.write('memdup(')
|
||||||
|
}
|
||||||
|
if !node.expr_type.is_ptr() {
|
||||||
|
g.write('&')
|
||||||
|
}
|
||||||
|
g.expr(node.expr)
|
||||||
|
if !receiver.typ.is_ptr() {
|
||||||
|
g.write(', sizeof($expr_styp))')
|
||||||
|
}
|
||||||
|
g.write(')')
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
n_ptr := node.expr_type.nr_muls() - 1
|
n_ptr := node.expr_type.nr_muls() - 1
|
||||||
if n_ptr > 0 {
|
if n_ptr > 0 {
|
||||||
|
|
|
@ -378,6 +378,11 @@ fn (mut w Walker) expr(node_ ast.Expr) {
|
||||||
}
|
}
|
||||||
ast.SelectorExpr {
|
ast.SelectorExpr {
|
||||||
w.expr(node.expr)
|
w.expr(node.expr)
|
||||||
|
if node.expr_type != 0 {
|
||||||
|
if method := w.table.find_method(w.table.sym(node.expr_type), node.field_name) {
|
||||||
|
w.fn_by_name(method.fkey())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ast.SqlExpr {
|
ast.SqlExpr {
|
||||||
w.expr(node.db_expr)
|
w.expr(node.db_expr)
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
struct Foo {
|
||||||
|
s string
|
||||||
|
mut:
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f Foo) get_s() string {
|
||||||
|
return f.s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f &Foo) get_s_ref() string {
|
||||||
|
return f.s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f Foo) add(a int) int {
|
||||||
|
return a + f.i
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f &Foo) add_ref(a int) int {
|
||||||
|
return a + f.i
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut f Foo) set(a int) {
|
||||||
|
f.i = a
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f_ Foo) set_val(a int) int {
|
||||||
|
mut f := unsafe { &f_ }
|
||||||
|
old := f.i
|
||||||
|
f.i = a
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_methods_as_fields() {
|
||||||
|
mut f := Foo{
|
||||||
|
s: 'hello'
|
||||||
|
i: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get_s := f.get_s
|
||||||
|
get_s_ref := unsafe { f.get_s_ref }
|
||||||
|
add := f.add
|
||||||
|
add_ref := unsafe { f.add_ref }
|
||||||
|
set := unsafe { f.set }
|
||||||
|
set_val := f.set_val
|
||||||
|
|
||||||
|
assert typeof(get_s).str() == 'fn () string'
|
||||||
|
assert typeof(get_s_ref).str() == 'fn () string'
|
||||||
|
assert typeof(add).str() == 'fn (int) int'
|
||||||
|
assert typeof(add_ref).str() == 'fn (int) int'
|
||||||
|
|
||||||
|
assert get_s() == 'hello'
|
||||||
|
assert get_s_ref() == 'hello'
|
||||||
|
assert add(2) == 3
|
||||||
|
assert add_ref(2) == 3
|
||||||
|
|
||||||
|
assert f.i == 1
|
||||||
|
set(2)
|
||||||
|
assert f.i == 2
|
||||||
|
old := set_val(3)
|
||||||
|
assert f.i == 2
|
||||||
|
new := set_val(5)
|
||||||
|
assert old == new && old == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// the difference between these two tests is that here `f` is &Foo
|
||||||
|
fn test_methods_as_fields_ref() {
|
||||||
|
mut f := &Foo{
|
||||||
|
s: 'hello'
|
||||||
|
i: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get_s := f.get_s
|
||||||
|
get_s_ref := unsafe { f.get_s_ref }
|
||||||
|
add := f.add
|
||||||
|
add_ref := unsafe { f.add_ref }
|
||||||
|
set := unsafe { f.set }
|
||||||
|
set_val := f.set_val
|
||||||
|
|
||||||
|
assert typeof(get_s).str() == 'fn () string'
|
||||||
|
assert typeof(get_s_ref).str() == 'fn () string'
|
||||||
|
assert typeof(add).str() == 'fn (int) int'
|
||||||
|
assert typeof(add_ref).str() == 'fn (int) int'
|
||||||
|
|
||||||
|
assert get_s() == 'hello'
|
||||||
|
assert get_s_ref() == 'hello'
|
||||||
|
assert add(2) == 3
|
||||||
|
assert add_ref(2) == 3
|
||||||
|
|
||||||
|
assert f.i == 1
|
||||||
|
set(2)
|
||||||
|
assert f.i == 2
|
||||||
|
old := set_val(3)
|
||||||
|
assert f.i == 2
|
||||||
|
new := set_val(5)
|
||||||
|
assert old == new && old == 1
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
struct Foo {
|
||||||
|
x int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f Foo) hi() int {
|
||||||
|
return f.x
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
f := Foo{123}
|
||||||
|
_ = f.hi
|
||||||
|
}
|
Loading…
Reference in New Issue