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