From bc98c11d9d8cd9ef89f7623eb042e3d1a4c9a184 Mon Sep 17 00:00:00 2001 From: yuyi Date: Fri, 1 Apr 2022 00:58:04 +0800 Subject: [PATCH] parser, checker, cgen: minor cleanup of go_expr() (#13875) --- vlib/v/checker/checker.v | 26 ---- vlib/v/checker/fn.v | 26 ++++ vlib/v/gen/c/cgen.v | 252 --------------------------------------- vlib/v/gen/c/fn.v | 252 +++++++++++++++++++++++++++++++++++++++ vlib/v/parser/expr.v | 21 ---- vlib/v/parser/fn.v | 21 ++++ 6 files changed, 299 insertions(+), 299 deletions(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index fbe56ccaa9..f7a3c6d734 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2111,32 +2111,6 @@ fn (mut c Checker) global_decl(mut node ast.GlobalDecl) { } } -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() { - c.error('function in `go` statement cannot contain mutable non-reference arguments', - arg.expr.pos()) - } - } - if node.call_expr.is_method && node.call_expr.receiver_type.is_ptr() - && !node.call_expr.left_type.is_ptr() { - c.error('method in `go` statement cannot have non-reference mutable receiver', - node.call_expr.left.pos()) - } - - if c.pref.backend.is_js() { - return c.table.find_or_register_promise(ret_type) - } else { - return c.table.find_or_register_thread(ret_type) - } -} - fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) { if stmt.is_goto { c.warn('inline assembly goto is not supported, it will most likely not work', diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 00af1bb7d1..4ca75d24a5 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1484,6 +1484,32 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { return ast.void_type } +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() { + c.error('function in `go` statement cannot contain mutable non-reference arguments', + arg.expr.pos()) + } + } + if node.call_expr.is_method && node.call_expr.receiver_type.is_ptr() + && !node.call_expr.left_type.is_ptr() { + c.error('method in `go` statement cannot have non-reference mutable receiver', + node.call_expr.left.pos()) + } + + if c.pref.backend.is_js() { + return c.table.find_or_register_promise(ret_type) + } else { + return c.table.find_or_register_thread(ret_type) + } +} + fn (mut c Checker) deprecate_fnmethod(kind string, name string, the_fn ast.Fn, node ast.CallExpr) { mut deprecation_message := '' now := time.now() diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index a2817675b4..10171ec987 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -5146,258 +5146,6 @@ fn (mut g Gen) enum_val(node ast.EnumVal) { g.write('${styp}__$node.val') } -fn (mut g Gen) go_expr(node ast.GoExpr) { - line := g.go_before_stmt(0) - mut handle := '' - tmp := g.new_tmp_var() - mut expr := node.call_expr - mut name := expr.name // util.no_dots(expr.name) - // TODO: fn call is duplicated. merge with fn_call(). - for i, concrete_type in expr.concrete_types { - if concrete_type != ast.void_type && concrete_type != 0 { - // Using _T_ to differentiate between get and get_string - // `foo()` => `foo_T_int()` - if i == 0 { - name += '_T' - } - name += '_' + g.typ(concrete_type) - } - } - if expr.is_method { - receiver_sym := g.table.sym(expr.receiver_type) - name = receiver_sym.name + '_' + name - } else if mut expr.left is ast.AnonFn { - g.gen_anon_fn_decl(mut expr.left) - name = expr.left.decl.name - } else if expr.is_fn_var { - name = g.table.sym(expr.fn_var_type).name - } - name = util.no_dots(name) - if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') { - mut key := expr.name - if expr.is_method { - sym := g.table.sym(expr.receiver_type) - key = sym.name + '.' + expr.name - } - g.write('/* obf go: $key */') - name = g.obf_table[key] or { - panic('cgen: obf name "$key" not found, this should never happen') - } - } - g.empty_line = true - g.writeln('// start go') - wrapper_struct_name := 'thread_arg_' + name - wrapper_fn_name := name + '_thread_wrapper' - arg_tmp_var := 'arg_' + tmp - g.writeln('$wrapper_struct_name *$arg_tmp_var = malloc(sizeof(thread_arg_$name));') - if expr.is_method { - g.write('$arg_tmp_var->arg0 = ') - // TODO is this needed? - /* - if false && !expr.return_type.is_ptr() { - g.write('&') - } - */ - g.expr(expr.left) - g.writeln(';') - } - for i, arg in expr.args { - g.write('$arg_tmp_var->arg${i + 1} = ') - g.expr(arg.expr) - g.writeln(';') - } - s_ret_typ := g.typ(node.call_expr.return_type) - if g.pref.os == .windows && node.call_expr.return_type != ast.void_type { - g.writeln('$arg_tmp_var->ret_ptr = malloc(sizeof($s_ret_typ));') - } - is_opt := node.call_expr.return_type.has_flag(.optional) - mut gohandle_name := '' - if node.call_expr.return_type == ast.void_type { - gohandle_name = if is_opt { '__v_thread_Option_void' } else { '__v_thread' } - } else { - opt := if is_opt { 'Option_' } else { '' } - gohandle_name = '__v_thread_$opt${g.table.sym(g.unwrap_generic(node.call_expr.return_type)).cname}' - } - if g.pref.os == .windows { - 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);') - g.writeln('if (!$simple_handle) panic_lasterr(tos3("`go ${name}()`: "));') - 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 !node.is_expr { - g.writeln('CloseHandle(thread_$tmp);') - } - } else { - g.writeln('pthread_t thread_$tmp;') - g.writeln('int ${tmp}_thr_res = pthread_create(&thread_$tmp, NULL, (void*)$wrapper_fn_name, $arg_tmp_var);') - g.writeln('if (${tmp}_thr_res) panic_error_number(tos3("`go ${name}()`: "), ${tmp}_thr_res);') - if !node.is_expr { - g.writeln('pthread_detach(thread_$tmp);') - } - } - g.writeln('// end go') - if node.is_expr { - handle = 'thread_$tmp' - // create wait handler for this return type if none exists - waiter_fn_name := gohandle_name + '_wait' - mut should_register := false - lock g.waiter_fns { - if waiter_fn_name !in g.waiter_fns { - g.waiter_fns << waiter_fn_name - should_register = true - } - } - if should_register { - g.gowrappers.writeln('\n$s_ret_typ ${waiter_fn_name}($gohandle_name thread) {') - mut c_ret_ptr_ptr := 'NULL' - if node.call_expr.return_type != ast.void_type { - g.gowrappers.writeln('\t$s_ret_typ* ret_ptr;') - c_ret_ptr_ptr = '&ret_ptr' - } - if g.pref.os == .windows { - if node.call_expr.return_type == ast.void_type { - g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread, INFINITE);') - } else { - g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread.handle, INFINITE);') - g.gowrappers.writeln('\tret_ptr = thread.ret_ptr;') - } - } else { - g.gowrappers.writeln('\tint stat = pthread_join(thread, (void **)$c_ret_ptr_ptr);') - } - g.gowrappers.writeln('\tif (stat != 0) { _v_panic(_SLIT("unable to join thread")); }') - if g.pref.os == .windows { - if node.call_expr.return_type == ast.void_type { - g.gowrappers.writeln('\tCloseHandle(thread);') - } else { - g.gowrappers.writeln('\tCloseHandle(thread.handle);') - } - } - if node.call_expr.return_type != ast.void_type { - g.gowrappers.writeln('\t$s_ret_typ ret = *ret_ptr;') - g.gowrappers.writeln('\tfree(ret_ptr);') - g.gowrappers.writeln('\treturn ret;') - } - g.gowrappers.writeln('}') - } - } - // Register the wrapper type and function - mut should_register := false - lock g.threaded_fns { - if name !in g.threaded_fns { - g.threaded_fns << name - should_register = true - } - } - if should_register { - 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 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)) = ') - } 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') - } - if expr.is_method { - unwrapped_rec_type := g.unwrap_generic(expr.receiver_type) - typ_sym := g.table.sym(unwrapped_rec_type) - if typ_sym.kind == .interface_ - && (typ_sym.info as ast.Interface).defines_method(expr.name) { - rec_cc_type := g.cc_type(unwrapped_rec_type, false) - receiver_type_name := util.no_dots(rec_cc_type) - g.gowrappers.write_string('${c_name(receiver_type_name)}_name_table[') - g.gowrappers.write_string('arg->arg0') - dot := if expr.left_type.is_ptr() { '->' } else { '.' } - mname := c_name(expr.name) - g.gowrappers.write_string('${dot}_typ]._method_${mname}(') - g.gowrappers.write_string('arg->arg0') - g.gowrappers.write_string('${dot}_object') - } else { - g.gowrappers.write_string('${name}(') - g.gowrappers.write_string('arg->arg0') - } - if expr.args.len > 0 { - g.gowrappers.write_string(', ') - } - } else { - g.gowrappers.write_string('${name}(') - } - if expr.args.len > 0 { - mut has_cast := false - for i in 0 .. expr.args.len { - if g.table.sym(expr.expected_arg_types[i]).kind == .interface_ - && g.table.sym(expr.args[i].typ).kind != .interface_ { - has_cast = true - break - } - } - if has_cast { - pos := g.out.len - g.call_args(expr) - mut call_args_str := g.out.after(pos) - g.out.go_back(call_args_str.len) - mut rep_group := []string{cap: 2 * expr.args.len} - for i in 0 .. expr.args.len { - rep_group << g.expr_string(expr.args[i].expr) - rep_group << 'arg->arg${i + 1}' - } - call_args_str = call_args_str.replace_each(rep_group) - g.gowrappers.write_string(call_args_str) - } else { - 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('}') - } - if node.is_expr { - g.empty_line = false - g.write(line) - g.write(handle) - } -} - fn (mut g Gen) as_cast(node ast.AsCast) { // Make sure the sum type can be cast to this type (the types // are the same), otherwise panic. diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 72d6cde6b4..117d0fdb40 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1667,6 +1667,258 @@ fn (mut g Gen) call_args(node ast.CallExpr) { } } +fn (mut g Gen) go_expr(node ast.GoExpr) { + line := g.go_before_stmt(0) + mut handle := '' + tmp := g.new_tmp_var() + mut expr := node.call_expr + mut name := expr.name // util.no_dots(expr.name) + // TODO: fn call is duplicated. merge with fn_call(). + for i, concrete_type in expr.concrete_types { + if concrete_type != ast.void_type && concrete_type != 0 { + // Using _T_ to differentiate between get and get_string + // `foo()` => `foo_T_int()` + if i == 0 { + name += '_T' + } + name += '_' + g.typ(concrete_type) + } + } + if expr.is_method { + receiver_sym := g.table.sym(expr.receiver_type) + name = receiver_sym.name + '_' + name + } else if mut expr.left is ast.AnonFn { + g.gen_anon_fn_decl(mut expr.left) + name = expr.left.decl.name + } else if expr.is_fn_var { + name = g.table.sym(expr.fn_var_type).name + } + name = util.no_dots(name) + if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') { + mut key := expr.name + if expr.is_method { + sym := g.table.sym(expr.receiver_type) + key = sym.name + '.' + expr.name + } + g.write('/* obf go: $key */') + name = g.obf_table[key] or { + panic('cgen: obf name "$key" not found, this should never happen') + } + } + g.empty_line = true + g.writeln('// start go') + wrapper_struct_name := 'thread_arg_' + name + wrapper_fn_name := name + '_thread_wrapper' + arg_tmp_var := 'arg_' + tmp + g.writeln('$wrapper_struct_name *$arg_tmp_var = malloc(sizeof(thread_arg_$name));') + if expr.is_method { + g.write('$arg_tmp_var->arg0 = ') + // TODO is this needed? + /* + if false && !expr.return_type.is_ptr() { + g.write('&') + } + */ + g.expr(expr.left) + g.writeln(';') + } + for i, arg in expr.args { + g.write('$arg_tmp_var->arg${i + 1} = ') + g.expr(arg.expr) + g.writeln(';') + } + s_ret_typ := g.typ(node.call_expr.return_type) + if g.pref.os == .windows && node.call_expr.return_type != ast.void_type { + g.writeln('$arg_tmp_var->ret_ptr = malloc(sizeof($s_ret_typ));') + } + is_opt := node.call_expr.return_type.has_flag(.optional) + mut gohandle_name := '' + if node.call_expr.return_type == ast.void_type { + gohandle_name = if is_opt { '__v_thread_Option_void' } else { '__v_thread' } + } else { + opt := if is_opt { 'Option_' } else { '' } + gohandle_name = '__v_thread_$opt${g.table.sym(g.unwrap_generic(node.call_expr.return_type)).cname}' + } + if g.pref.os == .windows { + 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);') + g.writeln('if (!$simple_handle) panic_lasterr(tos3("`go ${name}()`: "));') + 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 !node.is_expr { + g.writeln('CloseHandle(thread_$tmp);') + } + } else { + g.writeln('pthread_t thread_$tmp;') + g.writeln('int ${tmp}_thr_res = pthread_create(&thread_$tmp, NULL, (void*)$wrapper_fn_name, $arg_tmp_var);') + g.writeln('if (${tmp}_thr_res) panic_error_number(tos3("`go ${name}()`: "), ${tmp}_thr_res);') + if !node.is_expr { + g.writeln('pthread_detach(thread_$tmp);') + } + } + g.writeln('// end go') + if node.is_expr { + handle = 'thread_$tmp' + // create wait handler for this return type if none exists + waiter_fn_name := gohandle_name + '_wait' + mut should_register := false + lock g.waiter_fns { + if waiter_fn_name !in g.waiter_fns { + g.waiter_fns << waiter_fn_name + should_register = true + } + } + if should_register { + g.gowrappers.writeln('\n$s_ret_typ ${waiter_fn_name}($gohandle_name thread) {') + mut c_ret_ptr_ptr := 'NULL' + if node.call_expr.return_type != ast.void_type { + g.gowrappers.writeln('\t$s_ret_typ* ret_ptr;') + c_ret_ptr_ptr = '&ret_ptr' + } + if g.pref.os == .windows { + if node.call_expr.return_type == ast.void_type { + g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread, INFINITE);') + } else { + g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread.handle, INFINITE);') + g.gowrappers.writeln('\tret_ptr = thread.ret_ptr;') + } + } else { + g.gowrappers.writeln('\tint stat = pthread_join(thread, (void **)$c_ret_ptr_ptr);') + } + g.gowrappers.writeln('\tif (stat != 0) { _v_panic(_SLIT("unable to join thread")); }') + if g.pref.os == .windows { + if node.call_expr.return_type == ast.void_type { + g.gowrappers.writeln('\tCloseHandle(thread);') + } else { + g.gowrappers.writeln('\tCloseHandle(thread.handle);') + } + } + if node.call_expr.return_type != ast.void_type { + g.gowrappers.writeln('\t$s_ret_typ ret = *ret_ptr;') + g.gowrappers.writeln('\tfree(ret_ptr);') + g.gowrappers.writeln('\treturn ret;') + } + g.gowrappers.writeln('}') + } + } + // Register the wrapper type and function + mut should_register := false + lock g.threaded_fns { + if name !in g.threaded_fns { + g.threaded_fns << name + should_register = true + } + } + if should_register { + 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 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)) = ') + } 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') + } + if expr.is_method { + unwrapped_rec_type := g.unwrap_generic(expr.receiver_type) + typ_sym := g.table.sym(unwrapped_rec_type) + if typ_sym.kind == .interface_ + && (typ_sym.info as ast.Interface).defines_method(expr.name) { + rec_cc_type := g.cc_type(unwrapped_rec_type, false) + receiver_type_name := util.no_dots(rec_cc_type) + g.gowrappers.write_string('${c_name(receiver_type_name)}_name_table[') + g.gowrappers.write_string('arg->arg0') + dot := if expr.left_type.is_ptr() { '->' } else { '.' } + mname := c_name(expr.name) + g.gowrappers.write_string('${dot}_typ]._method_${mname}(') + g.gowrappers.write_string('arg->arg0') + g.gowrappers.write_string('${dot}_object') + } else { + g.gowrappers.write_string('${name}(') + g.gowrappers.write_string('arg->arg0') + } + if expr.args.len > 0 { + g.gowrappers.write_string(', ') + } + } else { + g.gowrappers.write_string('${name}(') + } + if expr.args.len > 0 { + mut has_cast := false + for i in 0 .. expr.args.len { + if g.table.sym(expr.expected_arg_types[i]).kind == .interface_ + && g.table.sym(expr.args[i].typ).kind != .interface_ { + has_cast = true + break + } + } + if has_cast { + pos := g.out.len + g.call_args(expr) + mut call_args_str := g.out.after(pos) + g.out.go_back(call_args_str.len) + mut rep_group := []string{cap: 2 * expr.args.len} + for i in 0 .. expr.args.len { + rep_group << g.expr_string(expr.args[i].expr) + rep_group << 'arg->arg${i + 1}' + } + call_args_str = call_args_str.replace_each(rep_group) + g.gowrappers.write_string(call_args_str) + } else { + 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('}') + } + if node.is_expr { + g.empty_line = false + g.write(line) + g.write(handle) + } +} + // similar to `autofree_call_pregen()` but only to to handle [keep_args_alive] for C functions fn (mut g Gen) keep_alive_call_pregen(node ast.CallExpr) int { g.empty_line = true diff --git a/vlib/v/parser/expr.v b/vlib/v/parser/expr.v index 6738491ee4..c22d311746 100644 --- a/vlib/v/parser/expr.v +++ b/vlib/v/parser/expr.v @@ -545,27 +545,6 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr { } } -fn (mut p Parser) go_expr() ast.GoExpr { - p.next() - spos := p.tok.pos() - 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.pos()) - ast.CallExpr{ - scope: p.scope - } - } - pos := spos.extend(p.prev_tok.pos()) - p.register_auto_import('sync.threads') - p.table.gostmts++ - return ast.GoExpr{ - call_expr: call_expr - pos: pos - } -} - fn (p &Parser) fileis(s string) bool { return p.file_name.contains(s) } diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index fd72618bc3..8b5ce48a0c 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -928,6 +928,27 @@ fn (mut p Parser) fn_args() ([]ast.Param, bool, bool) { return args, types_only, is_variadic } +fn (mut p Parser) go_expr() ast.GoExpr { + p.next() + spos := p.tok.pos() + 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.pos()) + ast.CallExpr{ + scope: p.scope + } + } + pos := spos.extend(p.prev_tok.pos()) + p.register_auto_import('sync.threads') + p.table.gostmts++ + return ast.GoExpr{ + call_expr: call_expr + pos: pos + } +} + fn (mut p Parser) closure_vars() []ast.Param { p.check(.lsbr) mut vars := []ast.Param{cap: 5}