From 64ea46e0bcdf1bc6f3e2c56632fd1360ed28fb0e Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 26 Jul 2013 22:27:48 -0700 Subject: [PATCH] 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 --- go/types/call.go | 2 +- go/types/check.go | 2 +- go/types/methodset.go | 158 ++++-------------------------------------- go/types/selection.go | 143 ++++++++++++++++++++++++++++++++++++++ ssa/builder.go | 7 +- ssa/promote.go | 34 ++++----- ssa/ssa.go | 4 +- 7 files changed, 181 insertions(+), 169 deletions(-) create mode 100644 go/types/selection.go diff --git a/go/types/call.go b/go/types/call.go index 8d3a8de6..f0798e8a 100644 --- a/go/types/call.go +++ b/go/types/call.go @@ -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") diff --git a/go/types/check.go b/go/types/check.go index e363a939..ce08c342 100644 --- a/go/types/check.go +++ b/go/types/check.go @@ -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} } } diff --git a/go/types/methodset.go b/go/types/methodset.go index 288fc7e3..4733e565 100644 --- a/go/types/methodset.go +++ b/go/types/methodset.go @@ -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] } diff --git a/go/types/selection.go b/go/types/selection.go new file mode 100644 index 00000000..fb71d47a --- /dev/null +++ b/go/types/selection.go @@ -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() +} diff --git a/ssa/builder.go b/ssa/builder.go index fb10f514..9f3ee331 100644 --- a/ssa/builder.go +++ b/ssa/builder.go @@ -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. diff --git a/ssa/promote.go b/ssa/promote.go index 00fe78c4..989f46d7 100644 --- a/ssa/promote.go +++ b/ssa/promote.go @@ -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:] { diff --git a/ssa/ssa.go b/ssa/ssa.go index 29a5d645..08300aed 100644 --- a/ssa/ssa.go +++ b/ssa/ssa.go @@ -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