diff --git a/_test/select10.go b/_test/select10.go new file mode 100644 index 00000000..63792b4b --- /dev/null +++ b/_test/select10.go @@ -0,0 +1,14 @@ +package main + +func main() { + c := make(chan string) + select { + case <-c: + println("unexpected") + default: + } + println("bye") +} + +// Output: +// bye diff --git a/_test/select11.go b/_test/select11.go new file mode 100644 index 00000000..5239821c --- /dev/null +++ b/_test/select11.go @@ -0,0 +1,16 @@ +package main + +func main() { + c := make(chan string) + select { + case <-c: + println("unexpected") + default: + println("nothing received") + } + println("bye") +} + +// Output: +// nothing received +// bye diff --git a/interp/cfg.go b/interp/cfg.go index fe126739..eff7aedb 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -204,7 +204,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) { case commClause: sc = sc.pushBloc() - if n.child[0].action == aAssign { + if len(n.child) > 0 && n.child[0].action == aAssign { ch := n.child[0].child[1].child[0] var typ *itype if typ, err = nodeType(interp, sc, ch); err != nil { @@ -827,7 +827,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) { case typeSwichAssign(n) && len(n.child) > 1: n.start = n.child[1].start case len(n.child) == 0: - // empty case body: jump to switch node (exit node) + // Empty case body: jump to switch node (exit node). n.start = n.anc.anc.anc default: n.start = n.child[0].start @@ -838,10 +838,14 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) { case commClause: wireChild(n) - if len(n.child) > 1 { - n.start = n.child[1].start // Skip chan operation, performed by select - } else { + switch len(n.child) { + case 0: + sc.pop() + return + case 1: n.start = n.child[0].start // default clause + default: + n.start = n.child[1].start // Skip chan operation, performed by select } n.lastChild().tnext = n.anc.anc // exit node is selectStmt sc = sc.pop() @@ -1344,14 +1348,15 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) { var cur *node for _, c := range n.child[0].child { var an *node // channel init action node - c0 := c.child[0] - switch { - case c0.kind == exprStmt && len(c0.child) == 1 && c0.child[0].action == aRecv: - an = c0.child[0].child[0] - case c0.action == aAssign: - an = c0.lastChild().child[0] - case c0.kind == sendStmt: - an = c0.child[0] + if len(c.child) > 0 { + switch c0 := c.child[0]; { + case c0.kind == exprStmt && len(c0.child) == 1 && c0.child[0].action == aRecv: + an = c0.child[0].child[0] + case c0.action == aAssign: + an = c0.lastChild().child[0] + case c0.kind == sendStmt: + an = c0.child[0] + } } if an != nil { if cur == nil { diff --git a/interp/run.go b/interp/run.go index 1532fdd0..27e7b34b 100644 --- a/interp/run.go +++ b/interp/run.go @@ -2543,33 +2543,40 @@ func _select(n *node) { assignedValues := make([]func(*frame) reflect.Value, nbClause) okValues := make([]func(*frame) reflect.Value, nbClause) cases := make([]reflect.SelectCase, nbClause+1) + next := getExec(n.tnext) for i := 0; i < nbClause; i++ { - switch c0 := n.child[i].child[0]; { - case len(n.child[i].child) > 1: - // The comm clause contains a channel operation and a clause body. - clause[i] = getExec(n.child[i].child[1].start) - chans[i], assigned[i], ok[i], cases[i].Dir = clauseChanDir(n.child[i]) - chanValues[i] = genValue(chans[i]) - if assigned[i] != nil { - assignedValues[i] = genValue(assigned[i]) - } - if ok[i] != nil { - okValues[i] = genValue(ok[i]) - } - case c0.kind == exprStmt && len(c0.child) == 1 && c0.child[0].action == aRecv: - // The comm clause has an empty body clause after channel receive. - chanValues[i] = genValue(c0.child[0].child[0]) - cases[i].Dir = reflect.SelectRecv - case c0.kind == sendStmt: - // The comm clause as an empty body clause after channel send. - chanValues[i] = genValue(c0.child[0]) - cases[i].Dir = reflect.SelectSend - assignedValues[i] = genValue(c0.child[1]) - default: - // The comm clause has a default clause. - clause[i] = getExec(c0.start) + if len(n.child[i].child) == 0 { + // The comm clause is an empty default, exit select. cases[i].Dir = reflect.SelectDefault + clause[i] = func(*frame) bltn { return next } + } else { + switch c0 := n.child[i].child[0]; { + case len(n.child[i].child) > 1: + // The comm clause contains a channel operation and a clause body. + clause[i] = getExec(n.child[i].child[1].start) + chans[i], assigned[i], ok[i], cases[i].Dir = clauseChanDir(n.child[i]) + chanValues[i] = genValue(chans[i]) + if assigned[i] != nil { + assignedValues[i] = genValue(assigned[i]) + } + if ok[i] != nil { + okValues[i] = genValue(ok[i]) + } + case c0.kind == exprStmt && len(c0.child) == 1 && c0.child[0].action == aRecv: + // The comm clause has an empty body clause after channel receive. + chanValues[i] = genValue(c0.child[0].child[0]) + cases[i].Dir = reflect.SelectRecv + case c0.kind == sendStmt: + // The comm clause as an empty body clause after channel send. + chanValues[i] = genValue(c0.child[0]) + cases[i].Dir = reflect.SelectSend + assignedValues[i] = genValue(c0.child[1]) + default: + // The comm clause has a default clause. + clause[i] = getExec(c0.start) + cases[i].Dir = reflect.SelectDefault + } } }