diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index cf8aa07178..911a90c4c5 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -11,10 +11,10 @@ pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl pub type Expr = AnonFn | ArrayDecompose | ArrayInit | AsCast | Assoc | AtExpr | BoolLiteral | CTempVar | CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall | ComptimeSelector | - ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | - IntegerLiteral | Likely | LockExpr | MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | - PrefixExpr | RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | - StringLiteral | StructInit | Type | TypeOf | UnsafeExpr + ConcatExpr | EnumVal | FloatLiteral | GoExpr | Ident | IfExpr | IfGuardExpr | IndexExpr | + InfixExpr | IntegerLiteral | Likely | LockExpr | MapInit | MatchExpr | None | OrExpr | + ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectExpr | SelectorExpr | SizeOf | + SqlExpr | StringInterLiteral | StringLiteral | StructInit | Type | TypeOf | UnsafeExpr pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt | @@ -875,8 +875,18 @@ pub: pub struct GoStmt { pub: - call_expr Expr - pos token.Position + pos token.Position +pub mut: + call_expr CallExpr +} + +pub struct GoExpr { +pub: + pos token.Position +pub mut: + go_stmt GoStmt +mut: + return_type table.Type } pub struct GotoLabel { @@ -1172,7 +1182,7 @@ pub fn (expr Expr) position() token.Position { AnonFn { return expr.decl.pos } - ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr, CastExpr, ChanInit, CharLiteral, ConcatExpr, Comment, EnumVal, FloatLiteral, Ident, IfExpr, IndexExpr, IntegerLiteral, Likely, LockExpr, MapInit, MatchExpr, None, OrExpr, ParExpr, PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr, StringInterLiteral, StringLiteral, StructInit, Type, TypeOf, UnsafeExpr { + ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr, CastExpr, ChanInit, CharLiteral, ConcatExpr, Comment, EnumVal, FloatLiteral, GoExpr, Ident, IfExpr, IndexExpr, IntegerLiteral, Likely, LockExpr, MapInit, MatchExpr, None, OrExpr, ParExpr, PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr, StringInterLiteral, StringLiteral, StructInit, Type, TypeOf, UnsafeExpr { return expr.pos } ArrayDecompose { @@ -1261,7 +1271,7 @@ pub fn (stmt Stmt) position() token.Position { return stmt.pos } GoStmt { - return stmt.call_expr.position() + return stmt.call_expr.pos } TypeDecl { match stmt { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 7bcfdee534..c11794c4b4 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1338,6 +1338,12 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { if !c.check_types(arg_type, info.elem_type) && !c.check_types(left_type, arg_type) { c.error('cannot $method_name `$arg_sym.name` to `$left_type_sym.name`', arg_expr.position()) } + } else if left_type_sym.kind == .gohandle && method_name == 'wait' { + info := left_type_sym.info as table.GoHandle + if call_expr.args.len > 0 { + c.error('wait() does not have any arguments', call_expr.args[0].pos) + } + return info.return_type } mut method := table.Fn{} mut has_method := false @@ -2978,22 +2984,17 @@ fn (mut c Checker) stmt(node ast.Stmt) { } } ast.GoStmt { - if node.call_expr !is ast.CallExpr { - c.error('expression in `go` must be a function call', node.call_expr.position()) + c.call_expr(mut node.call_expr) + // 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.position()) + } } - c.expr(node.call_expr) - if mut node.call_expr is ast.CallExpr { - // 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.position()) - } - } - 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.position()) - } + 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.position()) } } ast.GotoLabel {} @@ -3241,6 +3242,10 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { ast.CallExpr { return c.call_expr(mut node) } + ast.GoExpr { + ret_type := c.call_expr(mut node.go_stmt.call_expr) + return c.table.find_or_register_gohandle(ret_type) + } ast.ChanInit { return c.chan_init(mut node) } diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 0c70ff9ce8..bb02a37fb7 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -892,6 +892,9 @@ pub fn (mut f Fmt) expr(node ast.Expr) { ast.FloatLiteral { f.write(node.val) } + ast.GoExpr { + f.stmt(node.go_stmt) + } ast.IfExpr { f.if_expr(node) } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index fe8858a96e..19ba147ca1 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -89,6 +89,7 @@ mut: defer_profile_code string str_types []string // types that need automatic str() generation threaded_fns []string // for generating unique wrapper types and fns for `go xxx()` + waiter_fns []string // functions that wait for `go xxx()` to finish array_fn_definitions []string // array equality functions that have been defined map_fn_definitions []string // map equality functions that have been defined struct_fn_definitions []string // struct equality functions that have been defined @@ -1107,7 +1108,7 @@ fn (mut g Gen) stmt(node ast.Stmt) { g.global_decl(node) } ast.GoStmt { - g.go_stmt(node) + g.go_stmt(node, false) } ast.GotoLabel { g.writeln('$node.name: {}') @@ -2577,6 +2578,9 @@ fn (mut g Gen) expr(node ast.Expr) { ast.FloatLiteral { g.write(node.val) } + ast.GoExpr { + g.go_expr(node) + } ast.Ident { g.ident(node) } @@ -4883,6 +4887,22 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) { table.Alias { // table.Alias { TODO } + table.GoHandle { + if g.pref.os == .windows { + if name == 'gohandle_void' { + g.type_definitions.writeln('typedef HANDLE $name;') + } else { + // Windows can only return `u32` (no void*) from a thread, so the + // V gohandle must maintain a pointer to the return value + g.type_definitions.writeln('typedef struct {') + g.type_definitions.writeln('\tvoid* ret_ptr;') + g.type_definitions.writeln('\tHANDLE handle;') + g.type_definitions.writeln('} $name;') + } + } else { + g.type_definitions.writeln('typedef pthread_t $name;') + } + } table.SumType { g.typedefs.writeln('typedef struct $name $name;') g.type_definitions.writeln('') @@ -5414,9 +5434,18 @@ fn (g &Gen) is_importing_os() bool { return 'os' in g.table.imports } -fn (mut g Gen) go_stmt(node ast.GoStmt) { +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() - expr := node.call_expr as ast.CallExpr + expr := node.call_expr mut name := expr.name // util.no_dots(expr.name) // TODO: fn call is duplicated. merge with fn_call(). if expr.generic_type != table.void_type && expr.generic_type != 0 { @@ -5454,23 +5483,87 @@ fn (mut g Gen) go_stmt(node ast.GoStmt) { 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 != table.void_type { + g.writeln('$arg_tmp_var->ret_ptr = malloc(sizeof($s_ret_typ));') + } + gohandle_name := 'gohandle_' + + g.table.get_type_symbol(g.unwrap_generic(node.call_expr.return_type)).name if g.pref.os == .windows { - g.writeln('CreateThread(0,0, (LPTHREAD_START_ROUTINE)$wrapper_fn_name, $arg_tmp_var, 0,0);') + simple_handle := if joinable && node.call_expr.return_type != table.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 != table.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 { + 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 { + g.writeln('pthread_detach(thread_$tmp);') + } } g.writeln('// endgo\n') + if joinable { + handle = 'thread_$tmp' + // create wait handler for this return type if none exists + waiter_fn_name := gohandle_name + '_wait' + if waiter_fn_name !in g.waiter_fns { + 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 != table.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 == table.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, $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 == table.void_type { + g.gowrappers.writeln('\tCloseHandle(thread);') + } else { + g.gowrappers.writeln('\tCloseHandle(thread.handle);') + } + } + if node.call_expr.return_type != table.void_type { + g.gowrappers.writeln('\t$s_ret_typ ret = *ret_ptr;') + g.gowrappers.writeln('\tfree(ret_ptr);') + g.gowrappers.writeln('\treturn ret;') + } else { + g.gowrappers.writeln('\treturn;') + } + g.gowrappers.writeln('}') + g.waiter_fns << waiter_fn_name + } + } // Register the wrapper type and function if name in g.threaded_fns { - return + 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;') } - if expr.args.len == 0 { + need_return_ptr := g.pref.os == .windows && node.call_expr.return_type != table.void_type + if expr.args.len == 0 && !need_return_ptr { g.type_definitions.writeln('EMPTY_STRUCT_DECLARATION;') } else { for i, arg in expr.args { @@ -5478,10 +5571,24 @@ fn (mut g Gen) go_stmt(node ast.GoStmt) { 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;') - g.type_definitions.writeln('void* ${wrapper_fn_name}($wrapper_struct_name *arg);') - g.gowrappers.writeln('void* ${wrapper_fn_name}($wrapper_struct_name *arg) {') - g.gowrappers.write('\t${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 != table.void_type { + if g.pref.os == .windows { + g.gowrappers.write('\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('\t*ret_ptr = ') + } + } else { + g.gowrappers.write('\t') + } + g.gowrappers.write('${name}(') if expr.is_method { g.gowrappers.write('arg->arg0') if expr.args.len > 0 { @@ -5495,9 +5602,15 @@ fn (mut g Gen) go_stmt(node ast.GoStmt) { } } g.gowrappers.writeln(');') - g.gowrappers.writeln('\treturn 0;') + g.gowrappers.writeln('\tfree(arg);') + if g.pref.os != .windows && node.call_expr.return_type != table.void_type { + g.gowrappers.writeln('\treturn ret_ptr;') + } else { + g.gowrappers.writeln('\treturn 0;') + } 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/builtin_types.v b/vlib/v/gen/js/builtin_types.v index b344c7285d..f55d222228 100644 --- a/vlib/v/gen/js/builtin_types.v +++ b/vlib/v/gen/js/builtin_types.v @@ -165,6 +165,9 @@ pub fn (mut g JsGen) typ(t table.Type) string { .aggregate { panic('TODO: unhandled aggregate in JS') } + .gohandle { + panic('TODO: unhandled gohandle in JS') + } } /* else { diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index c1a53138f3..a3a773f2bd 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -475,6 +475,9 @@ fn (mut g JsGen) expr(node ast.Expr) { ast.FloatLiteral { g.write('${g.typ(table.Type(table.f32_type))}($node.val)') } + ast.GoExpr { + // TODO + } ast.Ident { g.gen_ident(node) } @@ -923,33 +926,28 @@ fn (mut g JsGen) gen_for_stmt(it ast.ForStmt) { fn (mut g JsGen) gen_go_stmt(node ast.GoStmt) { // x := node.call_expr as ast.CallEpxr // TODO - match node.call_expr { - ast.CallExpr { - mut name := node.call_expr.name - if node.call_expr.is_method { - receiver_sym := g.table.get_type_symbol(node.call_expr.receiver_type) - name = receiver_sym.name + '.' + name - } - // todo: please add a name feild without the mod name for ast.CallExpr - if name.starts_with('${node.call_expr.mod}.') { - name = name[node.call_expr.mod.len + 1..] - } - g.writeln('await new Promise(function(resolve){') - g.inc_indent() - g.write('${name}(') - for i, arg in node.call_expr.args { - g.expr(arg.expr) - if i < node.call_expr.args.len - 1 { - g.write(', ') - } - } - g.writeln(');') - g.writeln('resolve();') - g.dec_indent() - g.writeln('});') - } - else {} + mut name := node.call_expr.name + if node.call_expr.is_method { + receiver_sym := g.table.get_type_symbol(node.call_expr.receiver_type) + name = receiver_sym.name + '.' + name } + // todo: please add a name feild without the mod name for ast.CallExpr + if name.starts_with('${node.call_expr.mod}.') { + name = name[node.call_expr.mod.len + 1..] + } + g.writeln('await new Promise(function(resolve){') + g.inc_indent() + g.write('${name}(') + for i, arg in node.call_expr.args { + g.expr(arg.expr) + if i < node.call_expr.args.len - 1 { + g.write(', ') + } + } + g.writeln(');') + g.writeln('resolve();') + g.dec_indent() + g.writeln('});') } fn (mut g JsGen) gen_import_stmt(it ast.Import) { diff --git a/vlib/v/parser/assign.v b/vlib/v/parser/assign.v index 6e969f094b..a5debeab50 100644 --- a/vlib/v/parser/assign.v +++ b/vlib/v/parser/assign.v @@ -90,7 +90,39 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme op := p.tok.kind pos := p.tok.position() p.next() - right, right_comments := p.expr_list() + mut right_comments := []ast.Comment{} + mut right := []ast.Expr{cap: left.len} + if p.tok.kind == .key_go { + spos := p.tok.position() + p.next() + right_comments = p.eat_comments() + mut mod := '' + mut language := table.Language.v + if p.peek_tok.kind == .dot { + if p.tok.lit == 'C' { + language = table.Language.c + p.check_for_impure_v(language, p.tok.position()) + } else if p.tok.lit == 'JS' { + language = table.Language.js + p.check_for_impure_v(language, p.tok.position()) + } else { + mod = p.tok.lit + } + p.next() + p.next() + } + call_expr := p.call_expr(language, mod) + allpos := spos.extend(p.tok.position()) + right << ast.GoExpr{ + go_stmt: ast.GoStmt{ + call_expr: call_expr + pos: allpos + } + pos: allpos + } + } else { + right, right_comments = p.expr_list() + } mut comments := []ast.Comment{cap: left_comments.len + right_comments.len} comments << left_comments comments << right_comments diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index dd1480aafb..7aef071a13 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -738,10 +738,16 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt { p.next() spos := p.tok.position() expr := p.expr(0) - // mut call_expr := &ast.CallExpr(0) // TODO - // { call_expr = it } + 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: expr + call_expr: call_expr pos: spos.extend(p.tok.position()) } } diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index 3421ef0c17..f1629495da 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -454,6 +454,20 @@ pub fn (t &Table) chan_cname(elem_type Type, is_mut bool) string { return 'chan_$elem_type_sym.cname' + suffix } +[inline] +pub fn (t &Table) gohandle_name(return_type Type) string { + return_type_sym := t.get_type_symbol(return_type) + ptr := if return_type.is_ptr() { '&' } else { '' } + return 'gohandle[$ptr$return_type_sym.name]' +} + +[inline] +pub fn (t &Table) gohandle_cname(return_type Type) string { + return_type_sym := t.get_type_symbol(return_type) + suffix := if return_type.is_ptr() { '_ptr' } else { '' } + return 'gohandle_$return_type_sym.cname$suffix' +} + // map_source_name generates the original name for the v source. // e. g. map[string]int [inline] @@ -517,6 +531,27 @@ pub fn (mut t Table) find_or_register_map(key_type Type, value_type Type) int { return t.register_type_symbol(map_typ) } +pub fn (mut t Table) find_or_register_gohandle(return_type Type) int { + name := t.gohandle_name(return_type) + cname := t.gohandle_cname(return_type) + // existing + existing_idx := t.type_idxs[name] + if existing_idx > 0 { + return existing_idx + } + // register + gohandle_typ := TypeSymbol{ + parent_idx: gohandle_type_idx + kind: .gohandle + name: name + cname: cname + info: GoHandle{ + return_type: return_type + } + } + return t.register_type_symbol(gohandle_typ) +} + pub fn (mut t Table) find_or_register_array(elem_type Type) int { name := t.array_name(elem_type) cname := t.array_cname(elem_type) diff --git a/vlib/v/table/types.v b/vlib/v/table/types.v index fa2a64695f..d413d749f3 100644 --- a/vlib/v/table/types.v +++ b/vlib/v/table/types.v @@ -16,7 +16,7 @@ import strings pub type Type = int pub type TypeInfo = Aggregate | Alias | Array | ArrayFixed | Chan | Enum | FnType | GenericStructInst | - Interface | Map | MultiReturn | Struct | SumType + GoHandle | Interface | Map | MultiReturn | Struct | SumType pub enum Language { v @@ -306,6 +306,7 @@ pub const ( any_type_idx = 25 float_literal_type_idx = 26 int_literal_type_idx = 27 + gohandle_type_idx = 28 ) pub const ( @@ -350,12 +351,15 @@ pub const ( any_type = new_type(any_type_idx) float_literal_type = new_type(float_literal_type_idx) int_literal_type = new_type(int_literal_type_idx) + gohandle_type = new_type(gohandle_type_idx) ) pub const ( builtin_type_names = ['void', 'voidptr', 'charptr', 'byteptr', 'i8', 'i16', 'int', 'i64', 'u16', 'u32', 'u64', 'int_literal', 'f32', 'f64', 'float_literal', 'string', 'ustring', 'char', 'byte', - 'bool', 'none', 'array', 'array_fixed', 'map', 'chan', 'any', 'struct', 'mapnode', 'size_t', 'rune'] + 'bool', 'none', 'array', 'array_fixed', 'map', 'chan', 'any', 'struct', 'mapnode', 'size_t', 'rune', + 'gohandle', + ] ) pub struct MultiReturn { @@ -417,6 +421,7 @@ pub enum Kind { float_literal int_literal aggregate + gohandle } pub fn (t &TypeSymbol) str() string { @@ -467,6 +472,14 @@ pub fn (t &TypeSymbol) chan_info() Chan { } } +[inline] +pub fn (t &TypeSymbol) gohandle_info() GoHandle { + match mut t.info { + GoHandle { return t.info } + else { panic('TypeSymbol.gohandle_info(): no gohandle info for type: $t.name') } + } +} + [inline] pub fn (t &TypeSymbol) map_info() Map { match mut t.info { @@ -529,6 +542,7 @@ pub fn (mut t Table) register_builtin_type_symbols() { cname: 'int_literal' mod: 'builtin' ) + t.register_type_symbol(kind: .gohandle, name: 'gohandle', cname: 'gohandle', mod: 'builtin') } [inline] @@ -602,6 +616,7 @@ pub fn (k Kind) str() string { .generic_struct_inst { 'generic_struct_inst' } .rune { 'rune' } .aggregate { 'aggregate' } + .gohandle { 'gohandle' } } return k_str } @@ -709,6 +724,11 @@ pub mut: is_mut bool } +pub struct GoHandle { +pub mut: + return_type Type +} + pub struct Map { pub mut: key_type Type diff --git a/vlib/v/tests/go_wait_1_test.v b/vlib/v/tests/go_wait_1_test.v new file mode 100644 index 0000000000..71e4839995 --- /dev/null +++ b/vlib/v/tests/go_wait_1_test.v @@ -0,0 +1,10 @@ +fn f(x int, y f64) f64 { + return x * y +} + +fn test_go_return() { + r := go f(3, 4.0) + z := r.wait() + assert typeof(z).name == 'f64' + assert z == 12.0 +} diff --git a/vlib/v/tests/go_wait_2_test.v b/vlib/v/tests/go_wait_2_test.v new file mode 100644 index 0000000000..d02c525f11 --- /dev/null +++ b/vlib/v/tests/go_wait_2_test.v @@ -0,0 +1,23 @@ +import time + +struct St { +mut: + x f64 +} + +fn f(x int, y f64, shared s St) { + time.usleep(50000) + lock s { + s.x = x * y + } + return +} + +fn test_go_return() { + shared t := &St{} + r := go f(3, 4.0, shared t) + r.wait() + rlock t { + assert t.x == 12.0 + } +}