go/ssa: create *ssa.selection.
Simplifies handling *types.Selections by always using a *ssa.selection
internally. Updates the selection during monomorphization.
Updates golang/go#48525
Change-Id: If9cf7a623d3fed060dda41a5b65c46fcfe3d431c
Reviewed-on: https://go-review.googlesource.com/c/tools/+/405557
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Run-TryBot: Tim King <taking@google.com>
diff --git a/go/ssa/builder.go b/go/ssa/builder.go
index 06c4389..de89160 100644
--- a/go/ssa/builder.go
+++ b/go/ssa/builder.go
@@ -443,8 +443,8 @@
return b.addr(fn, e.X, escaping)
case *ast.SelectorExpr:
- sel, ok := fn.info.Selections[e]
- if !ok {
+ sel := fn.selection(e)
+ if sel == nil {
// qualified identifier
return b.addr(fn, e.Sel, escaping)
}
@@ -786,8 +786,8 @@
return emitLoad(fn, fn.lookup(obj, false)) // var (address)
case *ast.SelectorExpr:
- sel, ok := fn.info.Selections[e]
- if !ok {
+ sel := fn.selection(e)
+ if sel == nil {
// builtin unsafe.{Add,Slice}
if obj, ok := fn.info.Uses[e.Sel].(*types.Builtin); ok {
return &Builtin{name: obj.Name(), sig: fn.typ(tv.Type).(*types.Signature)}
@@ -799,28 +799,12 @@
case types.MethodExpr:
// (*T).f or T.f, the method f from the method-set of type T.
// The result is a "thunk".
-
- sel := selection(sel)
- if base := fn.typ(sel.Recv()); base != sel.Recv() {
- // instantiate sel as sel.Recv is not equal after substitution.
- pkg := fn.declaredPackage().Pkg
- // mv is the instantiated method value.
- mv := types.NewMethodSet(base).Lookup(pkg, sel.Obj().Name())
- sel = toMethodExpr(mv)
- }
thunk := makeThunk(fn.Prog, sel, b.created)
return emitConv(fn, thunk, fn.typ(tv.Type))
case types.MethodVal:
// e.f where e is an expression and f is a method.
// The result is a "bound".
-
- if base := fn.typ(sel.Recv()); base != sel.Recv() {
- // instantiate sel as sel.Recv is not equal after substitution.
- pkg := fn.declaredPackage().Pkg
- // mv is the instantiated method value.
- sel = types.NewMethodSet(base).Lookup(pkg, sel.Obj().Name())
- }
obj := sel.Obj().(*types.Func)
rt := fn.typ(recvType(obj))
wantAddr := isPointer(rt)
@@ -939,7 +923,7 @@
// must thus be addressable.
//
// escaping is defined as per builder.addr().
-func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, sel *types.Selection) Value {
+func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, sel *selection) Value {
var v Value
if wantAddr && !sel.Indirect() && !isPointer(fn.typeOf(e)) {
v = b.addr(fn, e, escaping).address(fn)
@@ -964,15 +948,9 @@
// Is this a method call?
if selector, ok := unparen(e.Fun).(*ast.SelectorExpr); ok {
- sel, ok := fn.info.Selections[selector]
- if ok && sel.Kind() == types.MethodVal {
+ sel := fn.selection(selector)
+ if sel != nil && sel.Kind() == types.MethodVal {
obj := sel.Obj().(*types.Func)
- if recv := fn.typ(sel.Recv()); recv != sel.Recv() {
- // adjust obj if the sel.Recv() changed during monomorphization.
- pkg := fn.declaredPackage().Pkg
- method, _, _ := types.LookupFieldOrMethod(recv, true, pkg, sel.Obj().Name())
- obj = method.(*types.Func)
- }
recv := recvType(obj)
wantAddr := isPointer(recv)
diff --git a/go/ssa/func.go b/go/ssa/func.go
index 9ce2bfe..84d8113 100644
--- a/go/ssa/func.go
+++ b/go/ssa/func.go
@@ -52,6 +52,40 @@
return f.typeOf(id)
}
+// selection returns a *selection corresponding to f.info.Selections[selector]
+// with potential updates for type substitution.
+func (f *Function) selection(selector *ast.SelectorExpr) *selection {
+ sel := f.info.Selections[selector]
+ if sel == nil {
+ return nil
+ }
+
+ switch sel.Kind() {
+ case types.MethodExpr, types.MethodVal:
+ if recv := f.typ(sel.Recv()); recv != sel.Recv() {
+ // recv changed during type substitution.
+ pkg := f.declaredPackage().Pkg
+ obj, index, indirect := types.LookupFieldOrMethod(recv, true, pkg, sel.Obj().Name())
+
+ // sig replaces sel.Type(). See (types.Selection).Typ() for details.
+ sig := obj.Type().(*types.Signature)
+ sig = changeRecv(sig, newVar(sig.Recv().Name(), recv))
+ if sel.Kind() == types.MethodExpr {
+ sig = recvAsFirstArg(sig)
+ }
+ return &selection{
+ kind: sel.Kind(),
+ recv: recv,
+ typ: sig,
+ obj: obj,
+ index: index,
+ indirect: indirect,
+ }
+ }
+ }
+ return toSelection(sel)
+}
+
// Destinations associated with unlabelled for/switch/select stmts.
// We push/pop one of these as we enter/leave each construct and for
// each BranchStmt we scan for the innermost target of the right type.
diff --git a/go/ssa/methods.go b/go/ssa/methods.go
index aedb41f..9af4261 100644
--- a/go/ssa/methods.go
+++ b/go/ssa/methods.go
@@ -98,6 +98,7 @@
id := sel.Obj().Id()
fn := mset.mapping[id]
if fn == nil {
+ sel := toSelection(sel)
obj := sel.Obj().(*types.Func)
needsPromotion := len(sel.Index()) > 1
diff --git a/go/ssa/ssa.go b/go/ssa/ssa.go
index c96ced2..cbc638c 100644
--- a/go/ssa/ssa.go
+++ b/go/ssa/ssa.go
@@ -307,7 +307,7 @@
type Function struct {
name string
object types.Object // a declared *types.Func or one of its wrappers
- method selection // info about provenance of synthetic methods
+ method *selection // info about provenance of synthetic methods; thunk => non-nil
Signature *types.Signature
pos token.Pos
diff --git a/go/ssa/wrappers.go b/go/ssa/wrappers.go
index f3305fc..8896beb 100644
--- a/go/ssa/wrappers.go
+++ b/go/ssa/wrappers.go
@@ -42,7 +42,7 @@
// - the result may be a thunk or a wrapper.
//
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
-func makeWrapper(prog *Program, sel selection, cr *creator) *Function {
+func makeWrapper(prog *Program, sel *selection, cr *creator) *Function {
obj := sel.Obj().(*types.Func) // the declared function
sig := sel.Type().(*types.Signature) // type of this wrapper
@@ -255,7 +255,7 @@
// than inlining the stub.
//
// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
-func makeThunk(prog *Program, sel selection, cr *creator) *Function {
+func makeThunk(prog *Program, sel *selection, cr *creator) *Function {
if sel.Kind() != types.MethodExpr {
panic(sel)
}
@@ -303,9 +303,10 @@
inst *typeList // canonical type instantiation list.
}
-// methodExpr is an copy of a *types.Selection.
-// This exists as there is no way to create MethodExpr's for an instantiation.
-type methodExpr struct {
+// A local version of *types.Selection.
+// Needed for some additional control, such as creating a MethodExpr for an instantiation.
+type selection struct {
+ kind types.SelectionKind
recv types.Type
typ types.Type
obj types.Object
@@ -313,33 +314,21 @@
indirect bool
}
-func (*methodExpr) Kind() types.SelectionKind { return types.MethodExpr }
-func (m *methodExpr) Type() types.Type { return m.typ }
-func (m *methodExpr) Recv() types.Type { return m.recv }
-func (m *methodExpr) Obj() types.Object { return m.obj }
-func (m *methodExpr) Index() []int { return m.index }
-func (m *methodExpr) Indirect() bool { return m.indirect }
+// TODO(taking): inline and eliminate.
+func (sel *selection) Kind() types.SelectionKind { return sel.kind }
+func (sel *selection) Type() types.Type { return sel.typ }
+func (sel *selection) Recv() types.Type { return sel.recv }
+func (sel *selection) Obj() types.Object { return sel.obj }
+func (sel *selection) Index() []int { return sel.index }
+func (sel *selection) Indirect() bool { return sel.indirect }
-// create MethodExpr from a MethodValue.
-func toMethodExpr(mv *types.Selection) *methodExpr {
- if mv.Kind() != types.MethodVal {
- panic(mv)
+func toSelection(sel *types.Selection) *selection {
+ return &selection{
+ kind: sel.Kind(),
+ recv: sel.Recv(),
+ typ: sel.Type(),
+ obj: sel.Obj(),
+ index: sel.Index(),
+ indirect: sel.Indirect(),
}
- return &methodExpr{
- recv: mv.Recv(),
- typ: recvAsFirstArg(mv.Type().(*types.Signature)),
- obj: mv.Obj(),
- index: mv.Index(),
- indirect: mv.Indirect(),
- }
-}
-
-// generalization of a *types.Selection and a methodExpr.
-type selection interface {
- Kind() types.SelectionKind
- Type() types.Type
- Recv() types.Type
- Obj() types.Object
- Index() []int
- Indirect() bool
}