From 74d33a9c3385a4cff77edbcf3240acebb291eb9f Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 17 Dec 2013 15:45:01 -0800 Subject: [PATCH] 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 --- go/gcimporter/gcimporter.go | 7 +++---- go/importer/import.go | 3 +-- go/types/builtins.go | 2 +- go/types/expr.go | 2 +- go/types/operand.go | 2 +- go/types/stmt.go | 4 ++-- go/types/types.go | 21 ++++++++++++++------- go/types/typestring.go | 15 ++++++++------- go/types/typexpr.go | 14 +++++++++++++- oracle/peers.go | 8 ++++---- pointer/gen.go | 5 ++--- pointer/reflect.go | 15 +++++++-------- ssa/builder.go | 8 ++++---- ssa/print.go | 3 +-- ssa/ssa.go | 10 +++++----- 15 files changed, 67 insertions(+), 52 deletions(-) diff --git a/go/gcimporter/gcimporter.go b/go/gcimporter/gcimporter.go index eb7cb30b..18e6f7b4 100644 --- a/go/gcimporter/gcimporter.go +++ b/go/gcimporter/gcimporter.go @@ -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) diff --git a/go/importer/import.go b/go/importer/import.go index 5aa01487..c5e7850d 100644 --- a/go/importer/import.go +++ b/go/importer/import.go @@ -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: diff --git a/go/types/builtins.go b/go/types/builtins.go index be10b801..35b6078c 100644 --- a/go/types/builtins.go +++ b/go/types/builtins.go @@ -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 } diff --git a/go/types/expr.go b/go/types/expr.go index 6dd67543..aaae1c23 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -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 diff --git a/go/types/operand.go b/go/types/operand.go index fc474834..6d22d3d4 100644 --- a/go/types/operand.go +++ b/go/types/operand.go @@ -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) } diff --git a/go/types/stmt.go b/go/types/stmt.go index 5f8e7e1c..5ef682b1 100644 --- a/go/types/stmt.go +++ b/go/types/stmt.go @@ -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 } diff --git a/go/types/types.go b/go/types/types.go index 3272f2fc..d5f02768 100644 --- a/go/types/types.go +++ b/go/types/types.go @@ -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 } diff --git a/go/types/typestring.go b/go/types/typestring.go index c2b7161e..233e86d8 100644 --- a/go/types/typestring.go +++ b/go/types/typestring.go @@ -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 { diff --git a/go/types/typexpr.go b/go/types/typexpr.go index 20b86f9f..19d428f6 100644 --- a/go/types/typexpr.go +++ b/go/types/typexpr.go @@ -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 diff --git a/oracle/peers.go b/oracle/peers.go index 13a91d3d..a47a00c1 100644 --- a/oracle/peers.go +++ b/oracle/peers.go @@ -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}) diff --git a/pointer/gen.go b/pointer/gen.go index d603f53b..c2bd4eb7 100644 --- a/pointer/gen.go +++ b/pointer/gen.go @@ -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) } } diff --git a/pointer/reflect.go b/pointer/reflect.go index 05f5643d..38f631cb 100644 --- a/pointer/reflect.go +++ b/pointer/reflect.go @@ -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 } diff --git a/ssa/builder.go b/ssa/builder.go index 69c9869a..676cab66 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -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)) } diff --git a/ssa/print.go b/ssa/print.go index e75caf31..4738068c 100644 --- a/ssa/print.go +++ b/ssa/print.go @@ -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 { diff --git a/ssa/ssa.go b/ssa/ssa.go index a8a301de..a0dfb527 100644 --- a/ssa/ssa.go +++ b/ssa/ssa.go @@ -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