sync/select: allow push of literals and calculated expressions (#6429)

pull/6431/head
Uwe Krüger 2020-09-20 03:50:09 +02:00 committed by GitHub
parent c781a5f245
commit 4ae88c69ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 96 additions and 24 deletions

View File

@ -36,32 +36,23 @@ fn test_select() {
go do_send_int(chi) go do_send_int(chi)
go do_send_byte(chb) go do_send_byte(chb)
go do_send_i64(chl) go do_send_i64(chl)
mut channels := [&sync.Channel(chi), &sync.Channel(recch), &sync.Channel(chl), &sync.Channel(chb)]
directions := [sync.Direction.pop, .push, .pop, .pop]
mut sum := i64(0) mut sum := i64(0)
mut rl := i64(0) mut rl := i64(0)
mut ri := int(0)
mut rb := byte(0)
mut sl := i64(0) mut sl := i64(0)
mut objs := [voidptr(&ri), &sl, &rl, &rb]
for _ in 0 .. 1200 { for _ in 0 .. 1200 {
idx := sync.channel_select(mut channels, directions, mut objs, -1) select {
match idx { ri := <-chi {
0 {
sum += ri sum += ri
} }
1 { recch <- sl {
sl++ sl++
} }
2 { rl = <-chl {
sum += rl sum += rl
} }
3 { rb := <-chb {
sum += rb sum += rb
} }
else {
println('got $idx (timeout)')
}
} }
} }
// Use Gauß' formula for the first 2 contributions // Use Gauß' formula for the first 2 contributions

View File

@ -5,9 +5,12 @@ struct St {
a int a int
} }
fn f1(ch1 chan int, ch2 chan St, ch3 chan int, sem sync.Semaphore) { fn getint() int {
return 8
}
fn f1(ch1 chan int, ch2 chan St, ch3 chan int, ch4 chan int, ch5 chan int, sem sync.Semaphore) {
mut a := 5 mut a := 5
st := St{}
select { select {
a = <-ch3 { a = <-ch3 {
a = 0 a = 0
@ -15,9 +18,18 @@ fn f1(ch1 chan int, ch2 chan St, ch3 chan int, sem sync.Semaphore) {
b := <-ch2 { b := <-ch2 {
a = b.a a = b.a
} }
ch2 <- st { ch3 <- 5 {
a = 1
}
ch2 <- St{a: 37} {
a = 2 a = 2
} }
ch4 <- (6 + 7 * 9) {
a = 8
}
ch5 <- getint() {
a = 9
}
> 300 * time.millisecond { > 300 * time.millisecond {
a = 3 a = 3
} }
@ -30,7 +42,7 @@ fn f2(ch1 chan St, ch2 chan int, sem sync.Semaphore) {
mut r := 23 mut r := 23
for i in 0 .. 2 { for i in 0 .. 2 {
select { select {
b := <- ch1 { b := <-ch1 {
r = b.a r = b.a
} }
ch2 <- r { ch2 <- r {
@ -50,6 +62,8 @@ fn test_select_blocks() {
ch1 := chan int{cap: 1} ch1 := chan int{cap: 1}
ch2 := chan St{} ch2 := chan St{}
ch3 := chan int{} ch3 := chan int{}
ch4 := chan int{}
ch5 := chan int{}
sem := sync.new_semaphore() sem := sync.new_semaphore()
mut r := false mut r := false
t := select { t := select {
@ -69,7 +83,7 @@ fn test_select_blocks() {
ch2 <- St{a: 13} ch2 <- St{a: 13}
sem.wait() sem.wait()
stopwatch := time.new_stopwatch({}) stopwatch := time.new_stopwatch({})
go f1(ch1, ch2, ch3, sem) go f1(ch1, ch2, ch3, ch4, ch5, sem)
sem.wait() sem.wait()
elapsed_ms := f64(stopwatch.elapsed()) / time.millisecond elapsed_ms := f64(stopwatch.elapsed()) / time.millisecond
assert elapsed_ms >= 295.0 assert elapsed_ms >= 295.0

View File

@ -1,3 +1,11 @@
/*
* ATTENTION! Do not use this file as an example!
* For that, please look at `channel_select_2_test.v` or `channel_select_3_test.v`
*
* This test case uses the implementation in `sync/channels.v` directly
* in order to test it independently from the support in the core language
*/
import sync import sync
fn do_rec_i64(mut ch sync.Channel) { fn do_rec_i64(mut ch sync.Channel) {

View File

@ -3064,6 +3064,48 @@ pub fn (mut c Checker) select_expr(mut node ast.SelectExpr) table.Type {
node.expected_type = c.expected_type node.expected_type = c.expected_type
for branch in node.branches { for branch in node.branches {
c.stmt(branch.stmt) c.stmt(branch.stmt)
match branch.stmt as stmt {
ast.ExprStmt {
if branch.is_timeout {
if !stmt.typ.is_int() {
tsym := c.table.get_type_symbol(stmt.typ)
c.error('invalid type `$tsym.name` for timeout - expected integer type aka `time.Duration`',
stmt.pos)
}
} else {
if stmt.expr is ast.InfixExpr as expr {
if expr.left !is ast.Ident &&
expr.left !is ast.SelectorExpr && expr.left !is ast.IndexExpr {
c.error('channel in `select` key must be predefined', expr.left.position())
}
} else {
c.error('invalid expression for `select` key', stmt.expr.position())
}
}
}
ast.AssignStmt {
match stmt.right[0] as expr {
ast.PrefixExpr {
if expr.right !is ast.Ident &&
expr.right !is ast.SelectorExpr && expr.right !is ast.IndexExpr {
c.error('channel in `select` key must be predefined', expr.right.position())
}
if expr.or_block.kind != .absent {
err_prefix := if expr.or_block.kind == .block { 'or block' } else { 'error propagation' }
c.error('$err_prefix not allowed in `select` key', expr.or_block.pos)
}
}
else {
c.error('`<-` receive expression expected', stmt.right[0].position())
}
}
}
else {
if !branch.is_else {
c.error('receive or send statement expected as `select` key', branch.stmt.position())
}
}
}
c.stmts(branch.stmts) c.stmts(branch.stmts)
} }
return table.bool_type return table.bool_type

View File

@ -2767,11 +2767,23 @@ fn (mut g Gen) select_expr(node ast.SelectExpr) {
} else { } else {
match branch.stmt as stmt { match branch.stmt as stmt {
ast.ExprStmt { ast.ExprStmt {
// send expression
expr := stmt.expr as ast.InfixExpr expr := stmt.expr as ast.InfixExpr
channels << expr.left channels << expr.left
objs << expr.right if expr.right is ast.Ident ||
tmp_objs << '' expr.right is ast.IndexExpr || expr.right is ast.SelectorExpr || expr.right is ast.StructInit {
elem_types << '' // addressable objects in the `C` output
objs << expr.right
tmp_objs << ''
elem_types << ''
} else {
// must be evaluated to tmp var before real `select` is performed
objs << ast.Expr{}
tmp_obj := g.new_tmp_var()
tmp_objs << tmp_obj
el_stype := g.typ(g.table.mktyp(expr.right_type))
g.writeln('$el_stype $tmp_obj;')
}
is_push << true is_push << true
} }
ast.AssignStmt { ast.AssignStmt {
@ -2858,7 +2870,7 @@ fn (mut g Gen) select_expr(node ast.SelectExpr) {
g.writeln('-1) {') g.writeln('-1) {')
} else { } else {
g.writeln('$i) {') g.writeln('$i) {')
if tmp_objs[i] != '' { if !is_push[i] && tmp_objs[i] != '' {
g.write('\t${elem_types[i]}') g.write('\t${elem_types[i]}')
g.expr(objs[i]) g.expr(objs[i])
g.writeln(' = ${tmp_objs[i]};') g.writeln(' = ${tmp_objs[i]};')

View File

@ -358,6 +358,7 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
} }
} else { } else {
p.inside_match = true p.inside_match = true
p.inside_select = true
exprs, comments := p.expr_list() exprs, comments := p.expr_list()
if exprs.len != 1 { if exprs.len != 1 {
p.error('only one expression allowed as `select` key') p.error('only one expression allowed as `select` key')
@ -373,6 +374,7 @@ fn (mut p Parser) select_expr() ast.SelectExpr {
} }
} }
p.inside_match = false p.inside_match = false
p.inside_select = false
match stmt { match stmt {
ast.ExprStmt { ast.ExprStmt {
if !stmt.is_expr { if !stmt.is_expr {

View File

@ -47,6 +47,7 @@ mut:
is_amp bool // for generating the right code for `&Foo{}` is_amp bool // for generating the right code for `&Foo{}`
returns bool returns bool
inside_match bool // to separate `match A { }` from `Struct{}` inside_match bool // to separate `match A { }` from `Struct{}`
inside_select bool // to allow `ch <- Struct{} {` inside `select`
inside_match_case bool // to separate `match_expr { }` from `Struct{}` inside_match_case bool // to separate `match_expr { }` from `Struct{}`
inside_match_body bool // to fix eval not used TODO inside_match_body bool // to fix eval not used TODO
inside_unsafe bool inside_unsafe bool
@ -920,6 +921,7 @@ pub fn (mut p Parser) parse_ident(language table.Language) ast.Ident {
} }
pub fn (mut p Parser) name_expr() ast.Expr { pub fn (mut p Parser) name_expr() ast.Expr {
prev_tok_kind := p.prev_tok.kind
mut node := ast.Expr{} mut node := ast.Expr{}
if p.expecting_type { if p.expecting_type {
p.expecting_type = false p.expecting_type = false
@ -1073,7 +1075,8 @@ pub fn (mut p Parser) name_expr() ast.Expr {
} }
} else if (p.peek_tok.kind == .lcbr || } else if (p.peek_tok.kind == .lcbr ||
(p.peek_tok.kind == .lt && lit0_is_capital)) && (p.peek_tok.kind == .lt && lit0_is_capital)) &&
!p.inside_match && !p.inside_match_case && !p.inside_if && !p.inside_for { // && (p.tok.lit[0].is_capital() || p.builtin_mod) { (!p.inside_match || (p.inside_select && prev_tok_kind == .arrow && lit0_is_capital)) && !p.inside_match_case &&
!p.inside_if && !p.inside_for { // && (p.tok.lit[0].is_capital() || p.builtin_mod) {
return p.struct_init(false) // short_syntax: false return p.struct_init(false) // short_syntax: false
} else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) { } else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) {
// `Color.green` // `Color.green`