github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/src/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 "fmt" 9 ) 10 11 // function literals aka closures 12 func closurehdr(ntype *Node) { 13 n := nod(OCLOSURE, nil, nil) 14 n.Func.Ntype = ntype 15 n.Func.Depth = funcdepth 16 n.Func.Outerfunc = Curfn 17 18 funchdr(n) 19 20 // steal ntype's argument names and 21 // leave a fresh copy in their place. 22 // references to these variables need to 23 // refer to the variables in the external 24 // function declared below; see walkclosure. 25 n.List.Set(ntype.List.Slice()) 26 27 n.Rlist.Set(ntype.Rlist.Slice()) 28 ntype.List.Set(nil) 29 ntype.Rlist.Set(nil) 30 for _, n1 := range n.List.Slice() { 31 name := n1.Left 32 if name != nil { 33 name = newname(name.Sym) 34 } 35 a := nod(ODCLFIELD, name, n1.Right) 36 a.Isddd = n1.Isddd 37 if name != nil { 38 name.Isddd = a.Isddd 39 } 40 ntype.List.Append(a) 41 } 42 for _, n2 := range n.Rlist.Slice() { 43 name := n2.Left 44 if name != nil { 45 name = newname(name.Sym) 46 } 47 ntype.Rlist.Append(nod(ODCLFIELD, name, n2.Right)) 48 } 49 } 50 51 func closurebody(body []*Node) *Node { 52 if len(body) == 0 { 53 body = []*Node{nod(OEMPTY, nil, nil)} 54 } 55 56 func_ := Curfn 57 func_.Nbody.Set(body) 58 func_.Func.Endlineno = lineno 59 funcbody(func_) 60 61 // closure-specific variables are hanging off the 62 // ordinary ones in the symbol table; see oldname. 63 // unhook them. 64 // make the list of pointers for the closure call. 65 for _, v := range func_.Func.Cvars.Slice() { 66 // Unlink from v1; see comment in syntax.go type Param for these fields. 67 v1 := v.Name.Defn 68 v1.Name.Param.Innermost = v.Name.Param.Outer 69 70 // If the closure usage of v is not dense, 71 // we need to make it dense; now that we're out 72 // of the function in which v appeared, 73 // look up v.Sym in the enclosing function 74 // and keep it around for use in the compiled code. 75 // 76 // That is, suppose we just finished parsing the innermost 77 // closure f4 in this code: 78 // 79 // func f() { 80 // v := 1 81 // func() { // f2 82 // use(v) 83 // func() { // f3 84 // func() { // f4 85 // use(v) 86 // }() 87 // }() 88 // }() 89 // } 90 // 91 // At this point v.Outer is f2's v; there is no f3's v. 92 // To construct the closure f4 from within f3, 93 // we need to use f3's v and in this case we need to create f3's v. 94 // We are now in the context of f3, so calling oldname(v.Sym) 95 // obtains f3's v, creating it if necessary (as it is in the example). 96 // 97 // capturevars will decide whether to use v directly or &v. 98 v.Name.Param.Outer = oldname(v.Sym) 99 } 100 101 return func_ 102 } 103 104 func typecheckclosure(func_ *Node, top int) { 105 for _, ln := range func_.Func.Cvars.Slice() { 106 n := ln.Name.Defn 107 if !n.Name.Captured { 108 n.Name.Captured = true 109 if n.Name.Decldepth == 0 { 110 Fatalf("typecheckclosure: var %S does not have decldepth assigned", n) 111 } 112 113 // Ignore assignments to the variable in straightline code 114 // preceding the first capturing by a closure. 115 if n.Name.Decldepth == decldepth { 116 n.Assigned = false 117 } 118 } 119 } 120 121 for _, ln := range func_.Func.Dcl { 122 if ln.Op == ONAME && (ln.Class == PPARAM || ln.Class == PPARAMOUT) { 123 ln.Name.Decldepth = 1 124 } 125 } 126 127 oldfn := Curfn 128 func_.Func.Ntype = typecheck(func_.Func.Ntype, Etype) 129 func_.Type = func_.Func.Ntype.Type 130 func_.Func.Top = top 131 132 // Type check the body now, but only if we're inside a function. 133 // At top level (in a variable initialization: curfn==nil) we're not 134 // ready to type check code yet; we'll check it later, because the 135 // underlying closure function we create is added to xtop. 136 if Curfn != nil && func_.Type != nil { 137 Curfn = func_ 138 olddd := decldepth 139 decldepth = 1 140 typecheckslice(func_.Nbody.Slice(), Etop) 141 decldepth = olddd 142 Curfn = oldfn 143 } 144 145 // Create top-level function 146 xtop = append(xtop, makeclosure(func_)) 147 } 148 149 // closurename returns name for OCLOSURE n. 150 // It is not as simple as it ought to be, because we typecheck nested closures 151 // starting from the innermost one. So when we check the inner closure, 152 // we don't yet have name for the outer closure. This function uses recursion 153 // to generate names all the way up if necessary. 154 155 var closurename_closgen int 156 157 func closurename(n *Node) *Sym { 158 if n.Sym != nil { 159 return n.Sym 160 } 161 gen := 0 162 outer := "" 163 prefix := "" 164 switch { 165 case n.Func.Outerfunc == nil: 166 // Global closure. 167 outer = "glob." 168 169 prefix = "func" 170 closurename_closgen++ 171 gen = closurename_closgen 172 case n.Func.Outerfunc.Op == ODCLFUNC: 173 // The outermost closure inside of a named function. 174 outer = n.Func.Outerfunc.Func.Nname.Sym.Name 175 176 prefix = "func" 177 178 // Yes, functions can be named _. 179 // Can't use function closgen in such case, 180 // because it would lead to name clashes. 181 if !isblank(n.Func.Outerfunc.Func.Nname) { 182 n.Func.Outerfunc.Func.Closgen++ 183 gen = n.Func.Outerfunc.Func.Closgen 184 } else { 185 closurename_closgen++ 186 gen = closurename_closgen 187 } 188 case n.Func.Outerfunc.Op == OCLOSURE: 189 // Nested closure, recurse. 190 outer = closurename(n.Func.Outerfunc).Name 191 192 prefix = "" 193 n.Func.Outerfunc.Func.Closgen++ 194 gen = n.Func.Outerfunc.Func.Closgen 195 default: 196 Fatalf("closurename called for %S", n) 197 } 198 n.Sym = lookupf("%s.%s%d", outer, prefix, gen) 199 return n.Sym 200 } 201 202 func makeclosure(func_ *Node) *Node { 203 // wrap body in external function 204 // that begins by reading closure parameters. 205 xtype := nod(OTFUNC, nil, nil) 206 207 xtype.List.Set(func_.List.Slice()) 208 xtype.Rlist.Set(func_.Rlist.Slice()) 209 210 // create the function 211 xfunc := nod(ODCLFUNC, nil, nil) 212 213 xfunc.Func.Nname = newfuncname(closurename(func_)) 214 xfunc.Func.Nname.Sym.Flags |= SymExported // disable export 215 xfunc.Func.Nname.Name.Param.Ntype = xtype 216 xfunc.Func.Nname.Name.Defn = xfunc 217 declare(xfunc.Func.Nname, PFUNC) 218 xfunc.Func.Nname.Name.Funcdepth = func_.Func.Depth 219 xfunc.Func.Depth = func_.Func.Depth 220 xfunc.Func.Endlineno = func_.Func.Endlineno 221 if Ctxt.Flag_dynlink { 222 makefuncsym(xfunc.Func.Nname.Sym) 223 } 224 225 xfunc.Nbody.Set(func_.Nbody.Slice()) 226 xfunc.Func.Dcl = append(func_.Func.Dcl, xfunc.Func.Dcl...) 227 func_.Func.Dcl = nil 228 if xfunc.Nbody.Len() == 0 { 229 Fatalf("empty body - won't generate any code") 230 } 231 xfunc = typecheck(xfunc, Etop) 232 233 xfunc.Func.Closure = func_ 234 func_.Func.Closure = xfunc 235 236 func_.Nbody.Set(nil) 237 func_.List.Set(nil) 238 func_.Rlist.Set(nil) 239 240 return xfunc 241 } 242 243 // capturevars is called in a separate phase after all typechecking is done. 244 // It decides whether each variable captured by a closure should be captured 245 // by value or by reference. 246 // We use value capturing for values <= 128 bytes that are never reassigned 247 // after capturing (effectively constant). 248 func capturevars(xfunc *Node) { 249 lno := lineno 250 lineno = xfunc.Lineno 251 252 func_ := xfunc.Func.Closure 253 func_.Func.Enter.Set(nil) 254 for _, v := range func_.Func.Cvars.Slice() { 255 if v.Type == nil { 256 // if v->type is nil, it means v looked like it was 257 // going to be used in the closure but wasn't. 258 // this happens because when parsing a, b, c := f() 259 // the a, b, c gets parsed as references to older 260 // a, b, c before the parser figures out this is a 261 // declaration. 262 v.Op = OXXX 263 264 continue 265 } 266 267 // type check the & of closed variables outside the closure, 268 // so that the outer frame also grabs them and knows they escape. 269 dowidth(v.Type) 270 271 outer := v.Name.Param.Outer 272 outermost := v.Name.Defn 273 274 // out parameters will be assigned to implicitly upon return. 275 if outer.Class != PPARAMOUT && !outermost.Addrtaken && !outermost.Assigned && v.Type.Width <= 128 { 276 v.Name.Byval = true 277 } else { 278 outermost.Addrtaken = true 279 outer = nod(OADDR, outer, nil) 280 } 281 282 if Debug['m'] > 1 { 283 var name *Sym 284 if v.Name.Curfn != nil && v.Name.Curfn.Func.Nname != nil { 285 name = v.Name.Curfn.Func.Nname.Sym 286 } 287 how := "ref" 288 if v.Name.Byval { 289 how = "value" 290 } 291 Warnl(v.Lineno, "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, outermost.Addrtaken, outermost.Assigned, int32(v.Type.Width)) 292 } 293 294 outer = typecheck(outer, Erv) 295 func_.Func.Enter.Append(outer) 296 } 297 298 lineno = lno 299 } 300 301 // transformclosure is called in a separate phase after escape analysis. 302 // It transform closure bodies to properly reference captured variables. 303 func transformclosure(xfunc *Node) { 304 lno := lineno 305 lineno = xfunc.Lineno 306 func_ := xfunc.Func.Closure 307 308 if func_.Func.Top&Ecall != 0 { 309 // If the closure is directly called, we transform it to a plain function call 310 // with variables passed as args. This avoids allocation of a closure object. 311 // Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE) 312 // will complete the transformation later. 313 // For illustration, the following closure: 314 // func(a int) { 315 // println(byval) 316 // byref++ 317 // }(42) 318 // becomes: 319 // func(a int, byval int, &byref *int) { 320 // println(byval) 321 // (*&byref)++ 322 // }(byval, &byref, 42) 323 324 // f is ONAME of the actual function. 325 f := xfunc.Func.Nname 326 327 // We are going to insert captured variables before input args. 328 var params []*Field 329 var decls []*Node 330 for _, v := range func_.Func.Cvars.Slice() { 331 if v.Op == OXXX { 332 continue 333 } 334 fld := newField() 335 fld.Funarg = FunargParams 336 if v.Name.Byval { 337 // If v is captured by value, we merely downgrade it to PPARAM. 338 v.Class = PPARAM 339 340 v.Ullman = 1 341 fld.Nname = v 342 } else { 343 // If v of type T is captured by reference, 344 // we introduce function param &v *T 345 // and v remains PAUTOHEAP with &v heapaddr 346 // (accesses will implicitly deref &v). 347 addr := newname(lookupf("&%s", v.Sym.Name)) 348 addr.Type = ptrto(v.Type) 349 addr.Class = PPARAM 350 v.Name.Heapaddr = addr 351 fld.Nname = addr 352 } 353 354 fld.Type = fld.Nname.Type 355 fld.Sym = fld.Nname.Sym 356 357 params = append(params, fld) 358 decls = append(decls, fld.Nname) 359 } 360 361 if len(params) > 0 { 362 // Prepend params and decls. 363 f.Type.Params().SetFields(append(params, f.Type.Params().FieldSlice()...)) 364 xfunc.Func.Dcl = append(decls, xfunc.Func.Dcl...) 365 } 366 367 // Recalculate param offsets. 368 if f.Type.Width > 0 { 369 Fatalf("transformclosure: width is already calculated") 370 } 371 dowidth(f.Type) 372 xfunc.Type = f.Type // update type of ODCLFUNC 373 } else { 374 // The closure is not called, so it is going to stay as closure. 375 var body []*Node 376 offset := int64(Widthptr) 377 for _, v := range func_.Func.Cvars.Slice() { 378 if v.Op == OXXX { 379 continue 380 } 381 382 // cv refers to the field inside of closure OSTRUCTLIT. 383 cv := nod(OCLOSUREVAR, nil, nil) 384 385 cv.Type = v.Type 386 if !v.Name.Byval { 387 cv.Type = ptrto(v.Type) 388 } 389 offset = Rnd(offset, int64(cv.Type.Align)) 390 cv.Xoffset = offset 391 offset += cv.Type.Width 392 393 if v.Name.Byval && v.Type.Width <= int64(2*Widthptr) { 394 // If it is a small variable captured by value, downgrade it to PAUTO. 395 v.Class = PAUTO 396 v.Ullman = 1 397 xfunc.Func.Dcl = append(xfunc.Func.Dcl, v) 398 body = append(body, nod(OAS, v, cv)) 399 } else { 400 // Declare variable holding addresses taken from closure 401 // and initialize in entry prologue. 402 addr := newname(lookupf("&%s", v.Sym.Name)) 403 addr.Name.Param.Ntype = nod(OIND, typenod(v.Type), nil) 404 addr.Class = PAUTO 405 addr.Used = true 406 addr.Name.Curfn = xfunc 407 xfunc.Func.Dcl = append(xfunc.Func.Dcl, addr) 408 v.Name.Heapaddr = addr 409 if v.Name.Byval { 410 cv = nod(OADDR, cv, nil) 411 } 412 body = append(body, nod(OAS, addr, cv)) 413 } 414 } 415 416 if len(body) > 0 { 417 typecheckslice(body, Etop) 418 walkstmtlist(body) 419 xfunc.Func.Enter.Set(body) 420 xfunc.Func.Needctxt = true 421 } 422 } 423 424 lineno = lno 425 } 426 427 // hasemptycvars returns true iff closure func_ has an 428 // empty list of captured vars. OXXX nodes don't count. 429 func hasemptycvars(func_ *Node) bool { 430 for _, v := range func_.Func.Cvars.Slice() { 431 if v.Op == OXXX { 432 continue 433 } 434 return false 435 } 436 return true 437 } 438 439 // closuredebugruntimecheck applies boilerplate checks for debug flags 440 // and compiling runtime 441 func closuredebugruntimecheck(r *Node) { 442 if Debug_closure > 0 { 443 if r.Esc == EscHeap { 444 Warnl(r.Lineno, "heap closure, captured vars = %v", r.Func.Cvars) 445 } else { 446 Warnl(r.Lineno, "stack closure, captured vars = %v", r.Func.Cvars) 447 } 448 } 449 if compiling_runtime && r.Esc == EscHeap { 450 yyerrorl(r.Lineno, "heap-allocated closure, not allowed in runtime.") 451 } 452 } 453 454 func walkclosure(func_ *Node, init *Nodes) *Node { 455 // If no closure vars, don't bother wrapping. 456 if hasemptycvars(func_) { 457 if Debug_closure > 0 { 458 Warnl(func_.Lineno, "closure converted to global") 459 } 460 return func_.Func.Closure.Func.Nname 461 } else { 462 closuredebugruntimecheck(func_) 463 } 464 465 // Create closure in the form of a composite literal. 466 // supposing the closure captures an int i and a string s 467 // and has one float64 argument and no results, 468 // the generated code looks like: 469 // 470 // clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s} 471 // 472 // The use of the struct provides type information to the garbage 473 // collector so that it can walk the closure. We could use (in this case) 474 // [3]unsafe.Pointer instead, but that would leave the gc in the dark. 475 // The information appears in the binary in the form of type descriptors; 476 // the struct is unnamed so that closures in multiple packages with the 477 // same struct type can share the descriptor. 478 479 typ := nod(OTSTRUCT, nil, nil) 480 481 typ.List.Set1(nod(ODCLFIELD, newname(lookup(".F")), typenod(Types[TUINTPTR]))) 482 for _, v := range func_.Func.Cvars.Slice() { 483 if v.Op == OXXX { 484 continue 485 } 486 typ1 := typenod(v.Type) 487 if !v.Name.Byval { 488 typ1 = nod(OIND, typ1, nil) 489 } 490 typ.List.Append(nod(ODCLFIELD, newname(v.Sym), typ1)) 491 } 492 493 clos := nod(OCOMPLIT, nil, nod(OIND, typ, nil)) 494 clos.Esc = func_.Esc 495 clos.Right.Implicit = true 496 clos.List.Set(append([]*Node{nod(OCFUNC, func_.Func.Closure.Func.Nname, nil)}, func_.Func.Enter.Slice()...)) 497 498 // Force type conversion from *struct to the func type. 499 clos = nod(OCONVNOP, clos, nil) 500 501 clos.Type = func_.Type 502 503 clos = typecheck(clos, Erv) 504 505 // typecheck will insert a PTRLIT node under CONVNOP, 506 // tag it with escape analysis result. 507 clos.Left.Esc = func_.Esc 508 509 // non-escaping temp to use, if any. 510 // orderexpr did not compute the type; fill it in now. 511 if x := prealloc[func_]; x != nil { 512 x.Type = clos.Left.Left.Type 513 x.Orig.Type = x.Type 514 clos.Left.Right = x 515 delete(prealloc, func_) 516 } 517 518 return walkexpr(clos, init) 519 } 520 521 func typecheckpartialcall(fn *Node, sym *Sym) { 522 switch fn.Op { 523 case ODOTINTER, ODOTMETH: 524 break 525 526 default: 527 Fatalf("invalid typecheckpartialcall") 528 } 529 530 // Create top-level function. 531 xfunc := makepartialcall(fn, fn.Type, sym) 532 fn.Func = xfunc.Func 533 fn.Right = newname(sym) 534 fn.Op = OCALLPART 535 fn.Type = xfunc.Type 536 } 537 538 var makepartialcall_gopkg *Pkg 539 540 func makepartialcall(fn *Node, t0 *Type, meth *Sym) *Node { 541 var p string 542 543 rcvrtype := fn.Left.Type 544 if exportname(meth.Name) { 545 p = fmt.Sprintf("(%-S).%s-fm", rcvrtype, meth.Name) 546 } else { 547 p = fmt.Sprintf("(%-S).(%-v)-fm", rcvrtype, meth) 548 } 549 basetype := rcvrtype 550 if rcvrtype.IsPtr() { 551 basetype = basetype.Elem() 552 } 553 if !basetype.IsInterface() && basetype.Sym == nil { 554 Fatalf("missing base type for %v", rcvrtype) 555 } 556 557 var spkg *Pkg 558 if basetype.Sym != nil { 559 spkg = basetype.Sym.Pkg 560 } 561 if spkg == nil { 562 if makepartialcall_gopkg == nil { 563 makepartialcall_gopkg = mkpkg("go") 564 } 565 spkg = makepartialcall_gopkg 566 } 567 568 sym := Pkglookup(p, spkg) 569 570 if sym.Flags&SymUniq != 0 { 571 return sym.Def 572 } 573 sym.Flags |= SymUniq 574 575 savecurfn := Curfn 576 Curfn = nil 577 578 xtype := nod(OTFUNC, nil, nil) 579 var l []*Node 580 var callargs []*Node 581 ddd := false 582 xfunc := nod(ODCLFUNC, nil, nil) 583 Curfn = xfunc 584 for i, t := range t0.Params().Fields().Slice() { 585 n := newname(lookupN("a", i)) 586 n.Class = PPARAM 587 xfunc.Func.Dcl = append(xfunc.Func.Dcl, n) 588 callargs = append(callargs, n) 589 fld := nod(ODCLFIELD, n, typenod(t.Type)) 590 if t.Isddd { 591 fld.Isddd = true 592 ddd = true 593 } 594 595 l = append(l, fld) 596 } 597 598 xtype.List.Set(l) 599 l = nil 600 var retargs []*Node 601 for i, t := range t0.Results().Fields().Slice() { 602 n := newname(lookupN("r", i)) 603 n.Class = PPARAMOUT 604 xfunc.Func.Dcl = append(xfunc.Func.Dcl, n) 605 retargs = append(retargs, n) 606 l = append(l, nod(ODCLFIELD, n, typenod(t.Type))) 607 } 608 609 xtype.Rlist.Set(l) 610 611 xfunc.Func.Dupok = true 612 xfunc.Func.Nname = newfuncname(sym) 613 xfunc.Func.Nname.Sym.Flags |= SymExported // disable export 614 xfunc.Func.Nname.Name.Param.Ntype = xtype 615 xfunc.Func.Nname.Name.Defn = xfunc 616 declare(xfunc.Func.Nname, PFUNC) 617 618 // Declare and initialize variable holding receiver. 619 620 xfunc.Func.Needctxt = true 621 cv := nod(OCLOSUREVAR, nil, nil) 622 cv.Xoffset = int64(Widthptr) 623 cv.Type = rcvrtype 624 if int(cv.Type.Align) > Widthptr { 625 cv.Xoffset = int64(cv.Type.Align) 626 } 627 ptr := nod(ONAME, nil, nil) 628 ptr.Sym = lookup("rcvr") 629 ptr.Class = PAUTO 630 ptr.Addable = true 631 ptr.Ullman = 1 632 ptr.Used = true 633 ptr.Name.Curfn = xfunc 634 ptr.Xoffset = 0 635 xfunc.Func.Dcl = append(xfunc.Func.Dcl, ptr) 636 var body []*Node 637 if rcvrtype.IsPtr() || rcvrtype.IsInterface() { 638 ptr.Name.Param.Ntype = typenod(rcvrtype) 639 body = append(body, nod(OAS, ptr, cv)) 640 } else { 641 ptr.Name.Param.Ntype = typenod(ptrto(rcvrtype)) 642 body = append(body, nod(OAS, ptr, nod(OADDR, cv, nil))) 643 } 644 645 call := nod(OCALL, nodSym(OXDOT, ptr, meth), nil) 646 call.List.Set(callargs) 647 call.Isddd = ddd 648 if t0.Results().NumFields() == 0 { 649 body = append(body, call) 650 } else { 651 n := nod(OAS2, nil, nil) 652 n.List.Set(retargs) 653 n.Rlist.Set1(call) 654 body = append(body, n) 655 n = nod(ORETURN, nil, nil) 656 body = append(body, n) 657 } 658 659 xfunc.Nbody.Set(body) 660 661 xfunc = typecheck(xfunc, Etop) 662 sym.Def = xfunc 663 xtop = append(xtop, xfunc) 664 Curfn = savecurfn 665 666 return xfunc 667 } 668 669 func walkpartialcall(n *Node, init *Nodes) *Node { 670 // Create closure in the form of a composite literal. 671 // For x.M with receiver (x) type T, the generated code looks like: 672 // 673 // clos = &struct{F uintptr; R T}{M.T·f, x} 674 // 675 // Like walkclosure above. 676 677 if n.Left.Type.IsInterface() { 678 // Trigger panic for method on nil interface now. 679 // Otherwise it happens in the wrapper and is confusing. 680 n.Left = cheapexpr(n.Left, init) 681 682 checknil(n.Left, init) 683 } 684 685 typ := nod(OTSTRUCT, nil, nil) 686 typ.List.Set1(nod(ODCLFIELD, newname(lookup("F")), typenod(Types[TUINTPTR]))) 687 typ.List.Append(nod(ODCLFIELD, newname(lookup("R")), typenod(n.Left.Type))) 688 689 clos := nod(OCOMPLIT, nil, nod(OIND, typ, nil)) 690 clos.Esc = n.Esc 691 clos.Right.Implicit = true 692 clos.List.Set1(nod(OCFUNC, n.Func.Nname, nil)) 693 clos.List.Append(n.Left) 694 695 // Force type conversion from *struct to the func type. 696 clos = nod(OCONVNOP, clos, nil) 697 698 clos.Type = n.Type 699 700 clos = typecheck(clos, Erv) 701 702 // typecheck will insert a PTRLIT node under CONVNOP, 703 // tag it with escape analysis result. 704 clos.Left.Esc = n.Esc 705 706 // non-escaping temp to use, if any. 707 // orderexpr did not compute the type; fill it in now. 708 if x := prealloc[n]; x != nil { 709 x.Type = clos.Left.Left.Type 710 x.Orig.Type = x.Type 711 clos.Left.Right = x 712 delete(prealloc, n) 713 } 714 715 return walkexpr(clos, init) 716 }