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:
parent
ba2241824d
commit
64ea46e0bc
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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] }
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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:] {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue