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