Handle Receive and Send channels

This commit is contained in:
Julien Levesy
2020-06-23 09:04:04 +02:00
committed by GitHub
parent c2ad279643
commit c11d361953
7 changed files with 78 additions and 13 deletions

12
_test/chan10.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "time"
func main() {
var tick <-chan time.Time = time.Tick(time.Millisecond)
_ = tick
println("success")
}
// Output:
// success

11
_test/range9.go Normal file
View File

@@ -0,0 +1,11 @@
package main
func main() {
var c chan<- struct{} = make(chan struct{})
for _ = range c {
}
}
// Error:
// _test/range9.go:6:16: invalid operation: range c receive from send-only channel

View File

@@ -31,6 +31,8 @@ const (
caseBody
caseClause
chanType
chanTypeSend
chanTypeRecv
commClause
commClauseDefault
compositeLitExpr
@@ -109,6 +111,8 @@ var kinds = [...]string{
caseBody: "caseBody",
caseClause: "caseClause",
chanType: "chanType",
chanTypeSend: "chanTypeSend",
chanTypeRecv: "chanTypeRecv",
commClause: "commClause",
commClauseDefault: "commClauseDefault",
compositeLitExpr: "compositeLitExpr",
@@ -565,7 +569,14 @@ func (interp *Interpreter) ast(src, name string) (string, *node, error) {
st.push(addChild(&root, anc, pos, caseClause, aCase), nod)
case *ast.ChanType:
st.push(addChild(&root, anc, pos, chanType, aNop), nod)
switch a.Dir {
case ast.SEND | ast.RECV:
st.push(addChild(&root, anc, pos, chanType, aNop), nod)
case ast.SEND:
st.push(addChild(&root, anc, pos, chanTypeSend, aNop), nod)
case ast.RECV:
st.push(addChild(&root, anc, pos, chanTypeRecv, aNop), nod)
}
case *ast.CommClause:
kind := commClause

View File

@@ -69,6 +69,12 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
// type of array like value is not yet known. This could be fixed in ast structure
// by setting array/map node as 1st child of ForRangeStmt instead of 3rd child of
// RangeStmt. The following workaround is less elegant but ok.
c := n.anc.child[1]
if c != nil && c.typ != nil && isSendChan(c.typ) {
err = c.cfgErrorf("invalid operation: range %s receive from send-only channel", c.ident)
return false
}
if t := sc.rangeChanType(n.anc); t != nil {
// range over channel
e := n.anc.child[0]
@@ -402,7 +408,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) {
}
}
case arrayType, basicLit, chanType, funcType, interfaceType, mapType, structType:
case arrayType, basicLit, chanType, chanTypeRecv, chanTypeSend, funcType, interfaceType, mapType, structType:
n.typ, err = nodeType(interp, sc, n)
return false
}
@@ -1892,7 +1898,7 @@ func isBinType(v reflect.Value) bool { return v.IsValid() && v.Kind() == reflect
// isType returns true if node refers to a type definition, false otherwise.
func (n *node) isType(sc *scope) bool {
switch n.kind {
case arrayType, chanType, funcType, interfaceType, mapType, structType:
case arrayType, chanType, chanTypeRecv, chanTypeSend, funcType, interfaceType, mapType, structType:
return true
case parenExpr, starExpr:
if len(n.child) == 1 {
@@ -1920,7 +1926,7 @@ func wireChild(n *node) {
// Set start node, in subtree (propagated to ancestors by post-order processing)
for _, child := range n.child {
switch child.kind {
case arrayType, chanType, funcDecl, importDecl, mapType, basicLit, identExpr, typeDecl:
case arrayType, chanType, chanTypeRecv, chanTypeSend, funcDecl, importDecl, mapType, basicLit, identExpr, typeDecl:
continue
default:
n.start = child.start
@@ -1946,7 +1952,7 @@ func wireChild(n *node) {
// Chain subtree next to self
for i := len(n.child) - 1; i >= 0; i-- {
switch n.child[i].kind {
case arrayType, chanType, importDecl, mapType, funcDecl, basicLit, identExpr, typeDecl:
case arrayType, chanType, chanTypeRecv, chanTypeSend, importDecl, mapType, funcDecl, basicLit, identExpr, typeDecl:
continue
case breakStmt, continueStmt, gotoStmt, returnStmt:
// tnext is already computed, no change

View File

@@ -80,7 +80,8 @@ func TestInterpConsistencyBuild(t *testing.T) {
file.Name() == "server1a.go" || // syntax parsing
file.Name() == "server1.go" || // syntax parsing
file.Name() == "server0.go" || // syntax parsing
file.Name() == "server.go" { // syntax parsing
file.Name() == "server.go" || // syntax parsing
file.Name() == "range9.go" { // expect error
continue
}

View File

@@ -134,7 +134,7 @@ func (s *scope) lookup(ident string) (*symbol, int, bool) {
func (s *scope) rangeChanType(n *node) *itype {
if sym, _, found := s.lookup(n.child[1].ident); found {
if t := sym.typ; len(n.child) == 3 && t != nil && t.cat == chanT {
if t := sym.typ; len(n.child) == 3 && t != nil && (t.cat == chanT || t.cat == chanRecvT) {
return t
}
}
@@ -144,7 +144,7 @@ func (s *scope) rangeChanType(n *node) *itype {
return nil
}
switch {
case c.typ.cat == chanT:
case c.typ.cat == chanT, c.typ.cat == chanRecvT:
return c.typ
case c.typ.cat == valueT && c.typ.rtype.Kind() == reflect.Chan:
return &itype{cat: chanT, val: &itype{cat: valueT, rtype: c.typ.rtype.Elem()}}

View File

@@ -21,6 +21,8 @@ const (
boolT
builtinT
chanT
chanSendT
chanRecvT
complex64T
complex128T
errorT
@@ -105,7 +107,7 @@ type itype struct {
cat tcat // Type category
field []structField // Array of struct fields if structT or interfaceT
key *itype // Type of key element if MapT or nil
val *itype // Type of value element if chanT, mapT, ptrT, aliasT, arrayT or variadicT
val *itype // Type of value element if chanT,chanSendT, chanRecvT, mapT, ptrT, aliasT, arrayT or variadicT
arg []*itype // Argument types if funcT or nil
ret []*itype // Return types if funcT or nil
method []*node // Associated methods or nil
@@ -383,6 +385,20 @@ func nodeType(interp *Interpreter, sc *scope, n *node) (*itype, error) {
}
t.incomplete = t.val.incomplete
case chanTypeRecv:
t.cat = chanRecvT
if t.val, err = nodeType(interp, sc, n.child[0]); err != nil {
return nil, err
}
t.incomplete = t.val.incomplete
case chanTypeSend:
t.cat = chanSendT
if t.val, err = nodeType(interp, sc, n.child[0]); err != nil {
return nil, err
}
t.incomplete = t.val.incomplete
case ellipsisExpr:
t.cat = variadicT
if t.val, err = nodeType(interp, sc, n.child[0]); err != nil {
@@ -738,7 +754,7 @@ func (t *itype) referTo(name string, seen map[*itype]bool) bool {
}
seen[t] = true
switch t.cat {
case aliasT, arrayT, chanT, ptrT:
case aliasT, arrayT, chanT, chanRecvT, chanSendT, ptrT:
return t.val.referTo(name, seen)
case funcT:
for _, a := range t.arg {
@@ -814,7 +830,7 @@ func isComplete(t *itype, visited map[string]bool) bool {
visited[name] = true
}
switch t.cat {
case aliasT, arrayT, chanT, ptrT:
case aliasT, arrayT, chanT, chanRecvT, chanSendT, ptrT:
return isComplete(t.val, visited)
case funcT:
complete := true
@@ -1175,6 +1191,10 @@ func (t *itype) refType(defined map[string]*itype, wrapRecursive bool) reflect.T
}
case chanT:
t.rtype = reflect.ChanOf(reflect.BothDir, t.val.refType(defined, wrapRecursive))
case chanRecvT:
t.rtype = reflect.ChanOf(reflect.RecvDir, t.val.refType(defined, wrapRecursive))
case chanSendT:
t.rtype = reflect.ChanOf(reflect.SendDir, t.val.refType(defined, wrapRecursive))
case errorT:
t.rtype = reflect.TypeOf(new(error)).Elem()
case funcT:
@@ -1290,7 +1310,7 @@ func chanElement(t *itype) *itype {
switch t.cat {
case aliasT:
return chanElement(t.val)
case chanT:
case chanT, chanSendT, chanRecvT:
return t.val
case valueT:
return &itype{cat: valueT, rtype: t.rtype.Elem(), node: t.node, scope: t.scope}
@@ -1300,7 +1320,11 @@ func chanElement(t *itype) *itype {
func isBool(t *itype) bool { return t.TypeOf().Kind() == reflect.Bool }
func isChan(t *itype) bool { return t.TypeOf().Kind() == reflect.Chan }
func isMap(t *itype) bool { return t.TypeOf().Kind() == reflect.Map }
func isSendChan(t *itype) bool {
rt := t.TypeOf()
return rt.Kind() == reflect.Chan && rt.ChanDir() == reflect.SendDir
}
func isMap(t *itype) bool { return t.TypeOf().Kind() == reflect.Map }
func isInterfaceSrc(t *itype) bool {
return t.cat == interfaceT || (t.cat == aliasT && isInterfaceSrc(t.val))