Skip to content

Commit

Permalink
fix: handle default comm clause in select
Browse files Browse the repository at this point in the history
  • Loading branch information
mvertes committed Apr 25, 2020
1 parent 92eebba commit 01e2e46
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 37 deletions.
14 changes: 14 additions & 0 deletions _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
16 changes: 16 additions & 0 deletions _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
31 changes: 18 additions & 13 deletions interp/cfg.go
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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()
Expand Down Expand Up @@ -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 {
Expand Down
55 changes: 31 additions & 24 deletions interp/run.go
Expand Up @@ -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
}
}
}

Expand Down

0 comments on commit 01e2e46

Please sign in to comment.