go.tools/ssa: use new Selections to simplify builder case discrimination.

Delete importer.PkgInfo.{ClassifySelector,IsPackageRef}.

R=gri
CC=golang-dev
https://golang.org/cl/11937045
This commit is contained in:
Alan Donovan 2013-07-26 22:29:44 -04:00
parent ae8016313d
commit ba2241824d
3 changed files with 99 additions and 173 deletions

View File

@ -159,10 +159,11 @@ func (imp *Importer) CreateSourcePackage(importPath string, files []*ast.File) *
pkgInfo := &PackageInfo{ pkgInfo := &PackageInfo{
Files: files, Files: files,
Info: types.Info{ Info: types.Info{
Types: make(map[ast.Expr]types.Type), Types: make(map[ast.Expr]types.Type),
Values: make(map[ast.Expr]exact.Value), Values: make(map[ast.Expr]exact.Value),
Objects: make(map[*ast.Ident]types.Object), Objects: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object), Implicits: make(map[ast.Node]types.Object),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
}, },
} }
pkgInfo.Pkg, pkgInfo.Err = imp.config.TypeChecker.Check(importPath, imp.Fset, files, &pkgInfo.Info) pkgInfo.Pkg, pkgInfo.Err = imp.config.TypeChecker.Check(importPath, imp.Fset, files, &pkgInfo.Info)

View File

