From 4ae88c69ac12a9e0e49602d9abfc8095a7a00f73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kr=C3=BCger?= <45282134+UweKrueger@users.noreply.github.com> Date: Sun, 20 Sep 2020 03:50:09 +0200 Subject: [PATCH] sync/select: allow push of literals and calculated expressions (#6429) --- vlib/sync/channel_select_2_test.v | 19 ++++---------- vlib/sync/channel_select_3_test.v | 24 ++++++++++++++---- vlib/sync/channel_select_test.v | 8 ++++++ vlib/v/checker/checker.v | 42 +++++++++++++++++++++++++++++++ vlib/v/gen/cgen.v | 20 ++++++++++++--- vlib/v/parser/if_match.v | 2 ++ vlib/v/parser/parser.v | 5 +++- 7 files changed, 96 insertions(+), 24 deletions(-) diff --git a/vlib/sync/channel_select_2_test.v b/vlib/sync/channel_select_2_test.v index 1a4f3a216a..055b49abbc 100644 --- a/vlib/sync/channel_select_2_test.v +++ b/vlib/sync/channel_select_2_test.v @@ -36,32 +36,23 @@ fn test_select() { go do_send_int(chi) go do_send_byte(chb) 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 rl := i64(0) - mut ri := int(0) - mut rb := byte(0) mut sl := i64(0) - mut objs := [voidptr(&ri), &sl, &rl, &rb] for _ in 0 .. 1200 { - idx := sync.channel_select(mut channels, directions, mut objs, -1) - match idx { - 0 { + select { + ri := <-chi { sum += ri } - 1 { + recch <- sl { sl++ } - 2 { + rl = <-chl { sum += rl } - 3 { + rb := <-chb { sum += rb } - else { - println('got $idx (timeout)') - } } } // Use Gauß' formula for the first 2 contributions diff --git a/vlib/sync/channel_select_3_test.v b/vlib/sync/channel_select_3_test.v index b679cb9fd7..70bc0b2857 100644 --- a/vlib/sync/channel_select_3_test.v +++ b/vlib/sync/channel_select_3_test.v @@ -5,9 +5,12 @@ struct St { 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 - st := St{} select { a = <-ch3 { a = 0 @@ -15,9 +18,18 @@ fn f1(ch1 chan int, ch2 chan St, ch3 chan int, sem sync.Semaphore) { b := <-ch2 { a = b.a } - ch2 <- st { + ch3 <- 5 { + a = 1 + } + ch2 <- St{a: 37} { a = 2 } + ch4 <- (6 + 7 * 9) { + a = 8 + } + ch5 <- getint() { + a = 9 + } > 300 * time.millisecond { a = 3 } @@ -30,7 +42,7 @@ fn f2(ch1 chan St, ch2 chan int, sem sync.Semaphore) { mut r := 23 for i in 0 .. 2 { select { - b := <- ch1 { + b := <-ch1 { r = b.a } ch2 <- r { @@ -50,6 +62,8 @@ fn test_select_blocks() { ch1 := chan int{cap: 1} ch2 := chan St{} ch3 := chan int{} + ch4 := chan int{} + ch5 := chan int{} sem := sync.new_semaphore() mut r := false t := select { @@ -69,7 +83,7 @@ fn test_select_blocks() { ch2 <- St{a: 13} sem.wait() stopwatch := time.new_stopwatch({}) - go f1(ch1, ch2, ch3, sem) + go f1(ch1, ch2, ch3, ch4, ch5, sem) sem.wait() elapsed_ms := f64(stopwatch.elapsed()) / time.millisecond assert elapsed_ms >= 295.0 diff --git a/vlib/sync/channel_select_test.v b/vlib/sync/channel_select_test.v index 3131bc5658..2d99cd2a05 100644 --- a/vlib/sync/channel_select_test.v +++ b/vlib/sync/channel_select_test.v @@ -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 fn do_rec_i64(mut ch sync.Channel) { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 40860e0bbf..906d34592e 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3064,6 +3064,48 @@ pub fn (mut c Checker) select_expr(mut node ast.SelectExpr) table.Type { node.expected_type = c.expected_type for branch in node.branches { 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) } return table.bool_type diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index e9160a965e..7a63cb6ce6 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -2767,11 +2767,23 @@ fn (mut g Gen) select_expr(node ast.SelectExpr) { } else { match branch.stmt as stmt { ast.ExprStmt { + // send expression expr := stmt.expr as ast.InfixExpr channels << expr.left - objs << expr.right - tmp_objs << '' - elem_types << '' + if expr.right is ast.Ident || + expr.right is ast.IndexExpr || expr.right is ast.SelectorExpr || expr.right is ast.StructInit { + // 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 } ast.AssignStmt { @@ -2858,7 +2870,7 @@ fn (mut g Gen) select_expr(node ast.SelectExpr) { g.writeln('-1) {') } else { g.writeln('$i) {') - if tmp_objs[i] != '' { + if !is_push[i] && tmp_objs[i] != '' { g.write('\t${elem_types[i]}') g.expr(objs[i]) g.writeln(' = ${tmp_objs[i]};') diff --git a/vlib/v/parser/if_match.v b/vlib/v/parser/if_match.v index ca51bc0d27..d635c9cf54 100644 --- a/vlib/v/parser/if_match.v +++ b/vlib/v/parser/if_match.v @@ -358,6 +358,7 @@ fn (mut p Parser) select_expr() ast.SelectExpr { } } else { p.inside_match = true + p.inside_select = true exprs, comments := p.expr_list() if exprs.len != 1 { 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_select = false match stmt { ast.ExprStmt { if !stmt.is_expr { diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 7775adc0c5..f964b805fa 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -47,6 +47,7 @@ mut: is_amp bool // for generating the right code for `&Foo{}` returns bool 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_body bool // to fix eval not used TODO 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 { + prev_tok_kind := p.prev_tok.kind mut node := ast.Expr{} if p.expecting_type { p.expecting_type = false @@ -1073,7 +1075,8 @@ pub fn (mut p Parser) name_expr() ast.Expr { } } else if (p.peek_tok.kind == .lcbr || (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 } else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) { // `Color.green`