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