@ -88,11 +88,13 @@ func (info *PackageInfo) ObjectOf(id *ast.Ident) types.Object {
// IsType returns true iff expression e denotes a type. // IsType returns true iff expression e denotes a type.
// Precondition: e belongs to the package's ASTs. // Precondition: e belongs to the package's ASTs.
// //
// TODO(gri): move this into go/types.
//
func (info *PackageInfo) IsType(e ast.Expr) bool { func (info *PackageInfo) IsType(e ast.Expr) bool {
switch e := e.(type) { switch e := e.(type) {
case *ast.SelectorExpr: // pkg.Type case *ast.SelectorExpr: // pkg.Type
if obj := info.IsPackageRef(e); obj != nil { if sel := info.Selections[e]; sel.Kind() == types.PackageObj {
_, isType := obj.(*types.TypeName) _, isType := sel.Obj().(*types.TypeName)
return isType return isType
} }
case *ast.StarExpr: // *T case *ast.StarExpr: // *T
@ -108,40 +110,6 @@ func (info *PackageInfo) IsType(e ast.Expr) bool {
return false return false
} }
// IsPackageRef returns the identity of the object if sel is a
// package-qualified reference to a named const, var, func or type.
// Otherwise it returns nil.
// Precondition: sel belongs to the package's ASTs.
//
func (info *PackageInfo) IsPackageRef(sel *ast.SelectorExpr) types.Object {
if id, ok := sel.X.(*ast.Ident); ok {
if pkg, ok := info.ObjectOf(id).(*types.Package); ok {
return pkg.Scope().Lookup(sel.Sel.Name)
}
}
return nil
}
// ClassifySelector returns one of token.{PACKAGE,VAR,TYPE} to
// indicate whether the base operand of sel is a package, expression
// or type, respectively.
//
// Examples:
// PACKAGE: fmt.Println (func), math.Pi (const), types.Universe (var)
// VAR: info.IsType (*Method), info.Objects (*Field)
// TYPE: PackageInfo.IsType (func)
//
func (info *PackageInfo) ClassifySelector(sel *ast.SelectorExpr) token.Token {
switch {
case info.IsPackageRef(sel) != nil:
return token.PACKAGE
case info.IsType(sel.X):
return token.TYPE
default:
return token.VAR
}
}
// TypeCaseVar returns the implicit variable created by a single-type // TypeCaseVar returns the implicit variable created by a single-type
// case clause in a type switch, or nil if not found. // case clause in a type switch, or nil if not found.
// //

View File

@ -379,24 +379,21 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
return b.addr(fn, e.X, escaping) return b.addr(fn, e.X, escaping)
case *ast.SelectorExpr: case *ast.SelectorExpr:
// p.M where p is a package. switch sel := fn.Pkg.info.Selections[e]; sel.Kind() {
if obj := fn.Pkg.info.IsPackageRef(e); obj != nil { case types.PackageObj:
obj := sel.Obj()
if v := b.lookup(fn.Pkg, obj); v != nil { if v := b.lookup(fn.Pkg, obj); v != nil {
return &address{addr: v} return &address{addr: v}
} }
panic("undefined package-qualified name: " + obj.Name()) panic("undefined package-qualified name: " + obj.Name())
case types.FieldVal:
wantAddr := true
v := b.receiver(fn, e.X, wantAddr, escaping, sel)
last := len(sel.Index()) - 1
return &address{addr: emitFieldSelection(fn, v, sel.Index()[last], true, e.Sel.Pos())}
} }
// e.f where e is an expression and f is a field.
typ := fn.Pkg.typeOf(e.X)
obj, indices, isIndirect := types.LookupFieldOrMethod(typ, fn.Pkg.Object, e.Sel.Name)
_ = obj.(*types.Var) // assertion
wantAddr := true
v := b.receiver(fn, e.X, wantAddr, escaping, indices, isIndirect)
last := len(indices) - 1
return &address{addr: emitFieldSelection(fn, v, indices[last], true, e.Sel.Pos())}
case *ast.IndexExpr: case *ast.IndexExpr:
var x Value var x Value
var et types.Type var et types.Type
@ -523,8 +520,6 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value {
switch y := y.(type) { switch y := y.(type) {
case *Convert: case *Convert:
y.pos = e.Lparen y.pos = e.Lparen
case *ChangeInterface:
y.pos = e.Lparen
case *ChangeType: case *ChangeType:
y.pos = e.Lparen y.pos = e.Lparen
case *MakeInterface: case *MakeInterface:
@ -627,43 +622,27 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value {
return v return v
case *ast.SelectorExpr: case *ast.SelectorExpr:
selKind := fn.Pkg.info.ClassifySelector(e) switch sel := fn.Pkg.info.Selections[e]; sel.Kind() {
case types.PackageObj:
// p.M where p is a package.
if selKind == token.PACKAGE {
return b.expr(fn, e.Sel) return b.expr(fn, e.Sel)
}
typ := fn.Pkg.typeOf(e.X) case types.MethodExpr:
obj, indices, isIndirect := types.LookupFieldOrMethod(typ, fn.Pkg.Object, e.Sel.Name) // (*T).f or T.f, the method f from the method-set of type T.
// (*T).f or T.f, the method f from the method-set of type T. // TODO(adonovan): once methodsets contain
// We return a standalone function that calls the method. // Selections, eliminate this.
if selKind == token.TYPE { meth := types.NewMethod(sel.Obj().(*types.Func), fn.Pkg.typeOf(e.X),
obj := obj.(*types.Func) sel.Index(), sel.Indirect())
if _, ok := typ.Underlying().(*types.Interface); ok {
// T is an interface; return wrapper.
fn.Prog.methodsMu.Lock()
defer fn.Prog.methodsMu.Unlock()
return interfaceMethodWrapper(fn.Prog, typ, obj)
}
// TODO(gri): make LookupFieldOrMethod return one of these
// so we don't have to construct it.
meth := types.NewMethod(obj, typ, indices, isIndirect)
// For declared methods, a simple conversion will suffice. // For declared methods, a simple conversion will suffice.
return emitConv(fn, fn.Prog.LookupMethod(meth), fn.Pkg.typeOf(e)) return emitConv(fn, fn.Prog.LookupMethod(meth), fn.Pkg.typeOf(e))
}
// selKind == token.VAR case types.MethodVal:
switch obj := obj.(type) {
case *types.Func:
// e.f where e is an expression and f is a method. // e.f where e is an expression and f is a method.
// The result is a bound method closure. // The result is a bound method closure.
obj := sel.Obj().(*types.Func)
wantAddr := isPointer(recvType(obj)) wantAddr := isPointer(recvType(obj))
escaping := true escaping := true
v := b.receiver(fn, e.X, wantAddr, escaping, indices, isIndirect) v := b.receiver(fn, e.X, wantAddr, escaping, sel)
c := &MakeClosure{ c := &MakeClosure{
Fn: boundMethodWrapper(fn.Prog, obj), Fn: boundMethodWrapper(fn.Prog, obj),
Bindings: []Value{v}, Bindings: []Value{v},
@ -676,8 +655,8 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value {
// interaction of bound interface method // interaction of bound interface method
// closures and promotion. // closures and promotion.
case *types.Var: case types.FieldVal:
// e.f where e is an expression and f is a field. indices := sel.Index()
last := len(indices) - 1 last := len(indices) - 1
v := b.expr(fn, e.X) v := b.expr(fn, e.X)
v = emitImplicitSelections(fn, v, indices[:last]) v = emitImplicitSelections(fn, v, indices[:last])
@ -744,24 +723,24 @@ func (b *builder) stmtList(fn *Function, list []ast.Stmt) {
// receiver emits to fn code for expression e in the "receiver" // receiver emits to fn code for expression e in the "receiver"
// position of selection e.f (where f may be a field or a method) and // position of selection e.f (where f may be a field or a method) and
// returns the effective receiver after applying the implicit field // returns the effective receiver after applying the implicit field
// selections of indices (the last element of which is ignored). // selections of sel.
// //
// wantAddr requests that the result is an an address. If // wantAddr requests that the result is an an address. If
// !isIndirect, this may require that e be build in addr() mode; it // !sel.Indirect(), this may require that e be build in addr() mode; it
// must thus be addressable. // must thus be addressable.
// //
// escaping is defined as per builder.addr(). // escaping is defined as per builder.addr().
// //
func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, indices []int, isIndirect bool) Value { func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, sel *types.Selection) Value {
var v Value var v Value
if wantAddr && !isIndirect && !isPointer(fn.Pkg.typeOf(e)) { if wantAddr && !sel.Indirect() && !isPointer(fn.Pkg.typeOf(e)) {
v = b.addr(fn, e, escaping).address(fn) v = b.addr(fn, e, escaping).address(fn)
} else { } else {
v = b.expr(fn, e) v = b.expr(fn, e)
} }
last := len(indices) - 1 last := len(sel.Index()) - 1
v = emitImplicitSelections(fn, v, indices[:last]) v = emitImplicitSelections(fn, v, sel.Index()[:last])
if !wantAddr && isPointer(v.Type()) { if !wantAddr && isPointer(v.Type()) {
v = emitLoad(fn, v) v = emitLoad(fn, v)
} }
@ -776,89 +755,72 @@ func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
c.pos = e.Lparen c.pos = e.Lparen
c.HasEllipsis = e.Ellipsis != 0 c.HasEllipsis = e.Ellipsis != 0
// Is the call of the form x.f()? // Is this a method call?
sel, ok := unparen(e.Fun).(*ast.SelectorExpr) if selector, ok := unparen(e.Fun).(*ast.SelectorExpr); ok {
switch sel := fn.Pkg.info.Selections[selector]; sel.Kind() {
case types.PackageObj:
// e.g. fmt.Println
// e.Fun is not a selector. case types.MethodExpr:
// Evaluate it in the usual way. // T.f() or (*T).f(): a statically dispatched
if !ok { // call to the method f in the method-set of T
c.Value = b.expr(fn, e.Fun) // or *T. T may be an interface.
return
}
selKind := fn.Pkg.info.ClassifySelector(sel) // e.Fun would evaluate to a concrete method,
// interface wrapper function, or promotion
// wrapper.
//
// For now, we evaluate it in the usual way.
// e.Fun refers to a package-level func or var. // TODO(adonovan): opt: inline expr() here, to
// Evaluate it in the usual way. // make the call static and to avoid
if selKind == token.PACKAGE { // generation of wrappers. It's somewhat
c.Value = b.expr(fn, e.Fun) // tricky as it may consume the first actual
return // parameter if the call is "invoke" mode.
} //
// Examples:
// type T struct{}; func (T) f() {} // "call" mode
// type T interface { f() } // "invoke" mode
//
// type S struct{ T }
//
// var s S
// S.f(s)
// (*S).f(&s)
//
// Suggested approach:
// - consume the first actual parameter expression
// and build it with b.expr().
// - apply implicit field selections.
// - use MethodVal logic to populate fields of c.
typ := fn.Pkg.typeOf(sel.X) case types.FieldVal:
obj, indices, isIndirect := types.LookupFieldOrMethod(typ, fn.Pkg.Object, sel.Sel.Name) // A field access, not a method call.
// T.f() or (*T).f(): a statically dispatched call to the case types.MethodVal:
// method f in the method-set of T or *T. obj := sel.Obj().(*types.Func)
// T may be an interface. wantAddr := isPointer(recvType(obj))
if selKind == token.TYPE { escaping := true
// e.Fun would evaluate to a concrete method, v := b.receiver(fn, selector.X, wantAddr, escaping, sel)
// interface wrapper function, or promotion wrapper. if _, ok := deref(v.Type()).Underlying().(*types.Interface); ok {
// // Invoke-mode call.
// For now, we evaluate it in the usual way. c.Value = v
c.Value = b.expr(fn, e.Fun) c.Method = obj
} else {
// "Call"-mode call.
c.Value = fn.Prog.concreteMethod(obj)
c.Args = append(c.Args, v)
}
return
// TODO(adonovan): opt: inline expr() here, to make default:
// the call static and to avoid generation of panic(fmt.Sprintf("illegal (%s).%s() call; X:%T",
// wrappers. It's somewhat tricky as it may consume fn.Pkg.typeOf(selector.X), selector.Sel.Name, selector.X))
// the first actual parameter if the call is "invoke"
// mode.
//
// Examples:
// type T struct{}; func (T) f() {} // "call" mode
// type T interface { f() } // "invoke" mode
//
// type S struct{ T }
//
// var s S
// S.f(s)
// (*S).f(&s)
//
// Suggested approach:
// - consume the first actual parameter expression
// and build it with b.expr().
// - apply implicit field selections.
// - use same code as selKind == VAR case to populate fields of c.
return
}
// selKind == token.VAR
switch obj := obj.(type) {
case *types.Func:
wantAddr := isPointer(recvType(obj))
escaping := true
v := b.receiver(fn, sel.X, wantAddr, escaping, indices, isIndirect)
if _, ok := deref(v.Type()).Underlying().(*types.Interface); ok {
// Invoke-mode call.
c.Value = v
c.Method = obj
} else {
// "Call"-mode call.
c.Value = fn.Prog.concreteMethod(obj)
c.Args = append(c.Args, v)
} }
return
case *types.Var:
// Field access: x.f() where x.f is a function value
// in a struct field f; not a method call.
// Evaluate it in the usual way.
c.Value = b.expr(fn, e.Fun)
return
} }
panic(fmt.Sprintf("illegal (%s).%s() call; X:%T", typ, sel.Sel.Name, sel.X)) // Evaluate the function operand in the usual way.
c.Value = b.expr(fn, e.Fun)
} }
// emitCallArgs emits to f code for the actual parameters of call e to // emitCallArgs emits to f code for the actual parameters of call e to
@ -1468,18 +1430,18 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
ti = x ti = x
} }
fn.currentBlock = body fn.currentBlock = body
b.typeCase(fn, cc, ti, done) b.typeCaseBody(fn, cc, ti, done)
fn.currentBlock = next fn.currentBlock = next
} }
if default_ != nil { if default_ != nil {
b.typeCase(fn, default_, x, done) b.typeCaseBody(fn, default_, x, done)
} else { } else {
emitJump(fn, done) emitJump(fn, done)
} }
fn.currentBlock = done fn.currentBlock = done
} }
func (b *builder) typeCase(fn *Function, cc *ast.CaseClause, x Value, done *BasicBlock) { func (b *builder) typeCaseBody(fn *Function, cc *ast.CaseClause, x Value, done *BasicBlock) {
if obj := fn.Pkg.info.TypeCaseVar(cc); obj != nil { if obj := fn.Pkg.info.TypeCaseVar(cc); obj != nil {
// In a switch y := x.(type), each case clause // In a switch y := x.(type), each case clause
// implicitly declares a distinct object y. // implicitly declares a distinct object y.
@ -1540,23 +1502,18 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
Chan: ch, Chan: ch,
Send: emitConv(fn, b.expr(fn, comm.Value), Send: emitConv(fn, b.expr(fn, comm.Value),
ch.Type().Underlying().(*types.Chan).Elem()), ch.Type().Underlying().(*types.Chan).Elem()),
Pos: comm.Arrow,
}) })
case *ast.AssignStmt: // x := <-ch case *ast.AssignStmt: // x := <-ch
unary := unparen(comm.Rhs[0]).(*ast.UnaryExpr)
states = append(states, SelectState{ states = append(states, SelectState{
Dir: ast.RECV, Dir: ast.RECV,
Chan: b.expr(fn, unary.X), Chan: b.expr(fn, unparen(comm.Rhs[0]).(*ast.UnaryExpr).X),
Pos: unary.OpPos,
}) })
case *ast.ExprStmt: // <-ch case *ast.ExprStmt: // <-ch
unary := unparen(comm.X).(*ast.UnaryExpr)
states = append(states, SelectState{ states = append(states, SelectState{
Dir: ast.RECV, Dir: ast.RECV,
Chan: b.expr(fn, unary.X), Chan: b.expr(fn, unparen(comm.X).(*ast.UnaryExpr).X),
Pos: unary.OpPos,
}) })
} }
} }