src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

wrappers.go (10856B)


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