Handle Receive and Send channels
This commit is contained in:
12
_test/chan10.go
Normal file
12
_test/chan10.go
Normal 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
11
_test/range9.go
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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()}}
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user