go.tools/go/types: replace Method w/ Selection

A Method corresponds to a MethodVal Selection;
so the explicit Method object is not needed anymore.

- moved Selection code into separate file
- implemented Selection.String()
- improved and more consistent documentation

R=adonovan
CC=golang-dev
https://golang.org/cl/11950043
This commit is contained in:
Robert Griesemer 2013-07-26 22:27:48 -07:00
parent ba2241824d
commit 64ea46e0bc
7 changed files with 181 additions and 169 deletions

View File

@ -297,7 +297,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
// of checker once we rely on MethodSet lookup instead of individual
// lookup.
mset := typ.MethodSet()
if m := mset.Lookup(check.pkg, sel); m == nil || m.Func != obj {
if m := mset.Lookup(check.pkg, sel); m == nil || m.obj != obj {
check.dump("%s: (%s).%v -> %s", e.Pos(), typ, obj.name, m)
check.dump("%s\n", mset)
panic("method sets and lookup don't agree")

View File

@ -101,7 +101,7 @@ func (check *checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, r
check.recordObject(x.Sel, obj)
// TODO(gri) Should we also call recordTypeAndValue?
if m := check.Selections; m != nil {
m[x] = &Selection{kind, obj, selectorPath{recv, index, indirect}}
m[x] = &Selection{kind, recv, obj, index, indirect}
}
}

View File

@ -13,140 +13,11 @@ import (
"sync"
)
// SelectorKind describes the kind of a selector expression recv.obj.
type SelectionKind int
const (
FieldVal SelectionKind = iota // recv.obj is a struct field selector
MethodVal // recv.obj is a method selector
MethodExpr // recv.obj is a method expression
PackageObj // recv.obj is a qualified identifier
)
// A Selection describes a selector expression recv.obj.
type Selection struct {
kind SelectionKind
obj Object
selectorPath // not valid for PackageObj
}
func (s *Selection) Kind() SelectionKind { return s.kind }
func (s *Selection) Obj() Object { return s.obj }
// Type returns the type of the selector recv.obj which may be different
// from the type of the selected object obj.
//
// Given the example declarations:
//
// type T struct{ x int; E }
// type E struct{}
// func (e E) m() {}
// var p *T
//
// the following relationships exist:
//
// Kind Selector Recv type Selection type
//
// FieldVal p.x T int (type of x)
// MethodVal p.m *T func (e *T) m()
// MethodExpr T.m T func m(_ T)
// PackageObj math.Pi nil untyped numeric constant (type of Pi)
//
func (s *Selection) Type() Type {
switch s.kind {
case MethodVal:
// The type of recv.obj is a method with its receiver type
// changed to s.recv.
// TODO(gri) Similar code is already in Method.Type below.
// Method may go away in favor of Selection.
sig := *s.obj.(*Func).typ.(*Signature)
recv := *sig.recv
recv.typ = s.recv
sig.recv = &recv
return &sig
case MethodExpr:
// The type of recv.obj is a function (without receiver)
// and an additional first argument of type recv.
// TODO(gri) Similar code is already in call.go - factor!
sig := *s.obj.(*Func).typ.(*Signature)
arg0 := *sig.recv
arg0.typ = s.recv
var params []*Var
if sig.params != nil {
params = sig.params.vars
}
sig.params = NewTuple(append([]*Var{&arg0}, params...)...)
return &sig
}
// In all other cases, the type of recv.obj is the type of obj.
return s.obj.Type()
}
// TODO(gri) Replace Method in favor of Selection (make Method an interface?).
// A Method represents a concrete or abstract (interface) method x.m
// and corresponding path. The method belongs to the method set of x.
type Method struct {
*Func
selectorPath
}
// TODO(gri): expose this or something like it (e.g. lookupResult) to
// make life easier for callers of LookupFieldOrMethod.
func NewMethod(f *Func, recv Type, index []int, indirect bool) *Method {
return &Method{f, selectorPath{recv, index, indirect}}
}
// Type returns the promoted signature type of m (i.e., the receiver
// type is Recv()).
func (m *Method) Type() Type {
sig := *m.Func.typ.(*Signature)
recv := *sig.recv
recv.typ = m.recv
sig.recv = &recv
return &sig
}
func (m *Method) String() string {
return fmt.Sprintf("method (%s).%s %s", m.Recv(), m.Name(), m.Type())
}
// A selectorPath describes the path from a value x to one of its fields
// or methods f for a selector expression x.f. A path may include implicit
// field selections (e.g., x.f may be x.a.b.c.f).
type selectorPath struct {
recv Type
index []int
indirect bool
}
// Recv returns the type of x for the path p for x.f.
func (p *selectorPath) Recv() Type { return p.recv }
// Index describes the path from x to the concrete (possibly embedded) field
// or method f for the path p for x.f.
//
// The last index entry is the field or method index of the type declaring f;
// either:
//
// 1) the list of declared methods of a named type; or
// 2) the list of methods of an interface type; or
// 3) the list of fields of a struct type.
//
// The earlier index entries are the indices of the embedded fields implicitly
// traversed to get from (the type of) x to f, starting at embedding depth 0.
func (p *selectorPath) Index() []int { return p.index }
// Indirect reports whether any pointer indirection was required to get from
// a value x to f for the path p for x.f.
func (p *selectorPath) Indirect() bool { return p.indirect }
// A MethodSet is an ordered set of concrete or abstract (interface) methods.
// A MethodSet is an ordered set of concrete or abstract (interface) methods;
// a method is a MethodVal selection.
// The zero value for a MethodSet is a ready-to-use empty method set.
type MethodSet struct {
list []*Method
list []*Selection
}
func (s *MethodSet) String() string {
@ -156,8 +27,9 @@ func (s *MethodSet) String() string {
var buf bytes.Buffer
fmt.Fprintln(&buf, "MethodSet {")
for _, m := range s.list {
fmt.Fprintf(&buf, "\t%s -> %s\n", m.Id(), m.Func)
for _, f := range s.list {
m := f.obj.(*Func)
fmt.Fprintf(&buf, "\t%s -> %s\n", m.Id(), m)
}
fmt.Fprintln(&buf, "}")
return buf.String()
@ -167,10 +39,10 @@ func (s *MethodSet) String() string {
func (s *MethodSet) Len() int { return len(s.list) }
// At returns the i'th method in s for 0 <= i < s.Len().
func (s *MethodSet) At(i int) *Method { return s.list[i] }
func (s *MethodSet) At(i int) *Selection { return s.list[i] }
// Lookup returns the method with matching package and name, or nil if not found.
func (s *MethodSet) Lookup(pkg *Package, name string) *Method {
func (s *MethodSet) Lookup(pkg *Package, name string) *Selection {
if s.Len() == 0 {
return nil
}
@ -178,11 +50,11 @@ func (s *MethodSet) Lookup(pkg *Package, name string) *Method {
key := Id(pkg, name)
i := sort.Search(len(s.list), func(i int) bool {
m := s.list[i]
return m.Id() >= key
return m.obj.Id() >= key
})
if i < len(s.list) {
m := s.list[i]
if m.Id() == key {
if m.obj.Id() == key {
return m
}
}
@ -325,7 +197,7 @@ func NewMethodSet(T Type) *MethodSet {
}
// collect methods
var list []*Method
var list []*Selection
for _, m := range base {
if m != nil {
m.recv = T
@ -363,7 +235,7 @@ func (s fieldSet) add(f *Var, multiples bool) fieldSet {
// A methodSet is a set of methods and name collisions.
// A collision indicates that multiple methods with the
// same unique id appeared.
type methodSet map[string]*Method // a nil entry indicates a name collision
type methodSet map[string]*Selection // a nil entry indicates a name collision
// Add adds all functions in list to the method set s.
// If multiples is set, every function in list appears multiple times
@ -380,7 +252,7 @@ func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool)
// if f is not in the set, add it
if !multiples {
if _, found := s[key]; !found && (indirect || !ptrRecv(f)) {
s[key] = &Method{f, selectorPath{index: concat(index, i), indirect: indirect}}
s[key] = &Selection{MethodVal, nil, f, concat(index, i), indirect}
continue
}
}
@ -397,8 +269,8 @@ func ptrRecv(f *Func) bool {
}
// byUniqueName function lists can be sorted by their unique names.
type byUniqueName []*Method
type byUniqueName []*Selection
func (a byUniqueName) Len() int { return len(a) }
func (a byUniqueName) Less(i, j int) bool { return a[i].Id() < a[j].Id() }
func (a byUniqueName) Less(i, j int) bool { return a[i].obj.Id() < a[j].obj.Id() }
func (a byUniqueName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }

143
go/types/selection.go Normal file
View File

@ -0,0 +1,143 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements Selections.
package types
import (
"bytes"
"fmt"
)
// SelectionKind describes the kind of a selector expression x.f.
type SelectionKind int
const (
FieldVal SelectionKind = iota // x.f is a struct field selector
MethodVal // x.f is a method selector
MethodExpr // x.f is a method expression
PackageObj // x.f is a qualified identifier
)
// A Selection describes a selector expression x.f.
// For the declarations:
//
// type T struct{ x int; E }
// type E struct{}
// func (e E) m() {}
// var p *T
//
// the following relations exist:
//
// Selector Kind Recv Obj Type Index Indirect
//
// p.x FieldVal T x int {0} true
// p.m MethodVal *T m func (e *T) m() {1, 0} true
// T.m MethodExpr T m func m(_ T) {1, 0} false
// math.Pi PackageObj nil Pi untyped numeric nil false
//
type Selection struct {
kind SelectionKind
recv Type // type of x, nil if kind == PackageObj
obj Object // object denoted by x.f
index []int // path from x to x.f, nil if kind == PackageObj
indirect bool // set if there was any pointer indirection on the path, false if kind == PackageObj
}
// NewSelection returns a new Selection.
// TODO(gri) At the moment this is only used by package ssa.
func NewSelection(kind SelectionKind, recv Type, obj Object, index []int, indirect bool) *Selection {
return &Selection{kind, recv, obj, index, indirect}
}
// Kind returns the selection kind.
func (s *Selection) Kind() SelectionKind { return s.kind }
// Recv returns the type of x in x.f.
// The result is nil if x.f is a qualified identifier (PackageObj).
func (s *Selection) Recv() Type { return s.recv }
// Obj returns the object denoted by x.f.
// The following object types may appear:
//
// Kind Object
//
// FieldVal *Var field
// MethodVal *Func method
// MethodExpr *Func method
// PackageObj *Const, *Type, *Var, *Func imported const, type, var, or func
//
func (s *Selection) Obj() Object { return s.obj }
// Type returns the type of x.f, which may be different from the type of f.
// See Selection for more information.
func (s *Selection) Type() Type {
switch s.kind {
case MethodVal:
// The type of x.f is a method with its receiver type to the
// type of x.
sig := *s.obj.(*Func).typ.(*Signature)
recv := *sig.recv
recv.typ = s.recv
sig.recv = &recv
return &sig
case MethodExpr:
// The type of x.f is a function (without receiver)
// and an additional first argument with the same type as x.
// TODO(gri) Similar code is already in call.go - factor!
sig := *s.obj.(*Func).typ.(*Signature)
arg0 := *sig.recv
arg0.typ = s.recv
var params []*Var
if sig.params != nil {
params = sig.params.vars
}
sig.params = NewTuple(append([]*Var{&arg0}, params...)...)
return &sig
}
// In all other cases, the type of x.f is the type of x.
return s.obj.Type()
}
// Index describes the path from x to f in x.f.
// The result is nil if x.f is a qualified identifier (PackageObj).
//
// The last index entry is the field or method index of the type declaring f;
// either:
//
// 1) the list of declared methods of a named type; or
// 2) the list of methods of an interface type; or
// 3) the list of fields of a struct type.
//
// The earlier index entries are the indices of the embedded fields implicitly
// traversed to get from (the type of) x to f, starting at embedding depth 0.
func (s *Selection) Index() []int { return s.index }
// Indirect reports whether any pointer indirection was required to get from
// x to f in x.f.
// The result is false if x.f is a qualified identifier (PackageObj).
func (s *Selection) Indirect() bool { return s.indirect }
func (s *Selection) String() string {
var k string
switch s.kind {
case FieldVal:
k = "field"
case MethodVal:
k = "method"
case MethodExpr:
k = "method expr"
case PackageObj:
return fmt.Sprintf("qualified ident %s", s.obj)
default:
unreachable()
}
var buf bytes.Buffer
fmt.Fprintf(&buf, "%s (%s) %s", k, s.Recv(), s.obj.Name())
writeSignature(&buf, s.Type().(*Signature))
return buf.String()
}

View File

@ -628,13 +628,8 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value {
case types.MethodExpr:
// (*T).f or T.f, the method f from the method-set of type T.
// TODO(adonovan): once methodsets contain
// Selections, eliminate this.
meth := types.NewMethod(sel.Obj().(*types.Func), fn.Pkg.typeOf(e.X),
sel.Index(), sel.Indirect())
// 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(sel), fn.Pkg.typeOf(e))
case types.MethodVal:
// e.f where e is an expression and f is a method.

View File

@ -56,7 +56,7 @@ func (prog *Program) MethodSet(typ types.Type) MethodSet {
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
//
func (prog *Program) populateMethodSet(typ types.Type, meth *types.Method) MethodSet {
func (prog *Program) populateMethodSet(typ types.Type, meth *types.Selection) MethodSet {
tmset := methodSet(typ)
n := tmset.Len()
if n == 0 {
@ -78,7 +78,7 @@ func (prog *Program) populateMethodSet(typ types.Type, meth *types.Method) Metho
if len(mset) < n {
if meth != nil { // single method
id := meth.Id()
id := meth.Obj().Id()
if mset[id] == nil {
mset[id] = findMethod(prog, meth)
}
@ -86,7 +86,7 @@ func (prog *Program) populateMethodSet(typ types.Type, meth *types.Method) Metho
// complete set
for i := 0; i < n; i++ {
meth := tmset.At(i)
if id := meth.Id(); mset[id] == nil {
if id := meth.Obj().Id(); mset[id] == nil {
mset[id] = findMethod(prog, meth)
}
}
@ -114,8 +114,8 @@ func methodSet(typ types.Type) *types.MethodSet {
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
//
func (prog *Program) LookupMethod(meth *types.Method) *Function {
return prog.populateMethodSet(meth.Recv(), meth)[meth.Id()]
func (prog *Program) LookupMethod(meth *types.Selection) *Function {
return prog.populateMethodSet(meth.Recv(), meth)[meth.Obj().Id()]
}
// concreteMethod returns the concrete method denoted by obj.
@ -135,19 +135,20 @@ func (prog *Program) concreteMethod(obj *types.Func) *Function {
//
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
//
func findMethod(prog *Program, meth *types.Method) *Function {
func findMethod(prog *Program, meth *types.Selection) *Function {
needsPromotion := len(meth.Index()) > 1
needsIndirection := !isPointer(recvType(meth.Func)) && isPointer(meth.Recv())
mfunc := meth.Obj().(*types.Func)
needsIndirection := !isPointer(recvType(mfunc)) && isPointer(meth.Recv())
if needsPromotion || needsIndirection {
return makeWrapper(prog, meth.Recv(), meth)
}
if _, ok := meth.Recv().Underlying().(*types.Interface); ok {
return interfaceMethodWrapper(prog, meth.Recv(), meth.Func)
return interfaceMethodWrapper(prog, meth.Recv(), mfunc)
}
return prog.concreteMethod(meth.Func)
return prog.concreteMethod(mfunc)
}
// makeWrapper returns a synthetic wrapper Function that optionally
@ -171,21 +172,22 @@ func findMethod(prog *Program, meth *types.Method) *Function {
//
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
//
func makeWrapper(prog *Program, typ types.Type, meth *types.Method) *Function {
old := meth.Func.Type().(*types.Signature)
func makeWrapper(prog *Program, typ types.Type, meth *types.Selection) *Function {
mfunc := meth.Obj().(*types.Func)
old := mfunc.Type().(*types.Signature)
sig := types.NewSignature(nil, types.NewVar(token.NoPos, nil, "recv", typ), old.Params(), old.Results(), old.IsVariadic())
description := fmt.Sprintf("wrapper for %s", meth.Func)
description := fmt.Sprintf("wrapper for %s", mfunc)
if prog.mode&LogSource != 0 {
defer logStack("make %s to (%s)", description, typ)()
}
fn := &Function{
name: meth.Name(),
name: mfunc.Name(),
method: meth,
Signature: sig,
Synthetic: description,
Prog: prog,
pos: meth.Pos(),
pos: mfunc.Pos(),
}
fn.startBody()
fn.addSpilledParam(sig.Recv())
@ -218,10 +220,10 @@ func makeWrapper(prog *Program, typ types.Type, meth *types.Method) *Function {
if !isPointer(old.Recv().Type()) {
v = emitLoad(fn, v)
}
c.Call.Value = prog.concreteMethod(meth.Func)
c.Call.Value = prog.concreteMethod(mfunc)
c.Call.Args = append(c.Call.Args, v)
} else {
c.Call.Method = meth.Func
c.Call.Method = mfunc
c.Call.Value = emitLoad(fn, v)
}
for _, arg := range fn.Params[1:] {

View File

@ -253,8 +253,8 @@ type Instruction interface {
//
type Function struct {
name string
object types.Object // a declared *types.Func; nil for init, wrappers, etc.
method *types.Method // info about provenance of synthetic methods [currently unused]
object types.Object // a declared *types.Func; nil for init, wrappers, etc.
method *types.Selection // info about provenance of synthetic methods [currently unused]
Signature *types.Signature
pos token.Pos