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
 }