all: enable `x = <-ch or {...}` (#6195)

pull/6197/head
Uwe Krüger 2020-08-23 02:12:05 +02:00 committed by GitHub
parent 7dfae2384b
commit 61df70fdf5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 126 additions and 14 deletions

View File

@ -15,13 +15,12 @@ mut:
// this function gets an array of channels for `St` references // this function gets an array of channels for `St` references
fn do_rec_calc_send(chs []chan mut St) { fn do_rec_calc_send(chs []chan mut St) {
mut s := &St(0)
for { for {
if !(&sync.Channel(chs[0])).pop(&s) { mut s := <-chs[0] or {
break break
} }
s.n++ s.n++
(&sync.Channel(chs[1])).push(&s) chs[1] <- s
} }
} }
@ -32,8 +31,8 @@ fn test_channel_array_mut() {
n: 100 n: 100
} }
for _ in 0 .. num_iterations { for _ in 0 .. num_iterations {
(&sync.Channel(chs[0])).push(&t) chs[0] <- t
(&sync.Channel(chs[1])).pop(&t) t = <-chs[1]
} }
(&sync.Channel(chs[0])).close() (&sync.Channel(chs[0])).close()
assert t.n == 100 + num_iterations assert t.n == 100 + num_iterations

View File

@ -0,0 +1,39 @@
import sync
const (
num_iterations = 10000
)
fn get_val_from_chan(ch chan i64) ?i64 {
r := <-ch?
return r
}
// this function gets an array of channels for `i64`
fn do_rec_calc_send(chs []chan i64, sem sync.Semaphore) {
mut msg := ''
for {
mut s := get_val_from_chan(chs[0]) or {
msg = err.str()
break
}
s++
chs[1] <- s
}
assert msg == 'channel closed'
sem.post()
}
fn test_channel_array_mut() {
mut chs := [chan i64{}, chan i64{cap: 10}]
sem := sync.new_semaphore()
go do_rec_calc_send(chs, sem)
mut t := i64(100)
for _ in 0 .. num_iterations {
chs[0] <- t
t = <-chs[1]
}
(&sync.Channel(chs[0])).close()
sem.wait()
assert t == 100 + num_iterations
}

View File

