github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/compile/internal/gc/closure.go (about) 1 // Copyright 2009 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 gc 6 7 import ( 8 "github.com/gagliardetto/golang-go/cmd/compile/internal/syntax" 9 "github.com/gagliardetto/golang-go/cmd/compile/internal/types" 10 "fmt" 11 ) 12 13 func (p *noder) funcLit(expr *syntax.FuncLit) *Node { 14 xtype := p.typeExpr(expr.Type) 15 ntype := p.typeExpr(expr.Type) 16 17 xfunc := p.nod(expr, ODCLFUNC, nil, nil) 18 xfunc.Func.SetIsHiddenClosure(Curfn != nil) 19 xfunc.Func.Nname = newfuncnamel(p.pos(expr), nblank.Sym) // filled in by typecheckclosure 20 xfunc.Func.Nname.Name.Param.Ntype = xtype 21 xfunc.Func.Nname.Name.Defn = xfunc 22 23 clo := p.nod(expr, OCLOSURE, nil, nil) 24 clo.Func.Ntype = ntype 25 26 xfunc.Func.Closure = clo 27 clo.Func.Closure = xfunc 28 29 p.funcBody(xfunc, expr.Body) 30 31 // closure-specific variables are hanging off the 32 // ordinary ones in the symbol table; see oldname. 33 // unhook them. 34 // make the list of pointers for the closure call. 35 for _, v := range xfunc.Func.Cvars.Slice() { 36 // Unlink from v1; see comment in syntax.go type Param for these fields. 37 v1 := v.Name.Defn 38 v1.Name.Param.Innermost = v.Name.Param.Outer 39 40 // If the closure usage of v is not dense, 41 // we need to make it dense; now that we're out 42 // of the function in which v appeared, 43 // look up v.Sym in the enclosing function 44 // and keep it around for use in the compiled code. 45 // 46 // That is, suppose we just finished parsing the innermost 47 // closure f4 in this code: 48 // 49 // func f() { 50 // v := 1 51 // func() { // f2 52 // use(v) 53 // func() { // f3 54 // func() { // f4 55 // use(v) 56 // }() 57 // }() 58 // }() 59 // } 60 // 61 // At this point v.Outer is f2's v; there is no f3's v. 62 // To construct the closure f4 from within f3, 63 // we need to use f3's v and in this case we need to create f3's v. 64 // We are now in the context of f3, so calling oldname(v.Sym) 65 // obtains f3's v, creating it if necessary (as it is in the example). 66 // 67 // capturevars will decide whether to use v directly or &v. 68 v.Name.Param.Outer = oldname(v.Sym) 69 } 70 71 return clo 72 } 73 74 func typecheckclosure(clo *Node, top int) { 75 xfunc := clo.Func.Closure 76 // Set current associated iota value, so iota can be used inside 77 // function in ConstSpec, see issue #22344 78 if x := getIotaValue(); x >= 0 { 79 xfunc.SetIota(x) 80 } 81 82 clo.Func.Ntype = typecheck(clo.Func.Ntype, ctxType) 83 clo.Type = clo.Func.Ntype.Type 84 clo.Func.Top = top 85 86 // Do not typecheck xfunc twice, otherwise, we will end up pushing 87 // xfunc to xtop multiple times, causing initLSym called twice. 88 // See #30709 89 if xfunc.Typecheck() == 1 { 90 return 91 } 92 93 for _, ln := range xfunc.Func.Cvars.Slice() { 94 n := ln.Name.Defn 95 if !n.Name.Captured() { 96 n.Name.SetCaptured(true) 97 if n.Name.Decldepth == 0 { 98 Fatalf("typecheckclosure: var %S does not have decldepth assigned", n) 99 } 100 101 // Ignore assignments to the variable in straightline code 102 // preceding the first capturing by a closure. 103 if n.Name.Decldepth == decldepth { 104 n.Name.SetAssigned(false) 105 } 106 } 107 } 108 109 xfunc.Func.Nname.Sym = closurename(Curfn) 110 disableExport(xfunc.Func.Nname.Sym) 111 declare(xfunc.Func.Nname, PFUNC) 112 xfunc = typecheck(xfunc, ctxStmt) 113 114 // Type check the body now, but only if we're inside a function. 115 // At top level (in a variable initialization: curfn==nil) we're not 116 // ready to type check code yet; we'll check it later, because the 117 // underlying closure function we create is added to xtop. 118 if Curfn != nil && clo.Type != nil { 119 oldfn := Curfn 120 Curfn = xfunc 121 olddd := decldepth 122 decldepth = 1 123 typecheckslice(xfunc.Nbody.Slice(), ctxStmt) 124 decldepth = olddd 125 Curfn = oldfn 126 } 127 128 xtop = append(xtop, xfunc) 129 } 130 131 // globClosgen is like Func.Closgen, but for the global scope. 132 var globClosgen int 133 134 // closurename generates a new unique name for a closure within 135 // outerfunc. 136 func closurename(outerfunc *Node) *types.Sym { 137 outer := "glob." 138 prefix := "func" 139 gen := &globClosgen 140 141 if outerfunc != nil { 142 if outerfunc.Func.Closure != nil { 143 prefix = "" 144 } 145 146 outer = outerfunc.funcname() 147 148 // There may be multiple functions named "_". In those 149 // cases, we can't use their individual Closgens as it 150 // would lead to name clashes. 151 if !outerfunc.Func.Nname.isBlank() { 152 gen = &outerfunc.Func.Closgen 153 } 154 } 155 156 *gen++ 157 return lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen)) 158 } 159 160 // capturevarscomplete is set to true when the capturevars phase is done. 161 var capturevarscomplete bool 162 163 // capturevars is called in a separate phase after all typechecking is done. 164 // It decides whether each variable captured by a closure should be captured 165 // by value or by reference. 166 // We use value capturing for values <= 128 bytes that are never reassigned 167 // after capturing (effectively constant). 168 func capturevars(xfunc *Node) { 169 lno := lineno 170 lineno = xfunc.Pos 171 172 clo := xfunc.Func.Closure 173 cvars := xfunc.Func.Cvars.Slice() 174 out := cvars[:0] 175 for _, v := range cvars { 176 if v.Type == nil { 177 // If v.Type is nil, it means v looked like it 178 // was going to be used in the closure, but 179 // isn't. This happens in struct literals like 180 // s{f: x} where we can't distinguish whether 181 // f is a field identifier or expression until 182 // resolving s. 183 continue 184 } 185 out = append(out, v) 186 187 // type check the & of closed variables outside the closure, 188 // so that the outer frame also grabs them and knows they escape. 189 dowidth(v.Type) 190 191 outer := v.Name.Param.Outer 192 outermost := v.Name.Defn 193 194 // out parameters will be assigned to implicitly upon return. 195 if outermost.Class() != PPARAMOUT && !outermost.Name.Addrtaken() && !outermost.Name.Assigned() && v.Type.Width <= 128 { 196 v.Name.SetByval(true) 197 } else { 198 outermost.Name.SetAddrtaken(true) 199 outer = nod(OADDR, outer, nil) 200 } 201 202 if Debug['m'] > 1 { 203 var name *types.Sym 204 if v.Name.Curfn != nil && v.Name.Curfn.Func.Nname != nil { 205 name = v.Name.Curfn.Func.Nname.Sym 206 } 207 how := "ref" 208 if v.Name.Byval() { 209 how = "value" 210 } 211 Warnl(v.Pos, "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, outermost.Name.Addrtaken(), outermost.Name.Assigned(), int32(v.Type.Width)) 212 } 213 214 outer = typecheck(outer, ctxExpr) 215 clo.Func.Enter.Append(outer) 216 } 217 218 xfunc.Func.Cvars.Set(out) 219 lineno = lno 220 } 221 222 // transformclosure is called in a separate phase after escape analysis. 223 // It transform closure bodies to properly reference captured variables. 224 func transformclosure(xfunc *Node) { 225 lno := lineno 226 lineno = xfunc.Pos 227 clo := xfunc.Func.Closure 228 229 if clo.Func.Top&ctxCallee != 0 { 230 // If the closure is directly called, we transform it to a plain function call 231 // with variables passed as args. This avoids allocation of a closure object. 232 // Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE) 233 // will complete the transformation later. 234 // For illustration, the following closure: 235 // func(a int) { 236 // println(byval) 237 // byref++ 238 // }(42) 239 // becomes: 240 // func(byval int, &byref *int, a int) { 241 // println(byval) 242 // (*&byref)++ 243 // }(byval, &byref, 42) 244 245 // f is ONAME of the actual function. 246 f := xfunc.Func.Nname 247 248 // We are going to insert captured variables before input args. 249 var params []*types.Field 250 var decls []*Node 251 for _, v := range xfunc.Func.Cvars.Slice() { 252 if !v.Name.Byval() { 253 // If v of type T is captured by reference, 254 // we introduce function param &v *T 255 // and v remains PAUTOHEAP with &v heapaddr 256 // (accesses will implicitly deref &v). 257 addr := newname(lookup("&" + v.Sym.Name)) 258 addr.Type = types.NewPtr(v.Type) 259 v.Name.Param.Heapaddr = addr 260 v = addr 261 } 262 263 v.SetClass(PPARAM) 264 decls = append(decls, v) 265 266 fld := types.NewField() 267 fld.Nname = asTypesNode(v) 268 fld.Type = v.Type 269 fld.Sym = v.Sym 270 params = append(params, fld) 271 } 272 273 if len(params) > 0 { 274 // Prepend params and decls. 275 f.Type.Params().SetFields(append(params, f.Type.Params().FieldSlice()...)) 276 xfunc.Func.Dcl = append(decls, xfunc.Func.Dcl...) 277 } 278 279 dowidth(f.Type) 280 xfunc.Type = f.Type // update type of ODCLFUNC 281 } else { 282 // The closure is not called, so it is going to stay as closure. 283 var body []*Node 284 offset := int64(Widthptr) 285 for _, v := range xfunc.Func.Cvars.Slice() { 286 // cv refers to the field inside of closure OSTRUCTLIT. 287 cv := nod(OCLOSUREVAR, nil, nil) 288 289 cv.Type = v.Type 290 if !v.Name.Byval() { 291 cv.Type = types.NewPtr(v.Type) 292 } 293 offset = Rnd(offset, int64(cv.Type.Align)) 294 cv.Xoffset = offset 295 offset += cv.Type.Width 296 297 if v.Name.Byval() && v.Type.Width <= int64(2*Widthptr) { 298 // If it is a small variable captured by value, downgrade it to PAUTO. 299 v.SetClass(PAUTO) 300 xfunc.Func.Dcl = append(xfunc.Func.Dcl, v) 301 body = append(body, nod(OAS, v, cv)) 302 } else { 303 // Declare variable holding addresses taken from closure 304 // and initialize in entry prologue. 305 addr := newname(lookup("&" + v.Sym.Name)) 306 addr.Type = types.NewPtr(v.Type) 307 addr.SetClass(PAUTO) 308 addr.Name.SetUsed(true) 309 addr.Name.Curfn = xfunc 310 xfunc.Func.Dcl = append(xfunc.Func.Dcl, addr) 311 v.Name.Param.Heapaddr = addr 312 if v.Name.Byval() { 313 cv = nod(OADDR, cv, nil) 314 } 315 body = append(body, nod(OAS, addr, cv)) 316 } 317 } 318 319 if len(body) > 0 { 320 typecheckslice(body, ctxStmt) 321 xfunc.Func.Enter.Set(body) 322 xfunc.Func.SetNeedctxt(true) 323 } 324 } 325 326 lineno = lno 327 } 328 329 // hasemptycvars reports whether closure clo has an 330 // empty list of captured vars. 331 func hasemptycvars(clo *Node) bool { 332 xfunc := clo.Func.Closure 333 return xfunc.Func.Cvars.Len() == 0 334 } 335 336 // closuredebugruntimecheck applies boilerplate checks for debug flags 337 // and compiling runtime 338 func closuredebugruntimecheck(clo *Node) { 339 if Debug_closure > 0 { 340 xfunc := clo.Func.Closure 341 if clo.Esc == EscHeap { 342 Warnl(clo.Pos, "heap closure, captured vars = %v", xfunc.Func.Cvars) 343 } else { 344 Warnl(clo.Pos, "stack closure, captured vars = %v", xfunc.Func.Cvars) 345 } 346 } 347 if compiling_runtime && clo.Esc == EscHeap { 348 yyerrorl(clo.Pos, "heap-allocated closure, not allowed in runtime") 349 } 350 } 351 352 // closureType returns the struct type used to hold all the information 353 // needed in the closure for clo (clo must be a OCLOSURE node). 354 // The address of a variable of the returned type can be cast to a func. 355 func closureType(clo *Node) *types.Type { 356 // Create closure in the form of a composite literal. 357 // supposing the closure captures an int i and a string s 358 // and has one float64 argument and no results, 359 // the generated code looks like: 360 // 361 // clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s} 362 // 363 // The use of the struct provides type information to the garbage 364 // collector so that it can walk the closure. We could use (in this case) 365 // [3]unsafe.Pointer instead, but that would leave the gc in the dark. 366 // The information appears in the binary in the form of type descriptors; 367 // the struct is unnamed so that closures in multiple packages with the 368 // same struct type can share the descriptor. 369 fields := []*Node{ 370 namedfield(".F", types.Types[TUINTPTR]), 371 } 372 for _, v := range clo.Func.Closure.Func.Cvars.Slice() { 373 typ := v.Type 374 if !v.Name.Byval() { 375 typ = types.NewPtr(typ) 376 } 377 fields = append(fields, symfield(v.Sym, typ)) 378 } 379 typ := tostruct(fields) 380 typ.SetNoalg(true) 381 return typ 382 } 383 384 func walkclosure(clo *Node, init *Nodes) *Node { 385 xfunc := clo.Func.Closure 386 387 // If no closure vars, don't bother wrapping. 388 if hasemptycvars(clo) { 389 if Debug_closure > 0 { 390 Warnl(clo.Pos, "closure converted to global") 391 } 392 return xfunc.Func.Nname 393 } 394 closuredebugruntimecheck(clo) 395 396 typ := closureType(clo) 397 398 clos := nod(OCOMPLIT, nil, typenod(typ)) 399 clos.Esc = clo.Esc 400 clos.List.Set(append([]*Node{nod(OCFUNC, xfunc.Func.Nname, nil)}, clo.Func.Enter.Slice()...)) 401 402 clos = nod(OADDR, clos, nil) 403 clos.Esc = clo.Esc 404 405 // Force type conversion from *struct to the func type. 406 clos = convnop(clos, clo.Type) 407 408 // non-escaping temp to use, if any. 409 if x := prealloc[clo]; x != nil { 410 if !types.Identical(typ, x.Type) { 411 panic("closure type does not match order's assigned type") 412 } 413 clos.Left.Right = x 414 delete(prealloc, clo) 415 } 416 417 return walkexpr(clos, init) 418 } 419 420 func typecheckpartialcall(fn *Node, sym *types.Sym) { 421 switch fn.Op { 422 case ODOTINTER, ODOTMETH: 423 break 424 425 default: 426 Fatalf("invalid typecheckpartialcall") 427 } 428 429 // Create top-level function. 430 xfunc := makepartialcall(fn, fn.Type, sym) 431 fn.Func = xfunc.Func 432 fn.Right = newname(sym) 433 fn.Op = OCALLPART 434 fn.Type = xfunc.Type 435 } 436 437 func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node { 438 rcvrtype := fn.Left.Type 439 sym := methodSymSuffix(rcvrtype, meth, "-fm") 440 441 if sym.Uniq() { 442 return asNode(sym.Def) 443 } 444 sym.SetUniq(true) 445 446 savecurfn := Curfn 447 saveLineNo := lineno 448 Curfn = nil 449 450 // Set line number equal to the line number where the method is declared. 451 var m *types.Field 452 if lookdot0(meth, rcvrtype, &m, false) == 1 && m.Pos.IsKnown() { 453 lineno = m.Pos 454 } 455 // Note: !m.Pos.IsKnown() happens for method expressions where 456 // the method is implicitly declared. The Error method of the 457 // built-in error type is one such method. We leave the line 458 // number at the use of the method expression in this 459 // case. See issue 29389. 460 461 tfn := nod(OTFUNC, nil, nil) 462 tfn.List.Set(structargs(t0.Params(), true)) 463 tfn.Rlist.Set(structargs(t0.Results(), false)) 464 465 disableExport(sym) 466 xfunc := dclfunc(sym, tfn) 467 xfunc.Func.SetDupok(true) 468 xfunc.Func.SetNeedctxt(true) 469 470 tfn.Type.SetPkg(t0.Pkg()) 471 472 // Declare and initialize variable holding receiver. 473 474 cv := nod(OCLOSUREVAR, nil, nil) 475 cv.Type = rcvrtype 476 cv.Xoffset = Rnd(int64(Widthptr), int64(cv.Type.Align)) 477 478 ptr := newname(lookup(".this")) 479 declare(ptr, PAUTO) 480 ptr.Name.SetUsed(true) 481 var body []*Node 482 if rcvrtype.IsPtr() || rcvrtype.IsInterface() { 483 ptr.Type = rcvrtype 484 body = append(body, nod(OAS, ptr, cv)) 485 } else { 486 ptr.Type = types.NewPtr(rcvrtype) 487 body = append(body, nod(OAS, ptr, nod(OADDR, cv, nil))) 488 } 489 490 call := nod(OCALL, nodSym(OXDOT, ptr, meth), nil) 491 call.List.Set(paramNnames(tfn.Type)) 492 call.SetIsDDD(tfn.Type.IsVariadic()) 493 if t0.NumResults() != 0 { 494 n := nod(ORETURN, nil, nil) 495 n.List.Set1(call) 496 call = n 497 } 498 body = append(body, call) 499 500 xfunc.Nbody.Set(body) 501 funcbody() 502 503 xfunc = typecheck(xfunc, ctxStmt) 504 sym.Def = asTypesNode(xfunc) 505 xtop = append(xtop, xfunc) 506 Curfn = savecurfn 507 lineno = saveLineNo 508 509 return xfunc 510 } 511 512 // partialCallType returns the struct type used to hold all the information 513 // needed in the closure for n (n must be a OCALLPART node). 514 // The address of a variable of the returned type can be cast to a func. 515 func partialCallType(n *Node) *types.Type { 516 t := tostruct([]*Node{ 517 namedfield("F", types.Types[TUINTPTR]), 518 namedfield("R", n.Left.Type), 519 }) 520 t.SetNoalg(true) 521 return t 522 } 523 524 func walkpartialcall(n *Node, init *Nodes) *Node { 525 // Create closure in the form of a composite literal. 526 // For x.M with receiver (x) type T, the generated code looks like: 527 // 528 // clos = &struct{F uintptr; R T}{M.T·f, x} 529 // 530 // Like walkclosure above. 531 532 if n.Left.Type.IsInterface() { 533 // Trigger panic for method on nil interface now. 534 // Otherwise it happens in the wrapper and is confusing. 535 n.Left = cheapexpr(n.Left, init) 536 n.Left = walkexpr(n.Left, nil) 537 538 tab := nod(OITAB, n.Left, nil) 539 tab = typecheck(tab, ctxExpr) 540 541 c := nod(OCHECKNIL, tab, nil) 542 c.SetTypecheck(1) 543 init.Append(c) 544 } 545 546 typ := partialCallType(n) 547 548 clos := nod(OCOMPLIT, nil, typenod(typ)) 549 clos.Esc = n.Esc 550 clos.List.Set2(nod(OCFUNC, n.Func.Nname, nil), n.Left) 551 552 clos = nod(OADDR, clos, nil) 553 clos.Esc = n.Esc 554 555 // Force type conversion from *struct to the func type. 556 clos = convnop(clos, n.Type) 557 558 // non-escaping temp to use, if any. 559 if x := prealloc[n]; x != nil { 560 if !types.Identical(typ, x.Type) { 561 panic("partial call type does not match order's assigned type") 562 } 563 clos.Left.Right = x 564 delete(prealloc, n) 565 } 566 567 return walkexpr(clos, init) 568 }