wrappers.go raw

   1  // Copyright 2013 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package ssa
   6  
   7  // This file defines synthesis of Functions that delegate to declared
   8  // methods; they come in three kinds:
   9  //
  10  // (1) wrappers: methods that wrap declared methods, performing
  11  //     implicit pointer indirections and embedded field selections.
  12  //
  13  // (2) thunks: funcs that wrap declared methods.  Like wrappers,
  14  //     thunks perform indirections and field selections. The thunk's
  15  //     first parameter is used as the receiver for the method call.
  16  //
  17  // (3) bounds: funcs that wrap declared methods.  The bound's sole
  18  //     free variable, supplied by a closure, is used as the receiver
  19  //     for the method call.  No indirections or field selections are
  20  //     performed since they can be done before the call.
  21  
  22  import (
  23  	"fmt"
  24  
  25  	"go/token"
  26  	"go/types"
  27  
  28  	"golang.org/x/tools/internal/typeparams"
  29  )
  30  
  31  // -- wrappers -----------------------------------------------------------
  32  
  33  // createWrapper returns a synthetic method that delegates to the
  34  // declared method denoted by meth.Obj(), first performing any
  35  // necessary pointer indirections or field selections implied by meth.
  36  //
  37  // The resulting method's receiver type is meth.Recv().
  38  //
  39  // This function is versatile but quite subtle!  Consider the
  40  // following axes of variation when making changes:
  41  //   - optional receiver indirection
  42  //   - optional implicit field selections
  43  //   - meth.Obj() may denote a concrete or an interface method
  44  //   - the result may be a thunk or a wrapper.
  45  func createWrapper(prog *Program, sel *selection) *Function {
  46  	obj := sel.obj.(*types.Func)      // the declared function
  47  	sig := sel.typ.(*types.Signature) // type of this wrapper
  48  
  49  	var recv *types.Var // wrapper's receiver or thunk's params[0]
  50  	name := obj.Name()
  51  	var description string
  52  	if sel.kind == types.MethodExpr {
  53  		name += "$thunk"
  54  		description = "thunk"
  55  		recv = sig.Params().At(0)
  56  	} else {
  57  		description = "wrapper"
  58  		recv = sig.Recv()
  59  	}
  60  
  61  	description = fmt.Sprintf("%s for %s", description, sel.obj)
  62  	if prog.mode&LogSource != 0 {
  63  		defer logStack("create %s to (%s)", description, recv.Type())()
  64  	}
  65  	/* method wrapper */
  66  	return &Function{
  67  		name:      name,
  68  		method:    sel,
  69  		object:    obj,
  70  		Signature: sig,
  71  		Synthetic: description,
  72  		Prog:      prog,
  73  		pos:       obj.Pos(),
  74  		// wrappers have no syntax
  75  		build:     (*builder).buildWrapper,
  76  		syntax:    nil,
  77  		info:      nil,
  78  		goversion: "",
  79  	}
  80  }
  81  
  82  // buildWrapper builds fn.Body for a method wrapper.
  83  func (b *builder) buildWrapper(fn *Function) {
  84  	var recv *types.Var // wrapper's receiver or thunk's params[0]
  85  	var start int       // first regular param
  86  	if fn.method.kind == types.MethodExpr {
  87  		recv = fn.Signature.Params().At(0)
  88  		start = 1
  89  	} else {
  90  		recv = fn.Signature.Recv()
  91  	}
  92  
  93  	fn.startBody()
  94  	fn.addSpilledParam(recv)
  95  	createParams(fn, start)
  96  
  97  	indices := fn.method.index
  98  
  99  	var v Value = fn.Locals[0] // spilled receiver
 100  	if isPointer(fn.method.recv) {
 101  		v = emitLoad(fn, v)
 102  
 103  		// For simple indirection wrappers, perform an informative nil-check:
 104  		// "value method (T).f called using nil *T pointer"
 105  		if len(indices) == 1 && !isPointer(recvType(fn.object)) {
 106  			var c Call
 107  			c.Call.Value = &Builtin{
 108  				name: "ssa:wrapnilchk",
 109  				sig:  types.NewSignatureType(nil, nil, nil, types.NewTuple(anonVar(fn.method.recv), anonVar(tString), anonVar(tString)), types.NewTuple(anonVar(fn.method.recv)), false),
 110  			}
 111  			c.Call.Args = []Value{
 112  				v,
 113  				stringConst(typeparams.MustDeref(fn.method.recv).String()),
 114  				stringConst(fn.method.obj.Name()),
 115  			}
 116  			c.setType(v.Type())
 117  			v = fn.emit(&c)
 118  		}
 119  	}
 120  
 121  	// Invariant: v is a pointer, either
 122  	//   value of *A receiver param, or
 123  	// address of  A spilled receiver.
 124  
 125  	// We use pointer arithmetic (FieldAddr possibly followed by
 126  	// Load) in preference to value extraction (Field possibly
 127  	// preceded by Load).
 128  
 129  	v = emitImplicitSelections(fn, v, indices[:len(indices)-1], token.NoPos)
 130  
 131  	// Invariant: v is a pointer, either
 132  	//   value of implicit *C field, or
 133  	// address of implicit  C field.
 134  
 135  	var c Call
 136  	if r := recvType(fn.object); !types.IsInterface(r) { // concrete method
 137  		if !isPointer(r) {
 138  			v = emitLoad(fn, v)
 139  		}
 140  		c.Call.Value = fn.Prog.objectMethod(fn.object, b)
 141  		c.Call.Args = append(c.Call.Args, v)
 142  	} else {
 143  		c.Call.Method = fn.object
 144  		c.Call.Value = emitLoad(fn, v) // interface (possibly a typeparam)
 145  	}
 146  	for _, arg := range fn.Params[1:] {
 147  		c.Call.Args = append(c.Call.Args, arg)
 148  	}
 149  	emitTailCall(fn, &c)
 150  	fn.finishBody()
 151  }
 152  
 153  // createParams creates parameters for wrapper method fn based on its
 154  // Signature.Params, which do not include the receiver.
 155  // start is the index of the first regular parameter to use.
 156  func createParams(fn *Function, start int) {
 157  	tparams := fn.Signature.Params()
 158  	for i, n := start, tparams.Len(); i < n; i++ {
 159  		fn.addParamVar(tparams.At(i))
 160  	}
 161  }
 162  
 163  // -- bounds -----------------------------------------------------------
 164  
 165  // createBound returns a bound method wrapper (or "bound"), a synthetic
 166  // function that delegates to a concrete or interface method denoted
 167  // by obj.  The resulting function has no receiver, but has one free
 168  // variable which will be used as the method's receiver in the
 169  // tail-call.
 170  //
 171  // Use MakeClosure with such a wrapper to construct a bound method
 172  // closure.  e.g.:
 173  //
 174  //	type T int          or:  type T interface { meth() }
 175  //	func (t T) meth()
 176  //	var t T
 177  //	f := t.meth
 178  //	f() // calls t.meth()
 179  //
 180  // f is a closure of a synthetic wrapper defined as if by:
 181  //
 182  //	f := func() { return t.meth() }
 183  //
 184  // Unlike createWrapper, createBound need perform no indirection or field
 185  // selections because that can be done before the closure is
 186  // constructed.
 187  func createBound(prog *Program, obj *types.Func) *Function {
 188  	description := fmt.Sprintf("bound method wrapper for %s", obj)
 189  	if prog.mode&LogSource != 0 {
 190  		defer logStack("%s", description)()
 191  	}
 192  	/* bound method wrapper */
 193  	fn := &Function{
 194  		name:      obj.Name() + "$bound",
 195  		object:    obj,
 196  		Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver
 197  		Synthetic: description,
 198  		Prog:      prog,
 199  		pos:       obj.Pos(),
 200  		// wrappers have no syntax
 201  		build:     (*builder).buildBound,
 202  		syntax:    nil,
 203  		info:      nil,
 204  		goversion: "",
 205  	}
 206  	fn.FreeVars = []*FreeVar{{name: "recv", typ: recvType(obj), parent: fn}} // (cyclic)
 207  	return fn
 208  }
 209  
 210  // buildBound builds fn.Body for a bound method closure.
 211  func (b *builder) buildBound(fn *Function) {
 212  	fn.startBody()
 213  	createParams(fn, 0)
 214  	var c Call
 215  
 216  	recv := fn.FreeVars[0]
 217  	if !types.IsInterface(recvType(fn.object)) { // concrete
 218  		c.Call.Value = fn.Prog.objectMethod(fn.object, b)
 219  		c.Call.Args = []Value{recv}
 220  	} else {
 221  		c.Call.Method = fn.object
 222  		c.Call.Value = recv // interface (possibly a typeparam)
 223  	}
 224  	for _, arg := range fn.Params {
 225  		c.Call.Args = append(c.Call.Args, arg)
 226  	}
 227  	emitTailCall(fn, &c)
 228  	fn.finishBody()
 229  }
 230  
 231  // -- thunks -----------------------------------------------------------
 232  
 233  // createThunk returns a thunk, a synthetic function that delegates to a
 234  // concrete or interface method denoted by sel.obj.  The resulting
 235  // function has no receiver, but has an additional (first) regular
 236  // parameter.
 237  //
 238  // Precondition: sel.kind == types.MethodExpr.
 239  //
 240  //	type T int          or:  type T interface { meth() }
 241  //	func (t T) meth()
 242  //	f := T.meth
 243  //	var t T
 244  //	f(t) // calls t.meth()
 245  //
 246  // f is a synthetic wrapper defined as if by:
 247  //
 248  //	f := func(t T) { return t.meth() }
 249  func createThunk(prog *Program, sel *selection) *Function {
 250  	if sel.kind != types.MethodExpr {
 251  		panic(sel)
 252  	}
 253  
 254  	fn := createWrapper(prog, sel)
 255  	if fn.Signature.Recv() != nil {
 256  		panic(fn) // unexpected receiver
 257  	}
 258  
 259  	return fn
 260  }
 261  
 262  func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
 263  	return types.NewSignatureType(recv, nil, nil, s.Params(), s.Results(), s.Variadic())
 264  }
 265  
 266  // A local version of *types.Selection.
 267  // Needed for some additional control, such as creating a MethodExpr for an instantiation.
 268  type selection struct {
 269  	kind     types.SelectionKind
 270  	recv     types.Type
 271  	typ      types.Type
 272  	obj      types.Object
 273  	index    []int
 274  	indirect bool
 275  }
 276  
 277  func toSelection(sel *types.Selection) *selection {
 278  	return &selection{
 279  		kind:     sel.Kind(),
 280  		recv:     sel.Recv(),
 281  		typ:      sel.Type(),
 282  		obj:      sel.Obj(),
 283  		index:    sel.Index(),
 284  		indirect: sel.Indirect(),
 285  	}
 286  }
 287  
 288  // -- instantiations --------------------------------------------------
 289  
 290  // buildInstantiationWrapper builds the body of an instantiation
 291  // wrapper fn. The body calls the original generic function,
 292  // bracketed by ChangeType conversions on its arguments and results.
 293  func (b *builder) buildInstantiationWrapper(fn *Function) {
 294  	orig := fn.topLevelOrigin
 295  	sig := fn.Signature
 296  
 297  	fn.startBody()
 298  	if sig.Recv() != nil {
 299  		fn.addParamVar(sig.Recv())
 300  	}
 301  	createParams(fn, 0)
 302  
 303  	// Create body. Add a call to origin generic function
 304  	// and make type changes between argument and parameters,
 305  	// as well as return values.
 306  	var c Call
 307  	c.Call.Value = orig
 308  	if res := orig.Signature.Results(); res.Len() == 1 {
 309  		c.typ = res.At(0).Type()
 310  	} else {
 311  		c.typ = res
 312  	}
 313  
 314  	// parameter of instance becomes an argument to the call
 315  	// to the original generic function.
 316  	argOffset := 0
 317  	for i, arg := range fn.Params {
 318  		var typ types.Type
 319  		if i == 0 && sig.Recv() != nil {
 320  			typ = orig.Signature.Recv().Type()
 321  			argOffset = 1
 322  		} else {
 323  			typ = orig.Signature.Params().At(i - argOffset).Type()
 324  		}
 325  		c.Call.Args = append(c.Call.Args, emitTypeCoercion(fn, arg, typ))
 326  	}
 327  
 328  	results := fn.emit(&c)
 329  	var ret Return
 330  	switch res := sig.Results(); res.Len() {
 331  	case 0:
 332  		// no results, do nothing.
 333  	case 1:
 334  		ret.Results = []Value{emitTypeCoercion(fn, results, res.At(0).Type())}
 335  	default:
 336  		for i := 0; i < sig.Results().Len(); i++ {
 337  			v := emitExtract(fn, results, i)
 338  			ret.Results = append(ret.Results, emitTypeCoercion(fn, v, res.At(i).Type()))
 339  		}
 340  	}
 341  
 342  	fn.emit(&ret)
 343  	fn.currentBlock = nil
 344  
 345  	fn.finishBody()
 346  }
 347