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 }