fix: correct control flow graph for select blocks
This commit is contained in:
20
_test/chan9.go
Normal file
20
_test/chan9.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
type Channel chan string
|
||||
|
||||
type T struct {
|
||||
Channel
|
||||
}
|
||||
|
||||
func send(c Channel) { c <- "ping" }
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
t.Channel = make(Channel)
|
||||
go send(t.Channel)
|
||||
msg := <-t.Channel
|
||||
println(msg)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// ping
|
||||
17
_test/select4.go
Normal file
17
_test/select4.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
c1 := make(chan string)
|
||||
|
||||
go func() { c1 <- "done" }()
|
||||
|
||||
select {
|
||||
case msg1 := <-c1:
|
||||
println("received from c1:", msg1)
|
||||
}
|
||||
println("Bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// received from c1: done
|
||||
// Bye
|
||||
22
_test/select5.go
Normal file
22
_test/select5.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
c1 chan string
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
t.c1 = make(chan string)
|
||||
|
||||
go func(c chan string) { c <- "done" }(t.c1)
|
||||
|
||||
select {
|
||||
case msg1 := <-t.c1:
|
||||
println("received from c1:", msg1)
|
||||
}
|
||||
println("Bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// received from c1: done
|
||||
// Bye
|
||||
22
_test/select6.go
Normal file
22
_test/select6.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
c1 chan string
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
t.c1 = make(chan string)
|
||||
|
||||
go func(c chan string) { c <- "done" }(t.c1)
|
||||
|
||||
select {
|
||||
case <-t.c1:
|
||||
println("received from c1")
|
||||
}
|
||||
println("Bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// received from c1
|
||||
// Bye
|
||||
24
_test/select7.go
Normal file
24
_test/select7.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
c1 chan string
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
t.c1 = make(chan string)
|
||||
a := 0
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case t.c1 <- "done":
|
||||
a++
|
||||
}
|
||||
}()
|
||||
|
||||
msg1 := <-t.c1
|
||||
println("received from c1:", msg1)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// received from c1: done
|
||||
24
_test/select8.go
Normal file
24
_test/select8.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
c1 chan string
|
||||
c2 chan string
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
t.c1 = make(chan string)
|
||||
|
||||
go func(c chan string) { c <- "done" }(t.c1)
|
||||
|
||||
select {
|
||||
case msg := <-t.c1:
|
||||
println("received from c1:", msg)
|
||||
case <-t.c2:
|
||||
}
|
||||
println("Bye")
|
||||
}
|
||||
|
||||
// Output:
|
||||
// received from c1: done
|
||||
// Bye
|
||||
22
_test/select9.go
Normal file
22
_test/select9.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
c1 chan string
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := &T{}
|
||||
t.c1 = make(chan string)
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case t.c1 <- "done":
|
||||
}
|
||||
}()
|
||||
|
||||
msg1 := <-t.c1
|
||||
println("received from c1:", msg1)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// received from c1: done
|
||||
@@ -206,13 +206,20 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
sc = sc.pushBloc()
|
||||
if n.child[0].action == aAssign {
|
||||
ch := n.child[0].child[1].child[0]
|
||||
if sym, _, ok := sc.lookup(ch.ident); ok {
|
||||
assigned := n.child[0].child[0]
|
||||
index := sc.add(sym.typ.val)
|
||||
sc.sym[assigned.ident] = &symbol{index: index, kind: varSym, typ: sym.typ.val}
|
||||
assigned.findex = index
|
||||
assigned.typ = sym.typ.val
|
||||
var typ *itype
|
||||
if typ, err = nodeType(interp, sc, ch); err != nil {
|
||||
return false
|
||||
}
|
||||
if !isChan(typ) {
|
||||
err = n.cfgErrorf("invalid operation: receive from non-chan type")
|
||||
return false
|
||||
}
|
||||
elem := chanElement(typ)
|
||||
assigned := n.child[0].child[0]
|
||||
index := sc.add(elem)
|
||||
sc.sym[assigned.ident] = &symbol{index: index, kind: varSym, typ: elem}
|
||||
assigned.findex = index
|
||||
assigned.typ = elem
|
||||
}
|
||||
|
||||
case compositeLitExpr:
|
||||
@@ -831,7 +838,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
} else {
|
||||
n.start = n.child[0].start // default clause
|
||||
}
|
||||
n.lastChild().tnext = n.anc.anc // exit node is SelectStmt
|
||||
n.lastChild().tnext = n.anc.anc // exit node is selectStmt
|
||||
sc = sc.pop()
|
||||
|
||||
case compositeLitExpr:
|
||||
@@ -1329,9 +1336,40 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
|
||||
|
||||
case selectStmt:
|
||||
wireChild(n)
|
||||
// Move action to block statement, so select node can be an exit point
|
||||
// Move action to block statement, so select node can be an exit point.
|
||||
n.child[0].gen = _select
|
||||
n.start = n.child[0]
|
||||
// Chain channel init actions in commClauses prior to invoke select.
|
||||
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 an != nil {
|
||||
if cur == nil {
|
||||
// First channel init action, the entry point for the select block.
|
||||
n.start = an.start
|
||||
} else {
|
||||
// Chain channel init action to the previous one.
|
||||
cur.tnext = an.start
|
||||
}
|
||||
}
|
||||
cur = an
|
||||
}
|
||||
// Invoke select action
|
||||
if cur == nil {
|
||||
// There is no channel init action, call select directly.
|
||||
n.start = n.child[0]
|
||||
} else {
|
||||
// Select is called after the last channel init action.
|
||||
cur.tnext = n.child[0]
|
||||
}
|
||||
|
||||
case starExpr:
|
||||
switch {
|
||||
|
||||
@@ -2499,7 +2499,9 @@ func _select(n *node) {
|
||||
cases := make([]reflect.SelectCase, nbClause+1)
|
||||
|
||||
for i := 0; i < nbClause; i++ {
|
||||
if len(n.child[i].child) > 1 {
|
||||
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])
|
||||
@@ -2509,8 +2511,18 @@ func _select(n *node) {
|
||||
if ok[i] != nil {
|
||||
okValues[i] = genValue(ok[i])
|
||||
}
|
||||
} else {
|
||||
clause[i] = getExec(n.child[i].child[0].start)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1157,6 +1157,24 @@ func isShiftNode(n *node) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// chanElement returns the channel element type.
|
||||
func chanElement(t *itype) *itype {
|
||||
switch t.cat {
|
||||
case aliasT:
|
||||
return chanElement(t.val)
|
||||
case chanT:
|
||||
return t.val
|
||||
case valueT:
|
||||
return &itype{cat: valueT, rtype: t.rtype.Elem(), node: t.node, scope: t.scope}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isChan returns true if type is of channel kind.
|
||||
func isChan(t *itype) bool {
|
||||
return t.TypeOf().Kind() == reflect.Chan
|
||||
}
|
||||
|
||||
func isInterfaceSrc(t *itype) bool {
|
||||
return t.cat == interfaceT || (t.cat == aliasT && isInterfaceSrc(t.val))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user