go.tools/go/types: use types.ChanDir instead of ast.ChanDir

Clearer code and fewer dependencies on go/ast.

R=adonovan
CC=golang-dev
https://golang.org/cl/43630043
This commit is contained in:
Robert Griesemer 2013-12-17 15:45:01 -08:00
parent 1de4f6df60
commit 74d33a9c33
15 changed files with 67 additions and 52 deletions

View File

@ -10,7 +10,6 @@ import (
"bufio"
"errors"
"fmt"
"go/ast"
"go/build"
"go/token"
"io"
@ -601,17 +600,17 @@ func (p *parser) parseInterfaceType() types.Type {
// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
//
func (p *parser) parseChanType() types.Type {
dir := ast.SEND | ast.RECV
dir := types.SendRecv
if p.tok == scanner.Ident {
p.expectKeyword("chan")
if p.tok == '<' {
p.expectSpecial("<-")
dir = ast.SEND
dir = types.SendOnly
}
} else {
p.expectSpecial("<-")
p.expectKeyword("chan")
dir = ast.RECV
dir = types.RecvOnly
}
elem := p.parseType()
return types.NewChan(dir, elem)

View File

@ -12,7 +12,6 @@ package importer
import (
"encoding/binary"
"fmt"
"go/ast"
"go/token"
"code.google.com/p/go.tools/go/exact"
@ -251,7 +250,7 @@ func (p *importer) typ() types.Type {
t := new(types.Chan)
p.record(t)
*t = *types.NewChan(ast.ChanDir(p.int()), p.typ())
*t = *types.NewChan(types.ChanDir(p.int()), p.typ())
return t
case _Named:

View File

@ -175,7 +175,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
check.invalidArg(x.pos(), "%s is not a channel", x)
return
}
if c.dir&ast.SEND == 0 {
if c.dir == RecvOnly {
check.invalidArg(x.pos(), "%s must not be a receive-only channel", x)
return
}

View File

@ -102,7 +102,7 @@ func (check *checker) unary(x *operand, op token.Token) {
x.mode = invalid
return
}
if typ.dir&ast.RECV == 0 {
if typ.dir == SendOnly {
check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x)
x.mode = invalid
return

View File

@ -231,7 +231,7 @@ func (x *operand) isAssignableTo(conf *Config, T Type) bool {
// x is a bidirectional channel value, T is a channel
// type, x's type V and T have identical element types,
// and at least one of V or T is not a named type
if Vc, ok := Vu.(*Chan); ok && Vc.dir == ast.SEND|ast.RECV {
if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
if Tc, ok := Tu.(*Chan); ok && IsIdentical(Vc.elem, Tc.elem) {
return !isNamed(V) || !isNamed(T)
}

View File

@ -202,7 +202,7 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
if ch.mode == invalid || x.mode == invalid {
return
}
if tch, ok := ch.typ.Underlying().(*Chan); !ok || tch.dir&ast.SEND == 0 || !check.assignment(&x, tch.elem) {
if tch, ok := ch.typ.Underlying().(*Chan); !ok || tch.dir == RecvOnly || !check.assignment(&x, tch.elem) {
if x.mode != invalid {
check.invalidOp(ch.pos(), "cannot send %s to channel %s", &x, &ch)
}
@ -571,7 +571,7 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
case *Chan:
key = typ.elem
val = Typ[Invalid]
if typ.dir&ast.RECV == 0 {
if typ.dir == SendOnly {
check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
// ok to continue
}

View File

@ -4,10 +4,7 @@
package types
import (
"go/ast"
"sort"
)
import "sort"
// TODO(gri) Revisit factory functions - make sure they have all relevant parameters.
@ -322,17 +319,27 @@ func (m *Map) Elem() Type { return m.elem }
// A Chan represents a channel type.
type Chan struct {
dir ast.ChanDir
dir ChanDir
elem Type
}
// A ChanDir value indicates a channel direction.
type ChanDir int
// The direction of a channel is indicated by one of the following constants.
const (
SendRecv ChanDir = iota
SendOnly
RecvOnly
)
// NewChan returns a new channel type for the given direction and element type.
func NewChan(dir ast.ChanDir, elem Type) *Chan {
func NewChan(dir ChanDir, elem Type) *Chan {
return &Chan{dir, elem}
}
// Dir returns the direction of channel c.
func (c *Chan) Dir() ast.ChanDir { return c.dir }
func (c *Chan) Dir() ChanDir { return c.dir }
// Elem returns the element type of channel c.
func (c *Chan) Elem() Type { return c.elem }

View File

@ -9,7 +9,6 @@ package types
import (
"bytes"
"fmt"
"go/ast"
"sort"
)
@ -122,16 +121,18 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
var s string
var parens bool
switch t.dir {
case ast.SEND:
s = "chan<- "
case ast.RECV:
s = "<-chan "
default:
case SendRecv:
s = "chan "
// chan (<-chan T) requires parentheses
if c, _ := t.elem.(*Chan); c != nil && c.dir == ast.RECV {
if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
parens = true
}
case SendOnly:
s = "chan<- "
case RecvOnly:
s = "<-chan "
default:
panic("unreachable")
}
buf.WriteString(s)
if parens {

View File

@ -337,7 +337,19 @@ func (check *checker) typInternal(e ast.Expr, def *Named, cycleOk bool) Type {
def.underlying = typ
}
typ.dir = e.Dir
dir := SendRecv
switch e.Dir {
case ast.SEND | ast.RECV:
// nothing to do
case ast.SEND:
dir = SendOnly
case ast.RECV:
dir = RecvOnly
default:
check.invalidAST(e.Pos(), "unknown channel direction %d", e.Dir)
// ok to continue
}
typ.dir = dir
typ.elem = check.typ(e.Value, nil, true)
return typ

View File

@ -89,7 +89,7 @@ func peers(o *Oracle, qpos *QueryPos) (queryResult, error) {
for _, op := range ops {
for _, ptr := range ptares.Queries[op.ch] {
if ptr.PointsTo().Intersects(queryChanPts) {
if op.dir == ast.SEND {
if op.dir == types.SendOnly {
sends = append(sends, op.pos)
} else {
receives = append(receives, op.pos)
@ -129,7 +129,7 @@ func findArrow(qpos *QueryPos) token.Pos {
// chanOp abstracts an ssa.Send, ssa.Unop(ARROW), or a SelectState.
type chanOp struct {
ch ssa.Value
dir ast.ChanDir
dir types.ChanDir // SendOnly or RecvOnly
pos token.Pos
}
@ -140,10 +140,10 @@ func chanOps(instr ssa.Instruction) []chanOp {
switch instr := instr.(type) {
case *ssa.UnOp:
if instr.Op == token.ARROW {
ops = append(ops, chanOp{instr.X, ast.RECV, instr.Pos()})
ops = append(ops, chanOp{instr.X, types.RecvOnly, instr.Pos()})
}
case *ssa.Send:
ops = append(ops, chanOp{instr.Chan, ast.SEND, instr.Pos()})
ops = append(ops, chanOp{instr.Chan, types.SendOnly, instr.Pos()})
case *ssa.Select:
for _, st := range instr.States {
ops = append(ops, chanOp{st.Chan, st.Dir, st.Pos})

View File

@ -12,7 +12,6 @@ package pointer
import (
"fmt"
"go/ast"
"go/token"
"code.google.com/p/go.tools/go/types"
@ -994,11 +993,11 @@ func (a *analysis) genInstr(cgn *cgnode, instr ssa.Instruction) {
for _, st := range instr.States {
elemSize := a.sizeof(st.Chan.Type().Underlying().(*types.Chan).Elem())
switch st.Dir {
case ast.RECV:
case types.RecvOnly:
a.genLoad(cgn, recv, st.Chan, 0, elemSize)
recv += nodeid(elemSize)
case ast.SEND:
case types.SendOnly:
a.genStore(cgn, st.Chan, a.valueNode(st.Send), 0, elemSize)
}
}

View File

@ -25,7 +25,6 @@ package pointer
import (
"fmt"
"go/ast"
"reflect"
"code.google.com/p/go.tools/go/exact"
@ -781,7 +780,7 @@ type reflectChanOfConstraint struct {
cgn *cgnode
t nodeid // (ptr)
result nodeid
dirs []ast.ChanDir
dirs []types.ChanDir
}
func (c *reflectChanOfConstraint) String() string {
@ -809,11 +808,11 @@ func (c *reflectChanOfConstraint) solve(a *analysis, _ *node, delta nodeset) {
}
// dirMap maps reflect.ChanDir to the set of channel types generated by ChanOf.
var dirMap = [...][]ast.ChanDir{
0: {ast.RECV, ast.SEND, ast.RECV | ast.SEND}, // unknown
reflect.RecvDir: {ast.RECV},
reflect.SendDir: {ast.SEND},
reflect.BothDir: {ast.RECV | ast.SEND},
var dirMap = [...][]types.ChanDir{
0: {types.SendOnly, types.RecvOnly, types.SendRecv}, // unknown
reflect.RecvDir: {types.RecvOnly},
reflect.SendDir: {types.SendOnly},
reflect.BothDir: {types.SendRecv},
}
func ext۰reflect۰ChanOf(a *analysis, cgn *cgnode) {
@ -909,7 +908,7 @@ func (c *reflectMakeChanConstraint) solve(a *analysis, _ *node, delta nodeset) {
for typObj := range delta {
T := a.rtypeTaggedValue(typObj)
tChan, ok := T.Underlying().(*types.Chan)
if !ok || tChan.Dir() != ast.SEND|ast.RECV {
if !ok || tChan.Dir() != types.SendRecv {
continue // not a bidirectional channel type
}

View File

@ -1385,7 +1385,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
case *ast.SendStmt: // ch<- i
ch := b.expr(fn, comm.Chan)
st = &SelectState{
Dir: ast.SEND,
Dir: types.SendOnly,
Chan: ch,
Send: emitConv(fn, b.expr(fn, comm.Value),
ch.Type().Underlying().(*types.Chan).Elem()),
@ -1398,7 +1398,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
case *ast.AssignStmt: // x := <-ch
recv := unparen(comm.Rhs[0]).(*ast.UnaryExpr)
st = &SelectState{
Dir: ast.RECV,
Dir: types.RecvOnly,
Chan: b.expr(fn, recv.X),
Pos: recv.OpPos,
}
@ -1409,7 +1409,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
case *ast.ExprStmt: // <-ch
recv := unparen(comm.X).(*ast.UnaryExpr)
st = &SelectState{
Dir: ast.RECV,
Dir: types.RecvOnly,
Chan: b.expr(fn, recv.X),
Pos: recv.OpPos,
}
@ -1440,7 +1440,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
var vars []*types.Var
vars = append(vars, varIndex, varOk)
for _, st := range states {
if st.Dir == ast.RECV {
if st.Dir == types.RecvOnly {
tElem := st.Chan.Type().Underlying().(*types.Chan).Elem()
vars = append(vars, types.NewVar(token.NoPos, nil, "", tElem))
}

View File

@ -10,7 +10,6 @@ package ssa
import (
"bytes"
"fmt"
"go/ast"
"io"
"reflect"
"sort"
@ -322,7 +321,7 @@ func (s *Select) String() string {
if i > 0 {
b.WriteString(", ")
}
if st.Dir == ast.RECV {
if st.Dir == types.RecvOnly {
b.WriteString("<-")
b.WriteString(relName(st.Chan, s))
} else {

View File

@ -835,11 +835,11 @@ type Lookup struct {
// It represents one goal state and its corresponding communication.
//
type SelectState struct {
Dir ast.ChanDir // direction of case
Chan Value // channel to use (for send or receive)
Send Value // value to send (for send)
Pos token.Pos // position of token.ARROW
DebugNode ast.Node // ast.SendStmt or ast.UnaryExpr(<-) [debug mode]
Dir types.ChanDir // direction of case (SendOnly or RecvOnly)
Chan Value // channel to use (for send or receive)
Send Value // value to send (for send)
Pos token.Pos // position of token.ARROW
DebugNode ast.Node // ast.SendStmt or ast.UnaryExpr(<-) [debug mode]
}
// The Select instruction tests whether (or blocks until) one or more