golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/go/ssa/wrappers.go (about)

     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, cr *creator) *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  	fn := &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  	cr.Add(fn)
    81  	return fn
    82  }
    83  
    84  // buildWrapper builds fn.Body for a method wrapper.
    85  func (b *builder) buildWrapper(fn *Function) {
    86  	var recv *types.Var // wrapper's receiver or thunk's params[0]
    87  	var start int       // first regular param
    88  	if fn.method.kind == types.MethodExpr {
    89  		recv = fn.Signature.Params().At(0)
    90  		start = 1
    91  	} else {
    92  		recv = fn.Signature.Recv()
    93  	}
    94  
    95  	fn.startBody()
    96  	fn.addSpilledParam(recv)
    97  	createParams(fn, start)
    98  
    99  	indices := fn.method.index
   100  
   101  	var v Value = fn.Locals[0] // spilled receiver
   102  	if isPointer(fn.method.recv) {
   103  		v = emitLoad(fn, v)
   104  
   105  		// For simple indirection wrappers, perform an informative nil-check:
   106  		// "value method (T).f called using nil *T pointer"
   107  		if len(indices) == 1 && !isPointer(recvType(fn.object)) {
   108  			var c Call
   109  			c.Call.Value = &Builtin{
   110  				name: "ssa:wrapnilchk",
   111  				sig: types.NewSignature(nil,
   112  					types.NewTuple(anonVar(fn.method.recv), anonVar(tString), anonVar(tString)),
   113  					types.NewTuple(anonVar(fn.method.recv)), false),
   114  			}
   115  			c.Call.Args = []Value{
   116  				v,
   117  				stringConst(typeparams.MustDeref(fn.method.recv).String()),
   118  				stringConst(fn.method.obj.Name()),
   119  			}
   120  			c.setType(v.Type())
   121  			v = fn.emit(&c)
   122  		}
   123  	}
   124  
   125  	// Invariant: v is a pointer, either
   126  	//   value of *A receiver param, or
   127  	// address of  A spilled receiver.
   128  
   129  	// We use pointer arithmetic (FieldAddr possibly followed by
   130  	// Load) in preference to value extraction (Field possibly
   131  	// preceded by Load).
   132  
   133  	v = emitImplicitSelections(fn, v, indices[:len(indices)-1], token.NoPos)
   134  
   135  	// Invariant: v is a pointer, either
   136  	//   value of implicit *C field, or
   137  	// address of implicit  C field.
   138  
   139  	var c Call
   140  	if r := recvType(fn.object); !types.IsInterface(r) { // concrete method
   141  		if !isPointer(r) {
   142  			v = emitLoad(fn, v)
   143  		}
   144  		c.Call.Value = fn.Prog.objectMethod(fn.object, b.created)
   145  		c.Call.Args = append(c.Call.Args, v)
   146  	} else {
   147  		c.Call.Method = fn.object
   148  		c.Call.Value = emitLoad(fn, v) // interface (possibly a typeparam)
   149  	}
   150  	for _, arg := range fn.Params[1:] {
   151  		c.Call.Args = append(c.Call.Args, arg)
   152  	}
   153  	emitTailCall(fn, &c)
   154  	fn.finishBody()
   155  }
   156  
   157  // createParams creates parameters for wrapper method fn based on its
   158  // Signature.Params, which do not include the receiver.
   159  // start is the index of the first regular parameter to use.
   160  func createParams(fn *Function, start int) {
   161  	tparams := fn.Signature.Params()
   162  	for i, n := start, tparams.Len(); i < n; i++ {
   163  		fn.addParamVar(tparams.At(i))
   164  	}
   165  }
   166  
   167  // -- bounds -----------------------------------------------------------
   168  
   169  // createBound returns a bound method wrapper (or "bound"), a synthetic
   170  // function that delegates to a concrete or interface method denoted
   171  // by obj.  The resulting function has no receiver, but has one free
   172  // variable which will be used as the method's receiver in the
   173  // tail-call.
   174  //
   175  // Use MakeClosure with such a wrapper to construct a bound method
   176  // closure.  e.g.:
   177  //
   178  //	type T int          or:  type T interface { meth() }
   179  //	func (t T) meth()
   180  //	var t T
   181  //	f := t.meth
   182  //	f() // calls t.meth()
   183  //
   184  // f is a closure of a synthetic wrapper defined as if by:
   185  //
   186  //	f := func() { return t.meth() }
   187  //
   188  // Unlike createWrapper, createBound need perform no indirection or field
   189  // selections because that can be done before the closure is
   190  // constructed.
   191  func createBound(prog *Program, obj *types.Func, cr *creator) *Function {
   192  	description := fmt.Sprintf("bound method wrapper for %s", obj)
   193  	if prog.mode&LogSource != 0 {
   194  		defer logStack("%s", description)()
   195  	}
   196  	/* bound method wrapper */
   197  	fn := &Function{
   198  		name:      obj.Name() + "$bound",
   199  		object:    obj,
   200  		Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver
   201  		Synthetic: description,
   202  		Prog:      prog,
   203  		pos:       obj.Pos(),
   204  		// wrappers have no syntax
   205  		build:     (*builder).buildBound,
   206  		syntax:    nil,
   207  		info:      nil,
   208  		goversion: "",
   209  	}
   210  	fn.FreeVars = []*FreeVar{{name: "recv", typ: recvType(obj), parent: fn}} // (cyclic)
   211  	cr.Add(fn)
   212  	return fn
   213  }
   214  
   215  // buildBound builds fn.Body for a bound method closure.
   216  func (b *builder) buildBound(fn *Function) {
   217  	fn.startBody()
   218  	createParams(fn, 0)
   219  	var c Call
   220  
   221  	recv := fn.FreeVars[0]
   222  	if !types.IsInterface(recvType(fn.object)) { // concrete
   223  		c.Call.Value = fn.Prog.objectMethod(fn.object, b.created)
   224  		c.Call.Args = []Value{recv}
   225  	} else {
   226  		c.Call.Method = fn.object
   227  		c.Call.Value = recv // interface (possibly a typeparam)
   228  	}
   229  	for _, arg := range fn.Params {
   230  		c.Call.Args = append(c.Call.Args, arg)
   231  	}
   232  	emitTailCall(fn, &c)
   233  	fn.finishBody()
   234  }
   235  
   236  // -- thunks -----------------------------------------------------------
   237  
   238  // createThunk returns a thunk, a synthetic function that delegates to a
   239  // concrete or interface method denoted by sel.obj.  The resulting
   240  // function has no receiver, but has an additional (first) regular
   241  // parameter.
   242  //
   243  // Precondition: sel.kind == types.MethodExpr.
   244  //
   245  //	type T int          or:  type T interface { meth() }
   246  //	func (t T) meth()
   247  //	f := T.meth
   248  //	var t T
   249  //	f(t) // calls t.meth()
   250  //
   251  // f is a synthetic wrapper defined as if by:
   252  //
   253  //	f := func(t T) { return t.meth() }
   254  func createThunk(prog *Program, sel *selection, cr *creator) *Function {
   255  	if sel.kind != types.MethodExpr {
   256  		panic(sel)
   257  	}
   258  
   259  	fn := createWrapper(prog, sel, cr)
   260  	if fn.Signature.Recv() != nil {
   261  		panic(fn) // unexpected receiver
   262  	}
   263  
   264  	return fn
   265  }
   266  
   267  func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
   268  	return types.NewSignature(recv, s.Params(), s.Results(), s.Variadic())
   269  }
   270  
   271  // A local version of *types.Selection.
   272  // Needed for some additional control, such as creating a MethodExpr for an instantiation.
   273  type selection struct {
   274  	kind     types.SelectionKind
   275  	recv     types.Type
   276  	typ      types.Type
   277  	obj      types.Object
   278  	index    []int
   279  	indirect bool
   280  }
   281  
   282  func toSelection(sel *types.Selection) *selection {
   283  	return &selection{
   284  		kind:     sel.Kind(),
   285  		recv:     sel.Recv(),
   286  		typ:      sel.Type(),
   287  		obj:      sel.Obj(),
   288  		index:    sel.Index(),
   289  		indirect: sel.Indirect(),
   290  	}
   291  }
   292  
   293  // -- instantiations --------------------------------------------------
   294  
   295  // buildInstantiationWrapper builds the body of an instantiation
   296  // wrapper fn. The body calls the original generic function,
   297  // bracketed by ChangeType conversions on its arguments and results.
   298  func (b *builder) buildInstantiationWrapper(fn *Function) {
   299  	orig := fn.topLevelOrigin
   300  	sig := fn.Signature
   301  
   302  	fn.startBody()
   303  	if sig.Recv() != nil {
   304  		fn.addParamVar(sig.Recv())
   305  	}
   306  	createParams(fn, 0)
   307  
   308  	// Create body. Add a call to origin generic function
   309  	// and make type changes between argument and parameters,
   310  	// as well as return values.
   311  	var c Call
   312  	c.Call.Value = orig
   313  	if res := orig.Signature.Results(); res.Len() == 1 {
   314  		c.typ = res.At(0).Type()
   315  	} else {
   316  		c.typ = res
   317  	}
   318  
   319  	// parameter of instance becomes an argument to the call
   320  	// to the original generic function.
   321  	argOffset := 0
   322  	for i, arg := range fn.Params {
   323  		var typ types.Type
   324  		if i == 0 && sig.Recv() != nil {
   325  			typ = orig.Signature.Recv().Type()
   326  			argOffset = 1
   327  		} else {
   328  			typ = orig.Signature.Params().At(i - argOffset).Type()
   329  		}
   330  		c.Call.Args = append(c.Call.Args, emitTypeCoercion(fn, arg, typ))
   331  	}
   332  
   333  	results := fn.emit(&c)
   334  	var ret Return
   335  	switch res := sig.Results(); res.Len() {
   336  	case 0:
   337  		// no results, do nothing.
   338  	case 1:
   339  		ret.Results = []Value{emitTypeCoercion(fn, results, res.At(0).Type())}
   340  	default:
   341  		for i := 0; i < sig.Results().Len(); i++ {
   342  			v := emitExtract(fn, results, i)
   343  			ret.Results = append(ret.Results, emitTypeCoercion(fn, v, res.At(i).Type()))
   344  		}
   345  	}
   346  
   347  	fn.emit(&ret)
   348  	fn.currentBlock = nil
   349  
   350  	fn.finishBody()
   351  }