diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 5151c29679..89ca2504e3 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -19,8 +19,8 @@ pub type Expr = AnonFn | ArrayDecompose | ArrayInit | AsCast | Assoc | AtExpr | pub type Stmt = AsmStmt | AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | DeferStmt | EmptyStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | - GlobalDecl | GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | - NodeError | Return | SqlStmt | StructDecl | TypeDecl + GlobalDecl | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | NodeError | + 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. @@ -963,20 +963,12 @@ pub: pos token.Position } -pub struct GoStmt { -pub: - pos token.Position -pub mut: - call_expr CallExpr -} - pub struct GoExpr { pub: pos token.Position pub mut: - go_stmt GoStmt -mut: - return_type Type + call_expr CallExpr + is_expr bool } pub struct GotoLabel { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 1a55bd623e..eeddd227c6 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3539,13 +3539,6 @@ fn (mut c Checker) stmt(node ast.Stmt) { ast.GlobalDecl { c.global_decl(node) } - ast.GoStmt { - c.go_stmt(mut node) - if node.call_expr.or_block.kind != .absent { - c.error('optional handling cannot be done in `go` call. Do it when calling `.wait()`', - node.call_expr.or_block.pos) - } - } ast.GotoLabel {} ast.GotoStmt { if !c.inside_unsafe { @@ -3779,8 +3772,12 @@ fn (mut c Checker) global_decl(node ast.GlobalDecl) { } } -fn (mut c Checker) go_stmt(mut node ast.GoStmt) { - c.call_expr(mut node.call_expr) +fn (mut c Checker) go_expr(mut node ast.GoExpr) ast.Type { + ret_type := c.call_expr(mut node.call_expr) + if node.call_expr.or_block.kind != .absent { + c.error('optional handling cannot be done in `go` call. Do it when calling `.wait()`', + node.call_expr.or_block.pos) + } // Make sure there are no mutable arguments for arg in node.call_expr.args { if arg.is_mut && !arg.typ.is_ptr() { @@ -3793,6 +3790,7 @@ fn (mut c Checker) go_stmt(mut node ast.GoStmt) { c.error('method in `go` statement cannot have non-reference mutable receiver', node.call_expr.left.position()) } + return c.table.find_or_register_thread(ret_type) } fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) { @@ -4156,14 +4154,6 @@ pub fn (mut c Checker) expr(node ast.Expr) ast.Type { } return ret_type } - ast.GoExpr { - mut ret_type := c.call_expr(mut node.go_stmt.call_expr) - if node.go_stmt.call_expr.or_block.kind != .absent { - c.error('optional handling cannot be done in `go` call. Do it when calling `.wait()`', - node.go_stmt.call_expr.or_block.pos) - } - return c.table.find_or_register_thread(ret_type) - } ast.ChanInit { return c.chan_init(mut node) } @@ -4226,6 +4216,9 @@ pub fn (mut c Checker) expr(node ast.Expr) ast.Type { ast.FloatLiteral { return ast.float_literal_type } + ast.GoExpr { + return c.go_expr(mut node) + } ast.Ident { // c.checked_ident = node.name res := c.ident(mut node) diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 326a0cec6b..4fe58dc23f 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -428,9 +428,6 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) { ast.GlobalDecl { f.global_decl(node) } - ast.GoStmt { - f.go_stmt(node, false) - } ast.GotoLabel { f.goto_label(node) } @@ -541,7 +538,7 @@ pub fn (mut f Fmt) expr(node ast.Expr) { f.write(node.val) } ast.GoExpr { - f.go_stmt(node.go_stmt, true) + f.go_expr(node) } ast.Ident { f.ident(node) @@ -1130,12 +1127,9 @@ pub fn (mut f Fmt) global_decl(node ast.GlobalDecl) { f.writeln(')\n') } -pub fn (mut f Fmt) go_stmt(node ast.GoStmt, is_expr bool) { +pub fn (mut f Fmt) go_expr(node ast.GoExpr) { f.write('go ') f.expr(node.call_expr) - if !is_expr { - f.writeln('') - } } pub fn (mut f Fmt) goto_label(node ast.GotoLabel) { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index e49efbe79c..ce648d3bd2 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -1183,9 +1183,6 @@ fn (mut g Gen) stmt(node ast.Stmt) { ast.GlobalDecl { g.global_decl(node) } - ast.GoStmt { - g.go_stmt(node, false) - } ast.GotoLabel { g.writeln('$node.name: {}') } @@ -5890,13 +5887,6 @@ fn (g &Gen) is_importing_os() bool { fn (mut g Gen) go_expr(node ast.GoExpr) { line := g.go_before_stmt(0) - handle := g.go_stmt(node.go_stmt, true) - g.empty_line = false - g.write(line) - g.write(handle) -} - -fn (mut g Gen) go_stmt(node ast.GoStmt, joinable bool) string { mut handle := '' tmp := g.new_tmp_var() mut expr := node.call_expr @@ -5966,30 +5956,30 @@ fn (mut g Gen) go_stmt(node ast.GoStmt, joinable bool) string { gohandle_name = '__v_thread_$opt${g.table.get_type_symbol(g.unwrap_generic(node.call_expr.return_type)).cname}' } if g.pref.os == .windows { - simple_handle := if joinable && node.call_expr.return_type != ast.void_type { + simple_handle := if node.is_expr && node.call_expr.return_type != ast.void_type { 'thread_handle_$tmp' } else { 'thread_$tmp' } g.writeln('HANDLE $simple_handle = CreateThread(0,0, (LPTHREAD_START_ROUTINE)$wrapper_fn_name, $arg_tmp_var, 0,0);') - if joinable && node.call_expr.return_type != ast.void_type { + if node.is_expr && node.call_expr.return_type != ast.void_type { g.writeln('$gohandle_name thread_$tmp = {') g.writeln('\t.ret_ptr = $arg_tmp_var->ret_ptr,') g.writeln('\t.handle = thread_handle_$tmp') g.writeln('};') } - if !joinable { + if !node.is_expr { g.writeln('CloseHandle(thread_$tmp);') } } else { g.writeln('pthread_t thread_$tmp;') g.writeln('pthread_create(&thread_$tmp, NULL, (void*)$wrapper_fn_name, $arg_tmp_var);') - if !joinable { + if !node.is_expr { g.writeln('pthread_detach(thread_$tmp);') } } g.writeln('// endgo\n') - if joinable { + if node.is_expr { handle = 'thread_$tmp' // create wait handler for this return type if none exists waiter_fn_name := gohandle_name + '_wait' @@ -6030,63 +6020,66 @@ fn (mut g Gen) go_stmt(node ast.GoStmt, joinable bool) string { } } // Register the wrapper type and function - if name in g.threaded_fns { - return handle - } - g.type_definitions.writeln('\ntypedef struct $wrapper_struct_name {') - if expr.is_method { - styp := g.typ(expr.receiver_type) - g.type_definitions.writeln('\t$styp arg0;') - } - need_return_ptr := g.pref.os == .windows && node.call_expr.return_type != ast.void_type - if expr.args.len == 0 && !need_return_ptr { - g.type_definitions.writeln('EMPTY_STRUCT_DECLARATION;') - } else { - for i, arg in expr.args { - styp := g.typ(arg.typ) - g.type_definitions.writeln('\t$styp arg${i + 1};') + if name !in g.threaded_fns { + g.type_definitions.writeln('\ntypedef struct $wrapper_struct_name {') + if expr.is_method { + styp := g.typ(expr.receiver_type) + g.type_definitions.writeln('\t$styp arg0;') } - } - if need_return_ptr { - g.type_definitions.writeln('\tvoid* ret_ptr;') - } - g.type_definitions.writeln('} $wrapper_struct_name;') - thread_ret_type := if g.pref.os == .windows { 'u32' } else { 'void*' } - g.type_definitions.writeln('$thread_ret_type ${wrapper_fn_name}($wrapper_struct_name *arg);') - g.gowrappers.writeln('$thread_ret_type ${wrapper_fn_name}($wrapper_struct_name *arg) {') - if node.call_expr.return_type != ast.void_type { - if g.pref.os == .windows { - g.gowrappers.write_string('\t*(($s_ret_typ*)(arg->ret_ptr)) = ') + need_return_ptr := g.pref.os == .windows && node.call_expr.return_type != ast.void_type + if expr.args.len == 0 && !need_return_ptr { + g.type_definitions.writeln('EMPTY_STRUCT_DECLARATION;') } else { - g.gowrappers.writeln('\t$s_ret_typ* ret_ptr = malloc(sizeof($s_ret_typ));') - g.gowrappers.write_string('\t*ret_ptr = ') + for i, arg in expr.args { + styp := g.typ(arg.typ) + g.type_definitions.writeln('\t$styp arg${i + 1};') + } } - } else { - g.gowrappers.write_string('\t') - } - g.gowrappers.write_string('${name}(') - if expr.is_method { - g.gowrappers.write_string('arg->arg0') - if expr.args.len > 0 { - g.gowrappers.write_string(', ') + if need_return_ptr { + g.type_definitions.writeln('\tvoid* ret_ptr;') } - } - for i in 0 .. expr.args.len { - g.gowrappers.write_string('arg->arg${i + 1}') - if i < expr.args.len - 1 { - g.gowrappers.write_string(', ') + g.type_definitions.writeln('} $wrapper_struct_name;') + thread_ret_type := if g.pref.os == .windows { 'u32' } else { 'void*' } + g.type_definitions.writeln('$thread_ret_type ${wrapper_fn_name}($wrapper_struct_name *arg);') + g.gowrappers.writeln('$thread_ret_type ${wrapper_fn_name}($wrapper_struct_name *arg) {') + if node.call_expr.return_type != ast.void_type { + if g.pref.os == .windows { + g.gowrappers.write_string('\t*(($s_ret_typ*)(arg->ret_ptr)) = ') + } else { + g.gowrappers.writeln('\t$s_ret_typ* ret_ptr = malloc(sizeof($s_ret_typ));') + g.gowrappers.write_string('\t*ret_ptr = ') + } + } else { + g.gowrappers.write_string('\t') } + g.gowrappers.write_string('${name}(') + if expr.is_method { + g.gowrappers.write_string('arg->arg0') + if expr.args.len > 0 { + g.gowrappers.write_string(', ') + } + } + for i in 0 .. expr.args.len { + g.gowrappers.write_string('arg->arg${i + 1}') + if i < expr.args.len - 1 { + g.gowrappers.write_string(', ') + } + } + g.gowrappers.writeln(');') + g.gowrappers.writeln('\tfree(arg);') + if g.pref.os != .windows && node.call_expr.return_type != ast.void_type { + g.gowrappers.writeln('\treturn ret_ptr;') + } else { + g.gowrappers.writeln('\treturn 0;') + } + g.gowrappers.writeln('}') + g.threaded_fns << name } - g.gowrappers.writeln(');') - g.gowrappers.writeln('\tfree(arg);') - if g.pref.os != .windows && node.call_expr.return_type != ast.void_type { - g.gowrappers.writeln('\treturn ret_ptr;') - } else { - g.gowrappers.writeln('\treturn 0;') + if node.is_expr { + g.empty_line = false + g.write(line) + g.write(handle) } - g.gowrappers.writeln('}') - g.threaded_fns << name - return handle } fn (mut g Gen) as_cast(node ast.AsCast) { diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index ad5e99a0a8..08765fc011 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -405,10 +405,6 @@ fn (mut g JsGen) stmt(node ast.Stmt) { ast.GlobalDecl { // TODO } - ast.GoStmt { - g.gen_go_stmt(node) - g.writeln('') - } ast.GotoLabel { g.writeln('${g.js_name(node.name)}:') } @@ -499,7 +495,7 @@ fn (mut g JsGen) expr(node ast.Expr) { g.gen_float_literal_expr(node) } ast.GoExpr { - // TODO + g.gen_go_expr(node) } ast.Ident { g.gen_ident(node) @@ -800,11 +796,14 @@ fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) { g.gen_method_decl(it) } -fn fn_has_go(it ast.FnDecl) bool { +fn fn_has_go(node ast.FnDecl) bool { mut has_go := false - for stmt in it.stmts { - if stmt is ast.GoStmt { - has_go = true + for stmt in node.stmts { + if stmt is ast.ExprStmt { + if stmt.expr is ast.GoExpr { + has_go = true + break + } } } return has_go @@ -989,8 +988,9 @@ fn (mut g JsGen) gen_for_stmt(it ast.ForStmt) { g.writeln('}') } -fn (mut g JsGen) gen_go_stmt(node ast.GoStmt) { - // x := node.call_expr as ast.CallEpxr // TODO +fn (mut g JsGen) gen_go_expr(node ast.GoExpr) { + // TODO Handle joinable expressions + // node.is_expr mut name := node.call_expr.name if node.call_expr.is_method { receiver_sym := g.table.get_type_symbol(node.call_expr.receiver_type) diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index ca8f5a1085..2b2057693d 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -90,9 +90,6 @@ pub fn (mut w Walker) stmt(node ast.Stmt) { w.expr(node.cond) w.stmts(node.stmts) } - ast.GoStmt { - w.expr(node.call_expr) - } ast.Return { w.exprs(node.exprs) } @@ -201,7 +198,7 @@ fn (mut w Walker) expr(node ast.Expr) { w.fn_by_name('eprintln') } ast.GoExpr { - w.expr(node.go_stmt.call_expr) + w.expr(node.call_expr) } ast.IndexExpr { w.expr(node.left) diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 7d532c4ba1..08955a3905 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -801,20 +801,10 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt { } } .key_go { - p.next() - spos := p.tok.position() - expr := p.expr(0) - call_expr := if expr is ast.CallExpr { - expr - } else { - p.error_with_pos('expression in `go` must be a function call', expr.position()) - ast.CallExpr{ - scope: p.scope - } - } - return ast.GoStmt{ - call_expr: call_expr - pos: spos.extend(p.prev_tok.position()) + go_expr := p.go_expr() + return ast.ExprStmt{ + expr: go_expr + pos: go_expr.pos } } .key_goto { diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index f98e469d86..e076cac2ee 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -92,12 +92,9 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { } } .key_go { - stmt := p.stmt(false) - go_stmt := stmt as ast.GoStmt - node = ast.GoExpr{ - go_stmt: go_stmt - pos: go_stmt.pos - } + mut go_expr := p.go_expr() + go_expr.is_expr = true + node = go_expr } .key_true, .key_false { node = ast.BoolLiteral{ @@ -490,6 +487,25 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr { } } +fn (mut p Parser) go_expr() ast.GoExpr { + p.next() + spos := p.tok.position() + expr := p.expr(0) + call_expr := if expr is ast.CallExpr { + expr + } else { + p.error_with_pos('expression in `go` must be a function call', expr.position()) + ast.CallExpr{ + scope: p.scope + } + } + pos := spos.extend(p.prev_tok.position()) + return ast.GoExpr{ + call_expr: call_expr + pos: pos + } +} + fn (p &Parser) fileis(s string) bool { return p.file_name.contains(s) }