@ -440,6 +440,7 @@ pub:
pos token.Position pos token.Position
pub mut: pub mut:
right_type table.Type right_type table.Type
or_block OrExpr
} }
pub struct IndexExpr { pub struct IndexExpr {

View File

@ -2465,6 +2465,7 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
if node.op == .arrow { if node.op == .arrow {
right := c.table.get_type_symbol(right_type) right := c.table.get_type_symbol(right_type)
if right.kind == .chan { if right.kind == .chan {
c.stmts(node.or_block.stmts)
return right.chan_info().elem_type return right.chan_info().elem_type
} else { } else {
c.error('<- operator can only be used with `chan` types', node.pos) c.error('<- operator can only be used with `chan` types', node.pos)

View File

@ -65,6 +65,7 @@ mut:
is_vlines_enabled bool // is it safe to generate #line directives when -g is passed is_vlines_enabled bool // is it safe to generate #line directives when -g is passed
vlines_path string // set to the proper path for generating #line directives vlines_path string // set to the proper path for generating #line directives
optionals []string // to avoid duplicates TODO perf, use map optionals []string // to avoid duplicates TODO perf, use map
chan_pop_optionals []string // types for `x := <-ch or {...}`
shareds []int // types with hidden mutex for which decl has been emitted shareds []int // types with hidden mutex for which decl has been emitted
inside_ternary int // ?: comma separated statements on a single line inside_ternary int // ?: comma separated statements on a single line
inside_map_postfix bool // inside map++/-- postfix expr inside_map_postfix bool // inside map++/-- postfix expr
@ -460,6 +461,22 @@ fn (mut g Gen) find_or_register_shared(t table.Type, base string) string {
return sh_typ return sh_typ
} }
fn (mut g Gen) register_chan_pop_optional_call(opt_el_type, styp string) {
if opt_el_type !in g.chan_pop_optionals {
g.chan_pop_optionals << opt_el_type
g.channel_definitions.writeln('
static inline $opt_el_type __Option_${styp}_popval($styp ch) {
$opt_el_type _tmp;
if (sync__Channel_try_pop_priv(ch, _tmp.data, false)) {
Option _tmp2 = v_error(tos_lit("channel closed"));
return *($opt_el_type*)&_tmp2;
}
_tmp.ok = true; _tmp.is_none = false; _tmp.v_error = (string){.str=(byteptr)""}; _tmp.ecode = 0;
return _tmp;
}')
}
}
// cc_type returns the Cleaned Concrete Type name, *without ptr*, // cc_type returns the Cleaned Concrete Type name, *without ptr*,
// i.e. it's always just Cat, not Cat_ptr: // i.e. it's always just Cat, not Cat_ptr:
fn (g &Gen) cc_type(t table.Type) string { fn (g &Gen) cc_type(t table.Type) string {
@ -519,7 +536,8 @@ typedef struct {
if typ.name != 'chan' { if typ.name != 'chan' {
styp := util.no_dots(typ.name) styp := util.no_dots(typ.name)
g.type_definitions.writeln('typedef chan $styp;') g.type_definitions.writeln('typedef chan $styp;')
el_stype := g.typ(typ.chan_info().elem_type) chan_inf := typ.chan_info()
el_stype := g.typ(chan_inf.elem_type)
g.channel_definitions.writeln(' g.channel_definitions.writeln('
static inline $el_stype __${styp}_popval($styp ch) { static inline $el_stype __${styp}_popval($styp ch) {
$el_stype val; $el_stype val;
@ -1997,22 +2015,45 @@ fn (mut g Gen) expr(node ast.Expr) {
} }
} }
ast.PrefixExpr { ast.PrefixExpr {
gen_or := node.op == .arrow && node.or_block.kind != .absent
if node.op == .amp { if node.op == .amp {
g.is_amp = true g.is_amp = true
} }
if node.op == .arrow { if node.op == .arrow {
right_type := g.unwrap_generic(node.right_type) styp := g.typ(node.right_type)
right_sym := g.table.get_type_symbol(right_type) right_sym := g.table.get_type_symbol(node.right_type)
styp := util.no_dots(right_sym.name) mut right_inf := right_sym.info as table.Chan
g.write('__${styp}_popval(') elem_type := right_inf.elem_type
is_gen_or_and_assign_rhs := gen_or && g.is_assign_rhs
cur_line := if is_gen_or_and_assign_rhs {
line := g.go_before_stmt(0)
g.out.write(tabs[g.indent])
line
} else {
''
}
tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
if gen_or {
opt_elem_type := g.typ(elem_type.set_flag(.optional))
g.register_chan_pop_optional_call(opt_elem_type, styp)
g.write('$opt_elem_type $tmp_opt = __Option_${styp}_popval(')
} else {
g.write('__${styp}_popval(')
}
g.expr(node.right)
g.write(')')
if gen_or {
g.or_block(tmp_opt, node.or_block, elem_type)
if is_gen_or_and_assign_rhs {
elem_styp := g.typ(elem_type)
g.write('\n$cur_line*($elem_styp*)${tmp_opt}.data')
}
}
} else { } else {
// g.write('/*pref*/') // g.write('/*pref*/')
g.write(node.op.str()) g.write(node.op.str())
// g.write('(') // g.write('(')
} g.expr(node.right)
g.expr(node.right)
if node.op == .arrow {
g.write(')')
} }
g.is_amp = false g.is_amp = false
} }

View File

@ -314,9 +314,40 @@ fn (mut p Parser) prefix_expr() ast.PrefixExpr {
if mut right is ast.CastExpr { if mut right is ast.CastExpr {
right.in_prexpr = true right.in_prexpr = true
} }
mut or_stmts := []ast.Stmt{}
mut or_kind := ast.OrKind.absent
// allow `x := <-ch or {...}` to handle closed channel
if op == .arrow && p.tok.kind == .key_orelse {
p.next()
p.open_scope()
p.scope.register('errcode', ast.Var{
name: 'errcode'
typ: table.int_type
pos: p.tok.position()
is_used: true
})
p.scope.register('err', ast.Var{
name: 'err'
typ: table.string_type
pos: p.tok.position()
is_used: true
})
or_kind = .block
or_stmts = p.parse_block_no_scope(false)
p.close_scope()
}
if p.tok.kind == .question {
p.next()
or_kind = .propagate
}
return ast.PrefixExpr{ return ast.PrefixExpr{
op: op op: op
right: right right: right
pos: pos pos: pos
or_block: ast.OrExpr{
stmts: or_stmts
kind: or_kind
pos: pos
}
} }
} }