all: support `thread` handles and `wait()` for functions returning optionals (#8990)

pull/8995/head
Uwe Krüger 2021-02-27 09:16:55 +01:00 committed by GitHub
parent aed348fb80
commit f67bff1696
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 242 additions and 15 deletions

View File

@ -49,6 +49,6 @@ fn start_client() ?&websocket.Client {
ws.connect() or { println(term.red('error on connect: $err')) }
go ws.listen() or { println(term.red('error on listen $err')) }
go ws.listen() // or { println(term.red('error on listen $err')) }
return ws
}

View File

@ -64,7 +64,7 @@ fn start_client() ? {
// // println('type: $msg.opcode payload:\n$msg.payload ref: $r')
// }, &r)
ws.connect() or { println('error on connect: $err') }
go write_echo(mut ws) or { println('error on write_echo $err') }
go write_echo(mut ws) // or { println('error on write_echo $err') }
ws.listen() or { println('error on listen $err') }
unsafe {
ws.free()

View File

@ -1333,9 +1333,13 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
elem_sym := c.table.get_type_symbol(elem_typ)
if elem_sym.kind == .thread {
if call_expr.args.len != 0 {
c.error('wait() does not have any arguments', call_expr.args[0].pos)
c.error('`.wait()` does not have any arguments', call_expr.args[0].pos)
}
thread_ret_type := elem_sym.thread_info().return_type
if thread_ret_type.has_flag(.optional) {
c.error('`.wait()` cannot be called for an array when thread functions return optionals. Iterate over the arrays elements instead and handle each returned optional with `or`.',
call_expr.pos)
}
call_expr.return_type = c.table.find_or_register_array(thread_ret_type)
} else {
c.error('`$left_type_sym.name` has no method `wait()` (only thread handles and arrays of them have)',
@ -1428,6 +1432,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
if call_expr.args.len > 0 {
c.error('wait() does not have any arguments', call_expr.args[0].pos)
}
call_expr.return_type = info.return_type
return info.return_type
}
mut method := table.Fn{}
@ -3155,6 +3160,10 @@ fn (mut c Checker) stmt(node ast.Stmt) {
}
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 {
@ -3634,7 +3643,11 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
return c.call_expr(mut node)
}
ast.GoExpr {
ret_type := c.call_expr(mut node.go_stmt.call_expr)
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 {

View File

@ -0,0 +1,69 @@
vlib/v/checker/tests/go_wait_or.vv:11:17: error: unexpected `?`, the function `wait` does not return an optional
9 | go d(1)
10 | ]
11 | r := tg.wait() ?
| ^
12 | println(r)
13 | s := tg[0].wait() or { panic('problem') }
vlib/v/checker/tests/go_wait_or.vv:13:20: error: unexpected `or` block, the function `wait` does not return an optional
11 | r := tg.wait() ?
12 | println(r)
13 | s := tg[0].wait() or { panic('problem') }
| ~~~~~~~~~~~~~~~~~~~~~~~
14 | println(s)
15 | tg2 := [
vlib/v/checker/tests/go_wait_or.vv:19:13: error: unexpected `or` block, the function `wait` does not return an optional
17 | go e(1)
18 | ]
19 | tg2.wait() or { panic('problem') }
| ~~~~~~~~~~~~~~~~~~~~~~~
20 | tg2[0].wait() ?
21 | tg3 := [
vlib/v/checker/tests/go_wait_or.vv:20:16: error: unexpected `?`, the function `wait` does not return an optional
18 | ]
19 | tg2.wait() or { panic('problem') }
20 | tg2[0].wait() ?
| ^
21 | tg3 := [
22 | go f(0)
vlib/v/checker/tests/go_wait_or.vv:25:6: error: `.wait()` cannot be called for an array when thread functions return optionals. Iterate over the arrays elements instead and handle each returned optional with `or`.
23 | go f(1)
24 | ]
25 | tg3.wait() or { panic('problem') }
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
26 | for t in tg3 {
27 | a := t.wait()
vlib/v/checker/tests/go_wait_or.vv:27:10: error: wait() returns an option, so it should have either an `or {}` block, or `?` at the end
25 | tg3.wait() or { panic('problem') }
26 | for t in tg3 {
27 | a := t.wait()
| ~~~~~~
28 | println(a)
29 | }
vlib/v/checker/tests/go_wait_or.vv:31:15: error: wait() returns an option, so it should have either an `or {}` block, or `?` at the end
29 | }
30 | for i, _ in tg3 {
31 | a := tg3[i].wait()
| ~~~~~~
32 | println(a)
33 | }
vlib/v/checker/tests/go_wait_or.vv:38:6: error: `.wait()` cannot be called for an array when thread functions return optionals. Iterate over the arrays elements instead and handle each returned optional with `or`.
36 | go g(1)
37 | ]
38 | tg4.wait()
| ~~~~~~
39 | tg4[0].wait()
40 | go g(3) or { panic('problem') }
vlib/v/checker/tests/go_wait_or.vv:39:9: error: wait() returns an option, so it should have either an `or {}` block, or `?` at the end
37 | ]
38 | tg4.wait()
39 | tg4[0].wait()
| ~~~~~~
40 | go g(3) or { panic('problem') }
41 | }
vlib/v/checker/tests/go_wait_or.vv:40:10: error: optional handling cannot be done in `go` call. Do it when calling `.wait()`
38 | tg4.wait()
39 | tg4[0].wait()
40 | go g(3) or { panic('problem') }
| ~~~~~~~~~~~~~~~~~~~~~~~
41 | }

View File

@ -0,0 +1,41 @@
fn d(n int) f64 { return f64(n) }
fn e(n int) { }
fn f(n int) ?f64 { return f64(n) }
fn g(n int) ? { }
fn main() {
tg := [
go d(0)
go d(1)
]
r := tg.wait() ?
println(r)
s := tg[0].wait() or { panic('problem') }
println(s)
tg2 := [
go e(0)
go e(1)
]
tg2.wait() or { panic('problem') }
tg2[0].wait() ?
tg3 := [
go f(0)
go f(1)
]
tg3.wait() or { panic('problem') }
for t in tg3 {
a := t.wait()
println(a)
}
for i, _ in tg3 {
a := tg3[i].wait()
println(a)
}
tg4 := [
go g(0)
go g(1)
]
tg4.wait()
tg4[0].wait()
go g(3) or { panic('problem') }
}

View File

@ -5942,10 +5942,12 @@ fn (mut g Gen) go_stmt(node ast.GoStmt, joinable bool) string {
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));')
}
is_opt := node.call_expr.return_type.has_flag(.optional)
gohandle_name := if node.call_expr.return_type == table.void_type {
'__v_thread'
if is_opt { '__v_thread_Option_void' } else { '__v_thread' }
} else {
'__v_thread_' + g.table.get_type_symbol(g.unwrap_generic(node.call_expr.return_type)).cname
opt := if is_opt { 'Option_' } else { '' }
'__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 != table.void_type {

View File

@ -120,14 +120,27 @@ pub fn (mut p Parser) parse_chan_type() table.Type {
}
pub fn (mut p Parser) parse_thread_type() table.Type {
is_opt := p.peek_tok.kind == .question
if is_opt {
p.next()
}
if p.peek_tok.kind != .name && p.peek_tok.kind != .key_mut && p.peek_tok.kind != .amp
&& p.peek_tok.kind != .lsbr {
p.next()
return table.thread_type
if is_opt {
mut ret_type := table.void_type
ret_type = ret_type.set_flag(.optional)
idx := p.table.find_or_register_thread(ret_type)
return table.new_type(idx)
} else {
return table.thread_type
}
}
p.next()
elem_type := p.parse_type()
idx := p.table.find_or_register_thread(elem_type)
if !is_opt {
p.next()
}
ret_type := p.parse_type()
idx := p.table.find_or_register_thread(ret_type)
return table.new_type(idx)
}

View File

@ -487,22 +487,32 @@ pub fn (t &Table) chan_cname(elem_type Type, is_mut bool) string {
[inline]
pub fn (t &Table) thread_name(return_type Type) string {
if return_type == void_type {
return 'thread'
if return_type.idx() == void_type_idx {
if return_type.has_flag(.optional) {
return 'thread ?'
} else {
return 'thread'
}
}
return_type_sym := t.get_type_symbol(return_type)
ptr := if return_type.is_ptr() { '&' } else { '' }
return 'thread $ptr$return_type_sym.name'
opt := if return_type.has_flag(.optional) { '?' } else { '' }
return 'thread $opt$ptr$return_type_sym.name'
}
[inline]
pub fn (t &Table) thread_cname(return_type Type) string {
if return_type == void_type {
return '__v_thread'
if return_type.has_flag(.optional) {
return '__v_thread_Option_void'
} else {
return '__v_thread'
}
}
return_type_sym := t.get_type_symbol(return_type)
suffix := if return_type.is_ptr() { '_ptr' } else { '' }
return '__v_thread_$return_type_sym.cname$suffix'
prefix := if return_type.has_flag(.optional) { 'Option_' } else { '' }
return '__v_thread_$prefix$return_type_sym.cname$suffix'
}
// map_source_name generates the original name for the v source.

View File

@ -0,0 +1,79 @@
fn f(n int) ?f64 {
if n < 0 {
return error('negative number')
}
return n + f64(n) / 2
}
fn g(n int) ? {
if n % 2 == 0 {
return error('even number')
} else {
return
}
}
fn test_opt_val_wait() {
h1 := go f(-1)
h2 := go f(3)
r1 := h1.wait() or { 17.0 }
r2 := h2.wait() or { 23.0 }
assert r1 == 17.0
assert r2 == 4.5
}
fn test_opt_void_wait() {
h1 := go g(2)
h2 := go g(3)
mut x := 0
mut y := 0
h1.wait() or { x = 1 }
h2.wait() or { y = 1 }
assert x == 1
assert y == 0
}
fn propagate(n int, m int) ?f64 {
h1 := go f(n)
h2 := go g(m)
r := h1.wait() ?
h2.wait() ?
return r
}
fn test_propagate() {
x := propagate(5, 3) or { 27.0 }
y := propagate(-3, 3) or { 29.0 }
z := propagate(5, 2) or { 31.0 }
assert x == 7.5
assert y == 29.0
assert z == 31.0
}
fn test_array_void_interate() {
mut r := []thread ?{}
for i in 0 .. 3 {
r << go g(i)
}
mut res := []int{len: 3, init: 17}
for i, t in r {
t.wait() or { res[i] = i }
}
assert res[0] == 0
assert res[1] == 17
assert res[2] == 2
}
fn test_array_val_interate() {
mut r := []thread ?f64{}
for i in -1 .. 2 {
r << go f(i)
}
mut res := []f64{len: 3}
for i, t in r {
res[i] = t.wait() or { 17.0 }
}
assert res[0] == 17.0
assert res[1] == 0.0
assert res[2] == 1.5
}