mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
all: initial support for closures (x64 / linux-only) (#11114)
This commit is contained in:
parent
2cfb8fd697
commit
da53f818df
@ -97,6 +97,7 @@ const (
|
|||||||
skip_on_windows = [
|
skip_on_windows = [
|
||||||
'vlib/orm/orm_test.v',
|
'vlib/orm/orm_test.v',
|
||||||
'vlib/v/tests/orm_sub_struct_test.v',
|
'vlib/v/tests/orm_sub_struct_test.v',
|
||||||
|
'vlib/v/tests/closure_test.v',
|
||||||
'vlib/net/websocket/ws_test.v',
|
'vlib/net/websocket/ws_test.v',
|
||||||
'vlib/net/unix/unix_test.v',
|
'vlib/net/unix/unix_test.v',
|
||||||
'vlib/net/websocket/websocket_test.v',
|
'vlib/net/websocket/websocket_test.v',
|
||||||
|
@ -22,8 +22,6 @@ pub type Stmt = AsmStmt | AssertStmt | AssignStmt | Block | BranchStmt | CompFor
|
|||||||
GlobalDecl | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | NodeError |
|
GlobalDecl | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | NodeError |
|
||||||
Return | SqlStmt | StructDecl | TypeDecl
|
Return | SqlStmt | StructDecl | TypeDecl
|
||||||
|
|
||||||
// NB: when you add a new Expr or Stmt type with a .pos field, remember to update
|
|
||||||
// the .position() token.Position methods too.
|
|
||||||
pub type ScopeObject = AsmRegister | ConstField | GlobalField | Var
|
pub type ScopeObject = AsmRegister | ConstField | GlobalField | Var
|
||||||
|
|
||||||
// TODO: replace Param
|
// TODO: replace Param
|
||||||
@ -354,6 +352,7 @@ pub:
|
|||||||
pub struct AnonFn {
|
pub struct AnonFn {
|
||||||
pub mut:
|
pub mut:
|
||||||
decl FnDecl
|
decl FnDecl
|
||||||
|
inherited_vars []Param
|
||||||
typ Type // the type of anonymous fn. Both .typ and .decl.name are auto generated
|
typ Type // the type of anonymous fn. Both .typ and .decl.name are auto generated
|
||||||
has_gen bool // has been generated
|
has_gen bool // has been generated
|
||||||
}
|
}
|
||||||
@ -498,6 +497,7 @@ pub:
|
|||||||
is_autofree_tmp bool
|
is_autofree_tmp bool
|
||||||
is_arg bool // fn args should not be autofreed
|
is_arg bool // fn args should not be autofreed
|
||||||
is_auto_deref bool
|
is_auto_deref bool
|
||||||
|
is_inherited bool
|
||||||
pub mut:
|
pub mut:
|
||||||
typ Type
|
typ Type
|
||||||
orig_type Type // original sumtype type; 0 if it's not a sumtype
|
orig_type Type // original sumtype type; 0 if it's not a sumtype
|
||||||
@ -1539,7 +1539,7 @@ pub fn (expr Expr) position() token.Position {
|
|||||||
AnonFn {
|
AnonFn {
|
||||||
return expr.decl.pos
|
return expr.decl.pos
|
||||||
}
|
}
|
||||||
EmptyExpr {
|
CTempVar, EmptyExpr {
|
||||||
// println('compiler bug, unhandled EmptyExpr position()')
|
// println('compiler bug, unhandled EmptyExpr position()')
|
||||||
return token.Position{}
|
return token.Position{}
|
||||||
}
|
}
|
||||||
@ -1571,9 +1571,6 @@ pub fn (expr Expr) position() token.Position {
|
|||||||
last_line: right_pos.last_line
|
last_line: right_pos.last_line
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CTempVar {
|
|
||||||
return token.Position{}
|
|
||||||
}
|
|
||||||
// Please, do NOT use else{} here.
|
// Please, do NOT use else{} here.
|
||||||
// This match is exhaustive *on purpose*, to help force
|
// This match is exhaustive *on purpose*, to help force
|
||||||
// maintaining/implementing proper .pos fields.
|
// maintaining/implementing proper .pos fields.
|
||||||
|
@ -41,20 +41,6 @@ fn (s &Scope) dont_lookup_parent() bool {
|
|||||||
return isnil(s.parent) || s.detached_from_parent
|
return isnil(s.parent) || s.detached_from_parent
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (s &Scope) find_with_scope(name string) ?(ScopeObject, &Scope) {
|
|
||||||
mut sc := s
|
|
||||||
for {
|
|
||||||
if name in sc.objects {
|
|
||||||
return sc.objects[name], sc
|
|
||||||
}
|
|
||||||
if sc.dont_lookup_parent() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
sc = sc.parent
|
|
||||||
}
|
|
||||||
return none
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (s &Scope) find(name string) ?ScopeObject {
|
pub fn (s &Scope) find(name string) ?ScopeObject {
|
||||||
for sc := s; true; sc = sc.parent {
|
for sc := s; true; sc = sc.parent {
|
||||||
if name in sc.objects {
|
if name in sc.objects {
|
||||||
@ -82,14 +68,6 @@ pub fn (s &Scope) find_struct_field(name string, struct_type Type, field_name st
|
|||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (s &Scope) is_known(name string) bool {
|
|
||||||
if _ := s.find(name) {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (s &Scope) find_var(name string) ?&Var {
|
pub fn (s &Scope) find_var(name string) ?&Var {
|
||||||
if obj := s.find(name) {
|
if obj := s.find(name) {
|
||||||
match obj {
|
match obj {
|
||||||
@ -121,23 +99,16 @@ pub fn (s &Scope) find_const(name string) ?&ConstField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn (s &Scope) known_var(name string) bool {
|
pub fn (s &Scope) known_var(name string) bool {
|
||||||
if _ := s.find_var(name) {
|
s.find_var(name) or { return false }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut s Scope) update_var_type(name string, typ Type) {
|
pub fn (mut s Scope) update_var_type(name string, typ Type) {
|
||||||
s.end_pos = s.end_pos // TODO mut bug
|
|
||||||
mut obj := s.objects[name]
|
mut obj := s.objects[name]
|
||||||
match mut obj {
|
if mut obj is Var {
|
||||||
Var {
|
if obj.typ != typ {
|
||||||
if obj.typ == typ {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
obj.typ = typ
|
obj.typ = typ
|
||||||
}
|
}
|
||||||
else {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,29 +123,10 @@ pub fn (mut s Scope) register_struct_field(name string, field ScopeStructField)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut s Scope) register(obj ScopeObject) {
|
pub fn (mut s Scope) register(obj ScopeObject) {
|
||||||
name := if obj is ConstField {
|
if obj.name == '_' || obj.name in s.objects {
|
||||||
obj.name
|
|
||||||
} else if obj is GlobalField {
|
|
||||||
obj.name
|
|
||||||
} else {
|
|
||||||
(obj as Var).name
|
|
||||||
}
|
|
||||||
if name == '_' {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if name in s.objects {
|
s.objects[obj.name] = obj
|
||||||
// println('existing obect: $name')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.objects[name] = obj
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (s &Scope) outermost() &Scope {
|
|
||||||
mut sc := s
|
|
||||||
for !sc.dont_lookup_parent() {
|
|
||||||
sc = sc.parent
|
|
||||||
}
|
|
||||||
return sc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the innermost scope containing pos
|
// returns the innermost scope containing pos
|
||||||
@ -211,6 +163,17 @@ pub fn (s &Scope) contains(pos int) bool {
|
|||||||
return pos >= s.start_pos && pos <= s.end_pos
|
return pos >= s.start_pos && pos <= s.end_pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (s &Scope) has_inherited_vars() bool {
|
||||||
|
for _, obj in s.objects {
|
||||||
|
if obj is Var {
|
||||||
|
if obj.is_inherited {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (sc Scope) show(depth int, max_depth int) string {
|
pub fn (sc Scope) show(depth int, max_depth int) string {
|
||||||
mut out := ''
|
mut out := ''
|
||||||
mut indent := ''
|
mut indent := ''
|
||||||
|
@ -34,50 +34,61 @@ pub fn (node &CallExpr) fkey() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// These methods are used only by vfmt, vdoc, and for debugging.
|
// These methods are used only by vfmt, vdoc, and for debugging.
|
||||||
|
pub fn (node &AnonFn) stringify(t &Table, cur_mod string, m2a map[string]string) string {
|
||||||
|
mut f := strings.new_builder(30)
|
||||||
|
f.write_string('fn ')
|
||||||
|
if node.inherited_vars.len > 0 {
|
||||||
|
f.write_string('[')
|
||||||
|
for i, var in node.inherited_vars {
|
||||||
|
if i > 0 {
|
||||||
|
f.write_string(', ')
|
||||||
|
}
|
||||||
|
if var.is_mut {
|
||||||
|
f.write_string('mut ')
|
||||||
|
}
|
||||||
|
f.write_string(var.name)
|
||||||
|
}
|
||||||
|
f.write_string('] ')
|
||||||
|
}
|
||||||
|
stringify_fn_after_name(node.decl, mut f, t, cur_mod, m2a)
|
||||||
|
return f.str()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (node &FnDecl) stringify(t &Table, cur_mod string, m2a map[string]string) string {
|
pub fn (node &FnDecl) stringify(t &Table, cur_mod string, m2a map[string]string) string {
|
||||||
mut f := strings.new_builder(30)
|
mut f := strings.new_builder(30)
|
||||||
if node.is_pub {
|
if node.is_pub {
|
||||||
f.write_string('pub ')
|
f.write_string('pub ')
|
||||||
}
|
}
|
||||||
mut receiver := ''
|
f.write_string('fn ')
|
||||||
if node.is_method {
|
if node.is_method {
|
||||||
|
f.write_string('(')
|
||||||
mut styp := util.no_cur_mod(t.type_to_code(node.receiver.typ.clear_flag(.shared_f)),
|
mut styp := util.no_cur_mod(t.type_to_code(node.receiver.typ.clear_flag(.shared_f)),
|
||||||
cur_mod)
|
cur_mod)
|
||||||
m := if node.rec_mut { node.receiver.typ.share().str() + ' ' } else { '' }
|
|
||||||
if node.rec_mut {
|
if node.rec_mut {
|
||||||
|
f.write_string(node.receiver.typ.share().str() + ' ')
|
||||||
styp = styp[1..] // remove &
|
styp = styp[1..] // remove &
|
||||||
}
|
}
|
||||||
|
f.write_string(node.receiver.name + ' ')
|
||||||
styp = util.no_cur_mod(styp, cur_mod)
|
styp = util.no_cur_mod(styp, cur_mod)
|
||||||
if node.params[0].is_auto_rec {
|
if node.params[0].is_auto_rec {
|
||||||
styp = styp.trim('&')
|
styp = styp.trim('&')
|
||||||
}
|
}
|
||||||
receiver = '($m$node.receiver.name $styp) '
|
f.write_string(styp + ') ')
|
||||||
/*
|
|
||||||
sym := t.get_type_symbol(node.receiver.typ)
|
|
||||||
name := sym.name.after('.')
|
|
||||||
mut m := if node.rec_mut { 'mut ' } else { '' }
|
|
||||||
if !node.rec_mut && node.receiver.typ.is_ptr() {
|
|
||||||
m = '&'
|
|
||||||
}
|
}
|
||||||
receiver = '($node.receiver.name $m$name) '
|
name := if !node.is_method && node.language == .v {
|
||||||
*/
|
node.name.all_after_last('.')
|
||||||
|
} else {
|
||||||
|
node.name
|
||||||
}
|
}
|
||||||
mut name := if node.is_anon { '' } else { node.name }
|
f.write_string(name)
|
||||||
if !node.is_anon && !node.is_method && node.language == .v {
|
|
||||||
name = node.name.all_after_last('.')
|
|
||||||
}
|
|
||||||
// mut name := if node.is_anon { '' } else { node.name.after_char(`.`) }
|
|
||||||
// if !node.is_method {
|
|
||||||
// if node.language == .c {
|
|
||||||
// name = 'C.$name'
|
|
||||||
// } else if node.language == .js {
|
|
||||||
// name = 'JS.$name'
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
f.write_string('fn $receiver$name')
|
|
||||||
if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!=', '>=', '<='] {
|
if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!=', '>=', '<='] {
|
||||||
f.write_string(' ')
|
f.write_string(' ')
|
||||||
}
|
}
|
||||||
|
stringify_fn_after_name(node, mut f, t, cur_mod, m2a)
|
||||||
|
return f.str()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stringify_fn_after_name(node &FnDecl, mut f strings.Builder, t &Table, cur_mod string, m2a map[string]string) {
|
||||||
mut add_para_types := true
|
mut add_para_types := true
|
||||||
if node.generic_names.len > 0 {
|
if node.generic_names.len > 0 {
|
||||||
if node.is_method {
|
if node.is_method {
|
||||||
@ -151,7 +162,6 @@ pub fn (node &FnDecl) stringify(t &Table, cur_mod string, m2a map[string]string)
|
|||||||
}
|
}
|
||||||
f.write_string(' ' + rs)
|
f.write_string(' ' + rs)
|
||||||
}
|
}
|
||||||
return f.str()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expressions in string interpolations may have to be put in braces if they
|
// Expressions in string interpolations may have to be put in braces if they
|
||||||
|
@ -2737,7 +2737,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
|
|||||||
}
|
}
|
||||||
call_expr.is_noreturn = func.is_noreturn
|
call_expr.is_noreturn = func.is_noreturn
|
||||||
if !found_in_args {
|
if !found_in_args {
|
||||||
if _ := call_expr.scope.find_var(fn_name) {
|
if call_expr.scope.known_var(fn_name) {
|
||||||
c.error('ambiguous call to: `$fn_name`, may refer to fn `$fn_name` or variable `$fn_name`',
|
c.error('ambiguous call to: `$fn_name`, may refer to fn `$fn_name` or variable `$fn_name`',
|
||||||
call_expr.pos)
|
call_expr.pos)
|
||||||
}
|
}
|
||||||
@ -5210,14 +5210,7 @@ pub fn (mut c Checker) expr(node ast.Expr) ast.Type {
|
|||||||
return node.typ
|
return node.typ
|
||||||
}
|
}
|
||||||
ast.AnonFn {
|
ast.AnonFn {
|
||||||
c.inside_anon_fn = true
|
return c.anon_fn(mut node)
|
||||||
keep_fn := c.table.cur_fn
|
|
||||||
c.table.cur_fn = unsafe { &node.decl }
|
|
||||||
c.stmts(node.decl.stmts)
|
|
||||||
c.fn_decl(mut node.decl)
|
|
||||||
c.table.cur_fn = keep_fn
|
|
||||||
c.inside_anon_fn = false
|
|
||||||
return node.typ
|
|
||||||
}
|
}
|
||||||
ast.ArrayDecompose {
|
ast.ArrayDecompose {
|
||||||
typ := c.expr(node.expr)
|
typ := c.expr(node.expr)
|
||||||
@ -8167,6 +8160,30 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
|||||||
node.source_file = c.file
|
node.source_file = c.file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut c Checker) anon_fn(mut node ast.AnonFn) ast.Type {
|
||||||
|
keep_fn := c.table.cur_fn
|
||||||
|
keep_inside_anon := c.inside_anon_fn
|
||||||
|
defer {
|
||||||
|
c.table.cur_fn = keep_fn
|
||||||
|
c.inside_anon_fn = keep_inside_anon
|
||||||
|
}
|
||||||
|
c.table.cur_fn = unsafe { &node.decl }
|
||||||
|
c.inside_anon_fn = true
|
||||||
|
for mut var in node.inherited_vars {
|
||||||
|
parent_var := node.decl.scope.parent.find_var(var.name) or {
|
||||||
|
panic('unexpected checker error: cannot find parent of inherited variable `$var.name`')
|
||||||
|
}
|
||||||
|
if var.is_mut && !parent_var.is_mut {
|
||||||
|
c.error('original `$parent_var.name` is immutable, declare it with `mut` to make it mutable',
|
||||||
|
var.pos)
|
||||||
|
}
|
||||||
|
var.typ = parent_var.typ
|
||||||
|
}
|
||||||
|
c.stmts(node.decl.stmts)
|
||||||
|
c.fn_decl(mut node.decl)
|
||||||
|
return node.typ
|
||||||
|
}
|
||||||
|
|
||||||
// NB: has_top_return/1 should be called on *already checked* stmts,
|
// NB: has_top_return/1 should be called on *already checked* stmts,
|
||||||
// which do have their stmt.expr.is_noreturn set properly:
|
// which do have their stmt.expr.is_noreturn set properly:
|
||||||
fn has_top_return(stmts []ast.Stmt) bool {
|
fn has_top_return(stmts []ast.Stmt) bool {
|
||||||
|
21
vlib/v/checker/tests/closure_immutable.out
Normal file
21
vlib/v/checker/tests/closure_immutable.out
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
vlib/v/checker/tests/closure_immutable.vv:4:3: error: `a` is immutable, declare it with `mut` to make it mutable
|
||||||
|
2 | a := 1
|
||||||
|
3 | f1 := fn [a] () {
|
||||||
|
4 | a++
|
||||||
|
| ^
|
||||||
|
5 | println(a)
|
||||||
|
6 | }
|
||||||
|
vlib/v/checker/tests/closure_immutable.vv:7:16: error: original `a` is immutable, declare it with `mut` to make it mutable
|
||||||
|
5 | println(a)
|
||||||
|
6 | }
|
||||||
|
7 | f2 := fn [mut a] () {
|
||||||
|
| ^
|
||||||
|
8 | a++
|
||||||
|
9 | println(a)
|
||||||
|
vlib/v/checker/tests/closure_immutable.vv:13:3: error: `b` is immutable, declare it with `mut` to make it mutable
|
||||||
|
11 | mut b := 2
|
||||||
|
12 | f3 := fn [b] () {
|
||||||
|
13 | b++
|
||||||
|
| ^
|
||||||
|
14 | println(b)
|
||||||
|
15 | }
|
19
vlib/v/checker/tests/closure_immutable.vv
Normal file
19
vlib/v/checker/tests/closure_immutable.vv
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
fn my_fn() {
|
||||||
|
a := 1
|
||||||
|
f1 := fn [a] () {
|
||||||
|
a++
|
||||||
|
println(a)
|
||||||
|
}
|
||||||
|
f2 := fn [mut a] () {
|
||||||
|
a++
|
||||||
|
println(a)
|
||||||
|
}
|
||||||
|
mut b := 2
|
||||||
|
f3 := fn [b] () {
|
||||||
|
b++
|
||||||
|
println(b)
|
||||||
|
}
|
||||||
|
f1()
|
||||||
|
f2()
|
||||||
|
f3()
|
||||||
|
}
|
@ -391,8 +391,7 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
|
|||||||
eprintln('stmt: ${node.pos:-42} | node: ${node.type_name():-20}')
|
eprintln('stmt: ${node.pos:-42} | node: ${node.type_name():-20}')
|
||||||
}
|
}
|
||||||
match node {
|
match node {
|
||||||
ast.NodeError {}
|
ast.EmptyStmt, ast.NodeError {}
|
||||||
ast.EmptyStmt {}
|
|
||||||
ast.AsmStmt {
|
ast.AsmStmt {
|
||||||
f.asm_stmt(node)
|
f.asm_stmt(node)
|
||||||
}
|
}
|
||||||
@ -491,7 +490,7 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
|||||||
ast.NodeError {}
|
ast.NodeError {}
|
||||||
ast.EmptyExpr {}
|
ast.EmptyExpr {}
|
||||||
ast.AnonFn {
|
ast.AnonFn {
|
||||||
f.fn_decl(node.decl)
|
f.anon_fn(node)
|
||||||
}
|
}
|
||||||
ast.ArrayDecompose {
|
ast.ArrayDecompose {
|
||||||
f.array_decompose(node)
|
f.array_decompose(node)
|
||||||
@ -868,6 +867,15 @@ pub fn (mut f Fmt) enum_decl(node ast.EnumDecl) {
|
|||||||
pub fn (mut f Fmt) fn_decl(node ast.FnDecl) {
|
pub fn (mut f Fmt) fn_decl(node ast.FnDecl) {
|
||||||
f.attrs(node.attrs)
|
f.attrs(node.attrs)
|
||||||
f.write(node.stringify(f.table, f.cur_mod, f.mod2alias)) // `Expr` instead of `ast.Expr` in mod ast
|
f.write(node.stringify(f.table, f.cur_mod, f.mod2alias)) // `Expr` instead of `ast.Expr` in mod ast
|
||||||
|
f.fn_body(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut f Fmt) anon_fn(node ast.AnonFn) {
|
||||||
|
f.write(node.stringify(f.table, f.cur_mod, f.mod2alias)) // `Expr` instead of `ast.Expr` in mod ast
|
||||||
|
f.fn_body(node.decl)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut f Fmt) fn_body(node ast.FnDecl) {
|
||||||
if node.language == .v {
|
if node.language == .v {
|
||||||
if !node.no_body {
|
if !node.no_body {
|
||||||
f.write(' {')
|
f.write(' {')
|
||||||
@ -1007,7 +1015,7 @@ pub fn (mut f Fmt) global_decl(node ast.GlobalDecl) {
|
|||||||
|
|
||||||
pub fn (mut f Fmt) go_expr(node ast.GoExpr) {
|
pub fn (mut f Fmt) go_expr(node ast.GoExpr) {
|
||||||
f.write('go ')
|
f.write('go ')
|
||||||
f.expr(node.call_expr)
|
f.call_expr(node.call_expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut f Fmt) goto_label(node ast.GotoLabel) {
|
pub fn (mut f Fmt) goto_label(node ast.GotoLabel) {
|
||||||
@ -1500,7 +1508,7 @@ pub fn (mut f Fmt) call_expr(node ast.CallExpr) {
|
|||||||
} else {
|
} else {
|
||||||
f.write_language_prefix(node.language)
|
f.write_language_prefix(node.language)
|
||||||
if node.left is ast.AnonFn {
|
if node.left is ast.AnonFn {
|
||||||
f.fn_decl(node.left.decl)
|
f.anon_fn(node.left)
|
||||||
} else if node.language != .v {
|
} else if node.language != .v {
|
||||||
f.write('${node.name.after_char(`.`)}')
|
f.write('${node.name.after_char(`.`)}')
|
||||||
} else {
|
} else {
|
||||||
|
10
vlib/v/fmt/tests/closure_keep.vv
Normal file
10
vlib/v/fmt/tests/closure_keep.vv
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
my_var := 1
|
||||||
|
my_simple_closure := fn [my_var] () {
|
||||||
|
println(my_var)
|
||||||
|
}
|
||||||
|
my_closure_returns := fn [my_var] () int {
|
||||||
|
return my_var
|
||||||
|
}
|
||||||
|
my_closure_has_params := fn [my_var, my_closure_returns] (add int) {
|
||||||
|
println(my_var + my_closure_returns() + add)
|
||||||
|
}
|
@ -180,6 +180,7 @@ mut:
|
|||||||
defer_vars []string
|
defer_vars []string
|
||||||
anon_fn bool
|
anon_fn bool
|
||||||
array_sort_fn map[string]bool
|
array_sort_fn map[string]bool
|
||||||
|
nr_closures int
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
|
pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
|
||||||
@ -381,6 +382,10 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if g.anon_fn_definitions.len > 0 {
|
if g.anon_fn_definitions.len > 0 {
|
||||||
|
if g.nr_closures > 0 {
|
||||||
|
b.writeln('\n// V closure helpers')
|
||||||
|
b.writeln(c_closure_helpers())
|
||||||
|
}
|
||||||
for fn_def in g.anon_fn_definitions {
|
for fn_def in g.anon_fn_definitions {
|
||||||
b.writeln(fn_def)
|
b.writeln(fn_def)
|
||||||
}
|
}
|
||||||
@ -613,9 +618,7 @@ fn (mut g Gen) generic_fn_name(types []ast.Type, before string, is_decl bool) st
|
|||||||
fn (mut g Gen) expr_string(expr ast.Expr) string {
|
fn (mut g Gen) expr_string(expr ast.Expr) string {
|
||||||
pos := g.out.len
|
pos := g.out.len
|
||||||
g.expr(expr)
|
g.expr(expr)
|
||||||
expr_str := g.out.after(pos)
|
return g.out.cut_to(pos).trim_space()
|
||||||
g.out.go_back(expr_str.len)
|
|
||||||
return expr_str.trim_space()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Surround a potentially multi-statement expression safely with `prepend` and `append`.
|
// Surround a potentially multi-statement expression safely with `prepend` and `append`.
|
||||||
@ -623,13 +626,13 @@ fn (mut g Gen) expr_string(expr ast.Expr) string {
|
|||||||
fn (mut g Gen) expr_string_surround(prepend string, expr ast.Expr, append string) string {
|
fn (mut g Gen) expr_string_surround(prepend string, expr ast.Expr, append string) string {
|
||||||
pos := g.out.len
|
pos := g.out.len
|
||||||
g.stmt_path_pos << pos
|
g.stmt_path_pos << pos
|
||||||
|
defer {
|
||||||
|
g.stmt_path_pos.delete_last()
|
||||||
|
}
|
||||||
g.write(prepend)
|
g.write(prepend)
|
||||||
g.expr(expr)
|
g.expr(expr)
|
||||||
g.write(append)
|
g.write(append)
|
||||||
expr_str := g.out.after(pos)
|
return g.out.cut_to(pos)
|
||||||
g.out.go_back(expr_str.len)
|
|
||||||
g.stmt_path_pos.delete_last()
|
|
||||||
return expr_str
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO this really shouldnt be seperate from typ
|
// TODO this really shouldnt be seperate from typ
|
||||||
@ -823,8 +826,7 @@ pub fn (mut g Gen) write_typedef_types() {
|
|||||||
if elem_sym.info is ast.FnType {
|
if elem_sym.info is ast.FnType {
|
||||||
pos := g.out.len
|
pos := g.out.len
|
||||||
g.write_fn_ptr_decl(&elem_sym.info, '')
|
g.write_fn_ptr_decl(&elem_sym.info, '')
|
||||||
fixed = g.out.after(pos)
|
fixed = g.out.cut_to(pos)
|
||||||
g.out.go_back(fixed.len)
|
|
||||||
mut def_str := 'typedef $fixed;'
|
mut def_str := 'typedef $fixed;'
|
||||||
def_str = def_str.replace_once('(*)', '(*$styp[$len])')
|
def_str = def_str.replace_once('(*)', '(*$styp[$len])')
|
||||||
g.type_definitions.writeln(def_str)
|
g.type_definitions.writeln(def_str)
|
||||||
@ -2623,7 +2625,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, voidptr(0))
|
g.fn_args(val.decl.params, 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 {
|
||||||
@ -2764,7 +2766,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, voidptr(0))
|
g.fn_args(func.func.params, 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 {
|
||||||
@ -3084,6 +3086,10 @@ fn (mut g Gen) autofree_scope_vars2(scope &ast.Scope, start_pos int, end_pos int
|
|||||||
g.trace_autofree('// skipping tmp var "$obj.name"')
|
g.trace_autofree('// skipping tmp var "$obj.name"')
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if obj.is_inherited {
|
||||||
|
g.trace_autofree('// skipping inherited var "$obj.name"')
|
||||||
|
continue
|
||||||
|
}
|
||||||
// if var.typ == 0 {
|
// if var.typ == 0 {
|
||||||
// // TODO why 0?
|
// // TODO why 0?
|
||||||
// continue
|
// continue
|
||||||
@ -3194,19 +3200,6 @@ fn (mut g Gen) autofree_var_call(free_fn_name string, v ast.Var) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) gen_anon_fn_decl(mut node ast.AnonFn) {
|
|
||||||
if !node.has_gen {
|
|
||||||
pos := g.out.len
|
|
||||||
g.anon_fn = true
|
|
||||||
g.stmt(node.decl)
|
|
||||||
g.anon_fn = false
|
|
||||||
fn_body := g.out.after(pos)
|
|
||||||
g.out.go_back(fn_body.len)
|
|
||||||
g.anon_fn_definitions << fn_body
|
|
||||||
node.has_gen = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) map_fn_ptrs(key_typ ast.TypeSymbol) (string, string, string, string) {
|
fn (mut g Gen) map_fn_ptrs(key_typ ast.TypeSymbol) (string, string, string, string) {
|
||||||
mut hash_fn := ''
|
mut hash_fn := ''
|
||||||
mut key_eq_fn := ''
|
mut key_eq_fn := ''
|
||||||
@ -3270,10 +3263,7 @@ fn (mut g Gen) expr(node ast.Expr) {
|
|||||||
g.error('g.expr(): unhandled EmptyExpr', token.Position{})
|
g.error('g.expr(): unhandled EmptyExpr', token.Position{})
|
||||||
}
|
}
|
||||||
ast.AnonFn {
|
ast.AnonFn {
|
||||||
// TODO: dont fiddle with buffers
|
g.gen_anon_fn(mut node)
|
||||||
g.gen_anon_fn_decl(mut node)
|
|
||||||
fsym := g.table.get_type_symbol(node.typ)
|
|
||||||
g.write(fsym.name)
|
|
||||||
}
|
}
|
||||||
ast.ArrayDecompose {
|
ast.ArrayDecompose {
|
||||||
g.expr(node.expr)
|
g.expr(node.expr)
|
||||||
@ -4439,6 +4429,9 @@ fn (mut g Gen) ident(node ast.Ident) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if v.is_inherited {
|
||||||
|
g.write(closure_ctx + '->')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if node_info is ast.IdentFn {
|
} else if node_info is ast.IdentFn {
|
||||||
if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') {
|
if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') {
|
||||||
@ -5703,8 +5696,7 @@ fn (mut g Gen) write_types(types []ast.TypeSymbol) {
|
|||||||
// optional and we dont want that
|
// optional and we dont want that
|
||||||
styp, base := g.optional_type_name(field.typ)
|
styp, base := g.optional_type_name(field.typ)
|
||||||
if styp !in g.optionals {
|
if styp !in g.optionals {
|
||||||
last_text := g.type_definitions.after(start_pos).clone()
|
last_text := g.type_definitions.cut_to(start_pos).clone()
|
||||||
g.type_definitions.go_back_to(start_pos)
|
|
||||||
g.optionals << styp
|
g.optionals << styp
|
||||||
g.typedefs2.writeln('typedef struct $styp $styp;')
|
g.typedefs2.writeln('typedef struct $styp $styp;')
|
||||||
g.type_definitions.writeln('${g.optional_type_text(styp,
|
g.type_definitions.writeln('${g.optional_type_text(styp,
|
||||||
@ -5796,8 +5788,7 @@ fn (mut g Gen) write_types(types []ast.TypeSymbol) {
|
|||||||
if elem_sym.info is ast.FnType {
|
if elem_sym.info is ast.FnType {
|
||||||
pos := g.out.len
|
pos := g.out.len
|
||||||
g.write_fn_ptr_decl(&elem_sym.info, '')
|
g.write_fn_ptr_decl(&elem_sym.info, '')
|
||||||
fixed_elem_name = g.out.after(pos)
|
fixed_elem_name = g.out.cut_to(pos)
|
||||||
g.out.go_back(fixed_elem_name.len)
|
|
||||||
mut def_str := 'typedef $fixed_elem_name;'
|
mut def_str := 'typedef $fixed_elem_name;'
|
||||||
def_str = def_str.replace_once('(*)', '(*$styp[$len])')
|
def_str = def_str.replace_once('(*)', '(*$styp[$len])')
|
||||||
g.type_definitions.writeln(def_str)
|
g.type_definitions.writeln(def_str)
|
||||||
@ -5883,9 +5874,7 @@ fn (g &Gen) nth_stmt_pos(n int) int {
|
|||||||
|
|
||||||
fn (mut g Gen) go_before_stmt(n int) string {
|
fn (mut g Gen) go_before_stmt(n int) string {
|
||||||
stmt_pos := g.nth_stmt_pos(n)
|
stmt_pos := g.nth_stmt_pos(n)
|
||||||
cur_line := g.out.after(stmt_pos)
|
return g.out.cut_to(stmt_pos)
|
||||||
g.out.go_back(cur_line.len)
|
|
||||||
return cur_line
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[inline]
|
[inline]
|
||||||
@ -6167,8 +6156,7 @@ fn (mut g Gen) go_expr(node ast.GoExpr) {
|
|||||||
name = receiver_sym.name + '_' + name
|
name = receiver_sym.name + '_' + name
|
||||||
} else if mut expr.left is ast.AnonFn {
|
} else if mut expr.left is ast.AnonFn {
|
||||||
g.gen_anon_fn_decl(mut expr.left)
|
g.gen_anon_fn_decl(mut expr.left)
|
||||||
fsym := g.table.get_type_symbol(expr.left.typ)
|
name = expr.left.decl.name
|
||||||
name = fsym.name
|
|
||||||
}
|
}
|
||||||
name = util.no_dots(name)
|
name = util.no_dots(name)
|
||||||
if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') {
|
if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') {
|
||||||
@ -6465,7 +6453,7 @@ fn (mut g Gen) interface_table() string {
|
|||||||
arg := method.params[i]
|
arg := method.params[i]
|
||||||
methods_struct_def.write_string(', ${g.typ(arg.typ)} $arg.name')
|
methods_struct_def.write_string(', ${g.typ(arg.typ)} $arg.name')
|
||||||
}
|
}
|
||||||
// TODO g.fn_args(method.args[1..], method.is_variadic)
|
// TODO g.fn_args(method.args[1..])
|
||||||
methods_struct_def.writeln(');')
|
methods_struct_def.writeln(');')
|
||||||
}
|
}
|
||||||
methods_struct_def.writeln('};')
|
methods_struct_def.writeln('};')
|
||||||
@ -6603,7 +6591,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, voidptr(0)) // second argument is ignored anyway
|
fargs, _, _ := g.fn_args(params, voidptr(0))
|
||||||
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')
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
module c
|
module c
|
||||||
|
|
||||||
|
import strings
|
||||||
|
|
||||||
// NB: @@@ here serve as placeholders.
|
// NB: @@@ here serve as placeholders.
|
||||||
// They will be replaced with correct strings
|
// They will be replaced with correct strings
|
||||||
// for each constant, during C code generation.
|
// for each constant, during C code generation.
|
||||||
@ -49,6 +51,82 @@ static inline void __sort_ptr(uintptr_t a[], bool b[], int l) {
|
|||||||
}
|
}
|
||||||
'
|
'
|
||||||
|
|
||||||
|
// Heavily based on Chris Wellons's work
|
||||||
|
// https://nullprogram.com/blog/2017/01/08/
|
||||||
|
|
||||||
|
fn c_closure_helpers() string {
|
||||||
|
$if windows {
|
||||||
|
verror('closures are not implemented on Windows yet')
|
||||||
|
}
|
||||||
|
$if !x64 {
|
||||||
|
verror('closures are not implemented on this architecture yet')
|
||||||
|
}
|
||||||
|
mut builder := strings.new_builder(1024)
|
||||||
|
$if !windows {
|
||||||
|
builder.writeln('#include <sys/mman.h>')
|
||||||
|
}
|
||||||
|
$if x64 {
|
||||||
|
builder.write_string('
|
||||||
|
static unsigned char __closure_thunk[6][13] = {
|
||||||
|
{
|
||||||
|
0x48, 0x8b, 0x3d, 0xe9, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0x25, 0xeb, 0xff, 0xff, 0xff
|
||||||
|
}, {
|
||||||
|
0x48, 0x8b, 0x35, 0xe9, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0x25, 0xeb, 0xff, 0xff, 0xff
|
||||||
|
}, {
|
||||||
|
0x48, 0x8b, 0x15, 0xe9, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0x25, 0xeb, 0xff, 0xff, 0xff
|
||||||
|
}, {
|
||||||
|
0x48, 0x8b, 0x0d, 0xe9, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0x25, 0xeb, 0xff, 0xff, 0xff
|
||||||
|
}, {
|
||||||
|
0x4C, 0x8b, 0x05, 0xe9, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0x25, 0xeb, 0xff, 0xff, 0xff
|
||||||
|
}, {
|
||||||
|
0x4C, 0x8b, 0x0d, 0xe9, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0x25, 0xeb, 0xff, 0xff, 0xff
|
||||||
|
},
|
||||||
|
};
|
||||||
|
')
|
||||||
|
}
|
||||||
|
builder.write_string('
|
||||||
|
static void __closure_set_data(void *closure, void *data) {
|
||||||
|
void **p = closure;
|
||||||
|
p[-2] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __closure_set_function(void *closure, void *f) {
|
||||||
|
void **p = closure;
|
||||||
|
p[-1] = f;
|
||||||
|
}
|
||||||
|
')
|
||||||
|
$if !windows {
|
||||||
|
builder.write_string('
|
||||||
|
static void * __closure_create(void *f, int nargs, void *userdata) {
|
||||||
|
long page_size = sysconf(_SC_PAGESIZE);
|
||||||
|
int prot = PROT_READ | PROT_WRITE;
|
||||||
|
int flags = MAP_ANONYMOUS | MAP_PRIVATE;
|
||||||
|
char *p = mmap(0, page_size * 2, prot, flags, -1, 0);
|
||||||
|
if (p == MAP_FAILED)
|
||||||
|
return 0;
|
||||||
|
void *closure = p + page_size;
|
||||||
|
memcpy(closure, __closure_thunk[nargs - 1], sizeof(__closure_thunk[0]));
|
||||||
|
mprotect(closure, page_size, PROT_READ | PROT_EXEC);
|
||||||
|
__closure_set_function(closure, f);
|
||||||
|
__closure_set_data(closure, userdata);
|
||||||
|
return closure;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __closure_destroy(void *closure) {
|
||||||
|
long page_size = sysconf(_SC_PAGESIZE);
|
||||||
|
munmap((char *)closure - page_size, page_size * 2);
|
||||||
|
}
|
||||||
|
')
|
||||||
|
}
|
||||||
|
return builder.str()
|
||||||
|
}
|
||||||
|
|
||||||
const c_common_macros = '
|
const c_common_macros = '
|
||||||
#define EMPTY_VARG_INITIALIZATION 0
|
#define EMPTY_VARG_INITIALIZATION 0
|
||||||
#define EMPTY_STRUCT_DECLARATION
|
#define EMPTY_STRUCT_DECLARATION
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
// that can be found in the LICENSE file.
|
// that can be found in the LICENSE file.
|
||||||
module c
|
module c
|
||||||
|
|
||||||
|
import strings
|
||||||
import v.ast
|
import v.ast
|
||||||
import v.util
|
import v.util
|
||||||
|
|
||||||
@ -39,7 +40,6 @@ fn (mut g Gen) process_fn_decl(node ast.FnDecl) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
g.gen_attrs(node.attrs)
|
g.gen_attrs(node.attrs)
|
||||||
// g.tmp_count = 0 TODO
|
|
||||||
mut skip := false
|
mut skip := false
|
||||||
pos := g.out.len
|
pos := g.out.len
|
||||||
should_bundle_module := util.should_bundle_module(node.mod)
|
should_bundle_module := util.should_bundle_module(node.mod)
|
||||||
@ -166,6 +166,15 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
|
|||||||
g.table.cur_fn = node
|
g.table.cur_fn = node
|
||||||
}
|
}
|
||||||
fn_start_pos := g.out.len
|
fn_start_pos := g.out.len
|
||||||
|
is_closure := node.scope.has_inherited_vars()
|
||||||
|
mut cur_closure_ctx := ''
|
||||||
|
if is_closure {
|
||||||
|
cur_closure_ctx = closure_ctx_struct(node)
|
||||||
|
// declare the struct before its implementation
|
||||||
|
g.definitions.write_string(cur_closure_ctx)
|
||||||
|
g.definitions.writeln(';')
|
||||||
|
}
|
||||||
|
|
||||||
g.write_v_source_line_info(node.pos)
|
g.write_v_source_line_info(node.pos)
|
||||||
msvc_attrs := g.write_fn_attrs(node.attrs)
|
msvc_attrs := g.write_fn_attrs(node.attrs)
|
||||||
// Live
|
// Live
|
||||||
@ -264,7 +273,19 @@ 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, heap_promoted := g.fn_args(node.params, node.is_variadic, node.scope)
|
fargs, fargtypes, heap_promoted := g.fn_args(node.params, node.scope)
|
||||||
|
if is_closure {
|
||||||
|
mut s := '$cur_closure_ctx *$c.closure_ctx'
|
||||||
|
if node.params.len > 0 {
|
||||||
|
s = ', ' + s
|
||||||
|
} else {
|
||||||
|
// remove generated `void`
|
||||||
|
g.out.cut_to(arg_start_pos)
|
||||||
|
}
|
||||||
|
g.definitions.write_string(s)
|
||||||
|
g.write(s)
|
||||||
|
g.nr_closures++
|
||||||
|
}
|
||||||
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 {
|
||||||
@ -386,6 +407,59 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const closure_ctx = '_V_closure_ctx'
|
||||||
|
|
||||||
|
fn closure_ctx_struct(node ast.FnDecl) string {
|
||||||
|
return 'struct _V_${node.name}_Ctx'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) gen_anon_fn(mut node ast.AnonFn) {
|
||||||
|
g.gen_anon_fn_decl(mut node)
|
||||||
|
if !node.decl.scope.has_inherited_vars() {
|
||||||
|
g.write(node.decl.name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// it may be possible to optimize `memdup` out if the closure never leaves current scope
|
||||||
|
ctx_var := g.new_tmp_var()
|
||||||
|
cur_line := g.go_before_stmt(0)
|
||||||
|
ctx_struct := closure_ctx_struct(node.decl)
|
||||||
|
g.writeln('$ctx_struct* $ctx_var = ($ctx_struct*) memdup(&($ctx_struct){')
|
||||||
|
g.indent++
|
||||||
|
for var in node.inherited_vars {
|
||||||
|
g.writeln('.$var.name = $var.name,')
|
||||||
|
}
|
||||||
|
g.indent--
|
||||||
|
g.writeln('}, sizeof($ctx_struct));')
|
||||||
|
g.empty_line = false
|
||||||
|
g.write(cur_line)
|
||||||
|
// TODO in case of an assignment, this should only call "__closure_set_data" and "__closure_set_function" (and free the former data)
|
||||||
|
g.write('__closure_create($node.decl.name, ${node.decl.params.len + 1}, $ctx_var)')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) gen_anon_fn_decl(mut node ast.AnonFn) {
|
||||||
|
if node.has_gen {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
node.has_gen = true
|
||||||
|
mut builder := strings.new_builder(256)
|
||||||
|
if node.inherited_vars.len > 0 {
|
||||||
|
ctx_struct := closure_ctx_struct(node.decl)
|
||||||
|
builder.writeln('$ctx_struct {')
|
||||||
|
for var in node.inherited_vars {
|
||||||
|
styp := g.typ(var.typ)
|
||||||
|
builder.writeln('\t$styp $var.name;')
|
||||||
|
}
|
||||||
|
builder.writeln('};\n')
|
||||||
|
}
|
||||||
|
pos := g.out.len
|
||||||
|
was_anon_fn := g.anon_fn
|
||||||
|
g.anon_fn = true
|
||||||
|
g.process_fn_decl(node.decl)
|
||||||
|
g.anon_fn = was_anon_fn
|
||||||
|
builder.write_string(g.out.cut_to(pos))
|
||||||
|
g.anon_fn_definitions << builder.str()
|
||||||
|
}
|
||||||
|
|
||||||
fn (g &Gen) defer_flag_var(stmt &ast.DeferStmt) string {
|
fn (g &Gen) defer_flag_var(stmt &ast.DeferStmt) string {
|
||||||
return '${g.last_fn_c_name}_defer_$stmt.idx_in_fn'
|
return '${g.last_fn_c_name}_defer_$stmt.idx_in_fn'
|
||||||
}
|
}
|
||||||
@ -405,7 +479,7 @@ 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, scope &ast.Scope) ([]string, []string, []bool) {
|
fn (mut g Gen) fn_args(args []ast.Param, scope &ast.Scope) ([]string, []string, []bool) {
|
||||||
mut fargs := []string{}
|
mut fargs := []string{}
|
||||||
mut fargtypes := []string{}
|
mut fargtypes := []string{}
|
||||||
mut heap_promoted := []bool{}
|
mut heap_promoted := []bool{}
|
||||||
@ -423,7 +497,7 @@ fn (mut g Gen) fn_args(args []ast.Param, is_variadic bool, scope &ast.Scope) ([]
|
|||||||
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, voidptr(0))
|
g.fn_args(func.params, voidptr(0))
|
||||||
g.write(')')
|
g.write(')')
|
||||||
g.definitions.write_string(')')
|
g.definitions.write_string(')')
|
||||||
fargs << caname
|
fargs << caname
|
||||||
|
@ -633,9 +633,11 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
|
|||||||
old_inside_defer := p.inside_defer
|
old_inside_defer := p.inside_defer
|
||||||
p.inside_defer = false
|
p.inside_defer = false
|
||||||
p.open_scope()
|
p.open_scope()
|
||||||
if !p.pref.backend.is_js() {
|
defer {
|
||||||
p.scope.detached_from_parent = true
|
p.close_scope()
|
||||||
}
|
}
|
||||||
|
p.scope.detached_from_parent = true
|
||||||
|
inherited_vars := if p.tok.kind == .lsbr { p.closure_vars() } else { []ast.Param{} }
|
||||||
// TODO generics
|
// TODO generics
|
||||||
args, _, is_variadic := p.fn_args()
|
args, _, is_variadic := p.fn_args()
|
||||||
for arg in args {
|
for arg in args {
|
||||||
@ -691,7 +693,6 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
|
|||||||
p.label_names = tmp
|
p.label_names = tmp
|
||||||
}
|
}
|
||||||
p.cur_fn_name = keep_fn_name
|
p.cur_fn_name = keep_fn_name
|
||||||
p.close_scope()
|
|
||||||
func.name = name
|
func.name = name
|
||||||
idx := p.table.find_or_register_fn_type(p.mod, func, true, false)
|
idx := p.table.find_or_register_fn_type(p.mod, func, true, false)
|
||||||
typ := ast.new_type(idx)
|
typ := ast.new_type(idx)
|
||||||
@ -714,6 +715,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
|
|||||||
scope: p.scope
|
scope: p.scope
|
||||||
label_names: label_names
|
label_names: label_names
|
||||||
}
|
}
|
||||||
|
inherited_vars: inherited_vars
|
||||||
typ: typ
|
typ: typ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -910,6 +912,50 @@ fn (mut p Parser) fn_args() ([]ast.Param, bool, bool) {
|
|||||||
return args, types_only, is_variadic
|
return args, types_only, is_variadic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut p Parser) closure_vars() []ast.Param {
|
||||||
|
p.check(.lsbr)
|
||||||
|
mut vars := []ast.Param{cap: 5}
|
||||||
|
for {
|
||||||
|
is_shared := p.tok.kind == .key_shared
|
||||||
|
is_atomic := p.tok.kind == .key_atomic
|
||||||
|
is_mut := p.tok.kind == .key_mut || is_shared || is_atomic
|
||||||
|
// FIXME is_shared & is_atomic aren't used further
|
||||||
|
if is_mut {
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
var_pos := p.tok.position()
|
||||||
|
p.check(.name)
|
||||||
|
var_name := p.prev_tok.lit
|
||||||
|
mut var := p.scope.parent.find_var(var_name) or {
|
||||||
|
p.error_with_pos('undefined ident: `$var_name`', p.prev_tok.position())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var.is_used = true
|
||||||
|
if is_mut {
|
||||||
|
var.is_changed = true
|
||||||
|
}
|
||||||
|
p.scope.register(ast.Var{
|
||||||
|
...(*var)
|
||||||
|
pos: var_pos
|
||||||
|
is_inherited: true
|
||||||
|
is_used: false
|
||||||
|
is_changed: false
|
||||||
|
is_mut: is_mut
|
||||||
|
})
|
||||||
|
vars << ast.Param{
|
||||||
|
pos: var_pos
|
||||||
|
name: var_name
|
||||||
|
is_mut: is_mut
|
||||||
|
}
|
||||||
|
if p.tok.kind != .comma {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
p.check(.rsbr)
|
||||||
|
return vars
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut p Parser) check_fn_mutable_arguments(typ ast.Type, pos token.Position) {
|
fn (mut p Parser) check_fn_mutable_arguments(typ ast.Type, pos token.Position) {
|
||||||
sym := p.table.get_type_symbol(typ)
|
sym := p.table.get_type_symbol(typ)
|
||||||
if sym.kind in [.array, .array_fixed, .interface_, .map, .placeholder, .struct_, .generic_inst,
|
if sym.kind in [.array, .array_fixed, .interface_, .map, .placeholder, .struct_, .generic_inst,
|
||||||
|
@ -1784,7 +1784,12 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident {
|
|||||||
if is_static {
|
if is_static {
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
if p.tok.kind == .name {
|
if p.tok.kind != .name {
|
||||||
|
p.error('unexpected token `$p.tok.lit`')
|
||||||
|
return ast.Ident{
|
||||||
|
scope: p.scope
|
||||||
|
}
|
||||||
|
}
|
||||||
pos := p.tok.position()
|
pos := p.tok.position()
|
||||||
mut name := p.check_name()
|
mut name := p.check_name()
|
||||||
if name == '_' {
|
if name == '_' {
|
||||||
@ -1825,11 +1830,6 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident {
|
|||||||
scope: p.scope
|
scope: p.scope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.error('unexpected token `$p.tok.lit`')
|
|
||||||
return ast.Ident{
|
|
||||||
scope: p.scope
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (p &Parser) is_typename(t token.Token) bool {
|
fn (p &Parser) is_typename(t token.Token) bool {
|
||||||
return t.kind == .name && (t.lit[0].is_capital() || p.table.known_type(t.lit))
|
return t.kind == .name && (t.lit[0].is_capital() || p.table.known_type(t.lit))
|
||||||
|
7
vlib/v/parser/tests/closure_not_declared.out
Normal file
7
vlib/v/parser/tests/closure_not_declared.out
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
vlib/v/parser/tests/closure_not_declared.vv:4:9: error: undefined ident: `a`
|
||||||
|
2 | a := 1
|
||||||
|
3 | f := fn () {
|
||||||
|
4 | print(a)
|
||||||
|
| ^
|
||||||
|
5 | }
|
||||||
|
6 | f()
|
8
vlib/v/parser/tests/closure_not_declared.vv
Normal file
8
vlib/v/parser/tests/closure_not_declared.vv
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fn my_fn() {
|
||||||
|
a := 1
|
||||||
|
f := fn () {
|
||||||
|
print(a)
|
||||||
|
}
|
||||||
|
f()
|
||||||
|
_ = a
|
||||||
|
}
|
7
vlib/v/parser/tests/closure_not_used.out
Normal file
7
vlib/v/parser/tests/closure_not_used.out
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
vlib/v/parser/tests/closure_not_used.vv:3:11: error: unused variable: `a`
|
||||||
|
1 | fn my_fn() {
|
||||||
|
2 | a := 1
|
||||||
|
3 | f := fn [a] () {
|
||||||
|
| ^
|
||||||
|
4 | print("hello")
|
||||||
|
5 | }
|
7
vlib/v/parser/tests/closure_not_used.vv
Normal file
7
vlib/v/parser/tests/closure_not_used.vv
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fn my_fn() {
|
||||||
|
a := 1
|
||||||
|
f := fn [a] () {
|
||||||
|
print("hello")
|
||||||
|
}
|
||||||
|
f()
|
||||||
|
}
|
6
vlib/v/parser/tests/closure_undefined_var.out
Normal file
6
vlib/v/parser/tests/closure_undefined_var.out
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
vlib/v/parser/tests/closure_undefined_var.vv:2:11: error: undefined ident: `dont_exist`
|
||||||
|
1 | fn my_fn() {
|
||||||
|
2 | f := fn [dont_exist] () {
|
||||||
|
| ~~~~~~~~~~
|
||||||
|
3 | print(dont_exist)
|
||||||
|
4 | }
|
6
vlib/v/parser/tests/closure_undefined_var.vv
Normal file
6
vlib/v/parser/tests/closure_undefined_var.vv
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
fn my_fn() {
|
||||||
|
f := fn [dont_exist] () {
|
||||||
|
print(dont_exist)
|
||||||
|
}
|
||||||
|
f()
|
||||||
|
}
|
153
vlib/v/tests/closure_test.v
Normal file
153
vlib/v/tests/closure_test.v
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
fn test_decl_assignment() {
|
||||||
|
my_var := 12
|
||||||
|
c1 := fn [my_var] () int {
|
||||||
|
return my_var
|
||||||
|
}
|
||||||
|
assert c1() == 12
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_assignment() {
|
||||||
|
v1 := 1
|
||||||
|
mut c := fn [v1] () int {
|
||||||
|
return v1
|
||||||
|
}
|
||||||
|
v2 := 3
|
||||||
|
c = fn [v2] () int {
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
assert c() == 3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_anon_with_3(f fn (int) int) int {
|
||||||
|
return f(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_closure_in_call() {
|
||||||
|
my_var := int(12)
|
||||||
|
r1 := call_anon_with_3(fn [my_var] (add int) int {
|
||||||
|
return my_var + add
|
||||||
|
})
|
||||||
|
assert r1 == 15
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_simple_counter() fn () int {
|
||||||
|
mut c := -1
|
||||||
|
return fn [mut c] () int {
|
||||||
|
c++
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn override_stack() {
|
||||||
|
// just create some variables to modify the stack
|
||||||
|
a := 1
|
||||||
|
b := a + 2
|
||||||
|
c := b + 3
|
||||||
|
d := c + 4
|
||||||
|
e := d + 5
|
||||||
|
f := e + 6
|
||||||
|
g := f + 7
|
||||||
|
_ = g
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_closure_exit_original_scope() {
|
||||||
|
mut c := create_simple_counter()
|
||||||
|
assert c() == 0
|
||||||
|
override_stack()
|
||||||
|
assert c() == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_vars_are_changed() {
|
||||||
|
mut my_var := 1
|
||||||
|
f1 := fn [mut my_var] (expected int) {
|
||||||
|
assert my_var == expected
|
||||||
|
}
|
||||||
|
f1(1)
|
||||||
|
my_var = 3
|
||||||
|
f1(1)
|
||||||
|
my_var = -1
|
||||||
|
f1(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Counter {
|
||||||
|
mut:
|
||||||
|
i u64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut c Counter) incr() {
|
||||||
|
c.i++
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut c Counter) next() u64 {
|
||||||
|
c.incr()
|
||||||
|
return c.i
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_call_methods() {
|
||||||
|
mut c := Counter{}
|
||||||
|
f1 := fn [mut c] () u64 {
|
||||||
|
return c.next()
|
||||||
|
}
|
||||||
|
assert f1() == 1
|
||||||
|
assert f1() == 2
|
||||||
|
c.incr()
|
||||||
|
assert f1() == 3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_call_methods_on_pointer() {
|
||||||
|
mut c := &Counter{}
|
||||||
|
f1 := fn [mut c] () u64 {
|
||||||
|
return c.next()
|
||||||
|
}
|
||||||
|
assert f1() == 1
|
||||||
|
assert f1() == 2
|
||||||
|
c.incr()
|
||||||
|
assert f1() == 4
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn test_methods_as_variables() {
|
||||||
|
mut c := Counter{}
|
||||||
|
f1 := c.next
|
||||||
|
assert f1() == 1
|
||||||
|
assert f1() == 2
|
||||||
|
c.incr()
|
||||||
|
assert f1() == 3
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn takes_callback(get fn () u64) u64 {
|
||||||
|
return get()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_methods_as_callback() {
|
||||||
|
mut c := Counter{}
|
||||||
|
assert takes_callback(c.next) == 1
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ZeroSize {}
|
||||||
|
|
||||||
|
fn test_zero_size_ctx() {
|
||||||
|
ctx := ZeroSize{}
|
||||||
|
c1 := fn [ctx] () int {
|
||||||
|
return 123
|
||||||
|
}
|
||||||
|
assert c1() == 123
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn test_go_call_closure() {
|
||||||
|
my_var := 12
|
||||||
|
ch := chan int{}
|
||||||
|
go fn [my_var, ch] () {
|
||||||
|
ch <- my_var
|
||||||
|
}()
|
||||||
|
assert <-ch == 12
|
||||||
|
go fn [ch] (arg int) {
|
||||||
|
ch <- arg
|
||||||
|
}(15)
|
||||||
|
assert <-ch == 15
|
||||||
|
}
|
||||||
|
*/
|
@ -13,10 +13,6 @@ pub mut:
|
|||||||
last_line int // the line number where the ast object ends (used by vfmt)
|
last_line int // the line number where the ast object ends (used by vfmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (pos Position) str() string {
|
|
||||||
return 'Position{ line_nr: $pos.line_nr, last_line: $pos.last_line, pos: $pos.pos, col: $pos.col, len: $pos.len }'
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (pos Position) extend(end Position) Position {
|
pub fn (pos Position) extend(end Position) Position {
|
||||||
return Position{
|
return Position{
|
||||||
...pos
|
...pos
|
||||||
@ -29,9 +25,9 @@ pub fn (pos Position) extend_with_last_line(end Position, last_line int) Positio
|
|||||||
return Position{
|
return Position{
|
||||||
len: end.pos - pos.pos + end.len
|
len: end.pos - pos.pos + end.len
|
||||||
line_nr: pos.line_nr
|
line_nr: pos.line_nr
|
||||||
last_line: last_line - 1
|
|
||||||
pos: pos.pos
|
pos: pos.pos
|
||||||
col: pos.col
|
col: pos.col
|
||||||
|
last_line: last_line - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user