Source file src/pkg/cmd/compile/internal/gc/closure.go
1
2
3
4
5 package gc
6
7 import (
8 "cmd/compile/internal/syntax"
9 "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)
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
32
33
34
35 for _, v := range xfunc.Func.Cvars.Slice() {
36
37 v1 := v.Name.Defn
38 v1.Name.Param.Innermost = v.Name.Param.Outer
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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 clo.Func.Ntype = typecheck(clo.Func.Ntype, Etype)
77 clo.Type = clo.Func.Ntype.Type
78 clo.Func.Top = top
79
80
81
82
83 if xfunc.Typecheck() == 1 {
84 return
85 }
86
87 for _, ln := range xfunc.Func.Cvars.Slice() {
88 n := ln.Name.Defn
89 if !n.Name.Captured() {
90 n.Name.SetCaptured(true)
91 if n.Name.Decldepth == 0 {
92 Fatalf("typecheckclosure: var %S does not have decldepth assigned", n)
93 }
94
95
96
97 if n.Name.Decldepth == decldepth {
98 n.SetAssigned(false)
99 }
100 }
101 }
102
103 xfunc.Func.Nname.Sym = closurename(Curfn)
104 disableExport(xfunc.Func.Nname.Sym)
105 declare(xfunc.Func.Nname, PFUNC)
106 xfunc = typecheck(xfunc, ctxStmt)
107
108
109
110
111
112 if Curfn != nil && clo.Type != nil {
113 oldfn := Curfn
114 Curfn = xfunc
115 olddd := decldepth
116 decldepth = 1
117 typecheckslice(xfunc.Nbody.Slice(), ctxStmt)
118 decldepth = olddd
119 Curfn = oldfn
120 }
121
122 xtop = append(xtop, xfunc)
123 }
124
125
126 var globClosgen int
127
128
129
130 func closurename(outerfunc *Node) *types.Sym {
131 outer := "glob."
132 prefix := "func"
133 gen := &globClosgen
134
135 if outerfunc != nil {
136 if outerfunc.Func.Closure != nil {
137 prefix = ""
138 }
139
140 outer = outerfunc.funcname()
141
142
143
144
145 if !outerfunc.Func.Nname.isBlank() {
146 gen = &outerfunc.Func.Closgen
147 }
148 }
149
150 *gen++
151 return lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen))
152 }
153
154
155 var capturevarscomplete bool
156
157
158
159
160
161
162 func capturevars(xfunc *Node) {
163 lno := lineno
164 lineno = xfunc.Pos
165
166 clo := xfunc.Func.Closure
167 cvars := xfunc.Func.Cvars.Slice()
168 out := cvars[:0]
169 for _, v := range cvars {
170 if v.Type == nil {
171
172
173
174
175
176
177 continue
178 }
179 out = append(out, v)
180
181
182
183 dowidth(v.Type)
184
185 outer := v.Name.Param.Outer
186 outermost := v.Name.Defn
187
188
189 if outermost.Class() != PPARAMOUT && !outermost.Addrtaken() && !outermost.Assigned() && v.Type.Width <= 128 {
190 v.Name.SetByval(true)
191 } else {
192 outermost.SetAddrtaken(true)
193 outer = nod(OADDR, outer, nil)
194 }
195
196 if Debug['m'] > 1 {
197 var name *types.Sym
198 if v.Name.Curfn != nil && v.Name.Curfn.Func.Nname != nil {
199 name = v.Name.Curfn.Func.Nname.Sym
200 }
201 how := "ref"
202 if v.Name.Byval() {
203 how = "value"
204 }
205 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))
206 }
207
208 outer = typecheck(outer, ctxExpr)
209 clo.Func.Enter.Append(outer)
210 }
211
212 xfunc.Func.Cvars.Set(out)
213 lineno = lno
214 }
215
216
217
218 func transformclosure(xfunc *Node) {
219 lno := lineno
220 lineno = xfunc.Pos
221 clo := xfunc.Func.Closure
222
223 if clo.Func.Top&ctxCallee != 0 {
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240 f := xfunc.Func.Nname
241
242
243 var params []*types.Field
244 var decls []*Node
245 for _, v := range xfunc.Func.Cvars.Slice() {
246 if !v.Name.Byval() {
247
248
249
250
251 addr := newname(lookup("&" + v.Sym.Name))
252 addr.Type = types.NewPtr(v.Type)
253 v.Name.Param.Heapaddr = addr
254 v = addr
255 }
256
257 v.SetClass(PPARAM)
258 decls = append(decls, v)
259
260 fld := types.NewField()
261 fld.Nname = asTypesNode(v)
262 fld.Type = v.Type
263 fld.Sym = v.Sym
264 params = append(params, fld)
265 }
266
267 if len(params) > 0 {
268
269 f.Type.Params().SetFields(append(params, f.Type.Params().FieldSlice()...))
270 xfunc.Func.Dcl = append(decls, xfunc.Func.Dcl...)
271 }
272
273 dowidth(f.Type)
274 xfunc.Type = f.Type
275 } else {
276
277 var body []*Node
278 offset := int64(Widthptr)
279 for _, v := range xfunc.Func.Cvars.Slice() {
280
281 cv := nod(OCLOSUREVAR, nil, nil)
282
283 cv.Type = v.Type
284 if !v.Name.Byval() {
285 cv.Type = types.NewPtr(v.Type)
286 }
287 offset = Rnd(offset, int64(cv.Type.Align))
288 cv.Xoffset = offset
289 offset += cv.Type.Width
290
291 if v.Name.Byval() && v.Type.Width <= int64(2*Widthptr) {
292
293 v.SetClass(PAUTO)
294 xfunc.Func.Dcl = append(xfunc.Func.Dcl, v)
295 body = append(body, nod(OAS, v, cv))
296 } else {
297
298
299 addr := newname(lookup("&" + v.Sym.Name))
300 addr.Type = types.NewPtr(v.Type)
301 addr.SetClass(PAUTO)
302 addr.Name.SetUsed(true)
303 addr.Name.Curfn = xfunc
304 xfunc.Func.Dcl = append(xfunc.Func.Dcl, addr)
305 v.Name.Param.Heapaddr = addr
306 if v.Name.Byval() {
307 cv = nod(OADDR, cv, nil)
308 }
309 body = append(body, nod(OAS, addr, cv))
310 }
311 }
312
313 if len(body) > 0 {
314 typecheckslice(body, ctxStmt)
315 xfunc.Func.Enter.Set(body)
316 xfunc.Func.SetNeedctxt(true)
317 }
318 }
319
320 lineno = lno
321 }
322
323
324
325 func hasemptycvars(clo *Node) bool {
326 xfunc := clo.Func.Closure
327 return xfunc.Func.Cvars.Len() == 0
328 }
329
330
331
332 func closuredebugruntimecheck(clo *Node) {
333 if Debug_closure > 0 {
334 xfunc := clo.Func.Closure
335 if clo.Esc == EscHeap {
336 Warnl(clo.Pos, "heap closure, captured vars = %v", xfunc.Func.Cvars)
337 } else {
338 Warnl(clo.Pos, "stack closure, captured vars = %v", xfunc.Func.Cvars)
339 }
340 }
341 if compiling_runtime && clo.Esc == EscHeap {
342 yyerrorl(clo.Pos, "heap-allocated closure, not allowed in runtime.")
343 }
344 }
345
346
347
348
349 func closureType(clo *Node) *types.Type {
350
351
352
353
354
355
356
357
358
359
360
361
362
363 fields := []*Node{
364 namedfield(".F", types.Types[TUINTPTR]),
365 }
366 for _, v := range clo.Func.Closure.Func.Cvars.Slice() {
367 typ := v.Type
368 if !v.Name.Byval() {
369 typ = types.NewPtr(typ)
370 }
371 fields = append(fields, symfield(v.Sym, typ))
372 }
373 typ := tostruct(fields)
374 typ.SetNoalg(true)
375 return typ
376 }
377
378 func walkclosure(clo *Node, init *Nodes) *Node {
379 xfunc := clo.Func.Closure
380
381
382 if hasemptycvars(clo) {
383 if Debug_closure > 0 {
384 Warnl(clo.Pos, "closure converted to global")
385 }
386 return xfunc.Func.Nname
387 }
388 closuredebugruntimecheck(clo)
389
390 typ := closureType(clo)
391
392 clos := nod(OCOMPLIT, nil, nod(ODEREF, typenod(typ), nil))
393 clos.Esc = clo.Esc
394 clos.Right.SetImplicit(true)
395 clos.List.Set(append([]*Node{nod(OCFUNC, xfunc.Func.Nname, nil)}, clo.Func.Enter.Slice()...))
396
397
398 clos = convnop(clos, clo.Type)
399
400
401
402 clos.Left.Esc = clo.Esc
403
404
405 if x := prealloc[clo]; x != nil {
406 if !types.Identical(typ, x.Type) {
407 panic("closure type does not match order's assigned type")
408 }
409 clos.Left.Right = x
410 delete(prealloc, clo)
411 }
412
413 return walkexpr(clos, init)
414 }
415
416 func typecheckpartialcall(fn *Node, sym *types.Sym) {
417 switch fn.Op {
418 case ODOTINTER, ODOTMETH:
419 break
420
421 default:
422 Fatalf("invalid typecheckpartialcall")
423 }
424
425
426 xfunc := makepartialcall(fn, fn.Type, sym)
427 fn.Func = xfunc.Func
428 fn.Right = newname(sym)
429 fn.Op = OCALLPART
430 fn.Type = xfunc.Type
431 }
432
433 func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node {
434 rcvrtype := fn.Left.Type
435 sym := methodSymSuffix(rcvrtype, meth, "-fm")
436
437 if sym.Uniq() {
438 return asNode(sym.Def)
439 }
440 sym.SetUniq(true)
441
442 savecurfn := Curfn
443 saveLineNo := lineno
444 Curfn = nil
445
446
447 var m *types.Field
448 if lookdot0(meth, rcvrtype, &m, false) == 1 && m.Pos.IsKnown() {
449 lineno = m.Pos
450 }
451
452
453
454
455
456
457 tfn := nod(OTFUNC, nil, nil)
458 tfn.List.Set(structargs(t0.Params(), true))
459 tfn.Rlist.Set(structargs(t0.Results(), false))
460
461 disableExport(sym)
462 xfunc := dclfunc(sym, tfn)
463 xfunc.Func.SetDupok(true)
464 xfunc.Func.SetNeedctxt(true)
465
466 tfn.Type.SetPkg(t0.Pkg())
467
468
469
470 cv := nod(OCLOSUREVAR, nil, nil)
471 cv.Type = rcvrtype
472 cv.Xoffset = Rnd(int64(Widthptr), int64(cv.Type.Align))
473
474 ptr := newname(lookup(".this"))
475 declare(ptr, PAUTO)
476 ptr.Name.SetUsed(true)
477 var body []*Node
478 if rcvrtype.IsPtr() || rcvrtype.IsInterface() {
479 ptr.Type = rcvrtype
480 body = append(body, nod(OAS, ptr, cv))
481 } else {
482 ptr.Type = types.NewPtr(rcvrtype)
483 body = append(body, nod(OAS, ptr, nod(OADDR, cv, nil)))
484 }
485
486 call := nod(OCALL, nodSym(OXDOT, ptr, meth), nil)
487 call.List.Set(paramNnames(tfn.Type))
488 call.SetIsDDD(tfn.Type.IsVariadic())
489 if t0.NumResults() != 0 {
490 n := nod(ORETURN, nil, nil)
491 n.List.Set1(call)
492 call = n
493 }
494 body = append(body, call)
495
496 xfunc.Nbody.Set(body)
497 funcbody()
498
499 xfunc = typecheck(xfunc, ctxStmt)
500 sym.Def = asTypesNode(xfunc)
501 xtop = append(xtop, xfunc)
502 Curfn = savecurfn
503 lineno = saveLineNo
504
505 return xfunc
506 }
507
508
509
510
511 func partialCallType(n *Node) *types.Type {
512 t := tostruct([]*Node{
513 namedfield("F", types.Types[TUINTPTR]),
514 namedfield("R", n.Left.Type),
515 })
516 t.SetNoalg(true)
517 return t
518 }
519
520 func walkpartialcall(n *Node, init *Nodes) *Node {
521
522
523
524
525
526
527
528 if n.Left.Type.IsInterface() {
529
530
531 n.Left = cheapexpr(n.Left, init)
532 n.Left = walkexpr(n.Left, nil)
533
534 tab := nod(OITAB, n.Left, nil)
535 tab = typecheck(tab, ctxExpr)
536
537 c := nod(OCHECKNIL, tab, nil)
538 c.SetTypecheck(1)
539 init.Append(c)
540 }
541
542 typ := partialCallType(n)
543
544 clos := nod(OCOMPLIT, nil, nod(ODEREF, typenod(typ), nil))
545 clos.Esc = n.Esc
546 clos.Right.SetImplicit(true)
547 clos.List.Set2(nod(OCFUNC, n.Func.Nname, nil), n.Left)
548
549
550 clos = convnop(clos, n.Type)
551
552
553
554 clos.Left.Esc = n.Esc
555
556
557 if x := prealloc[n]; x != nil {
558 if !types.Identical(typ, x.Type) {
559 panic("partial call type does not match order's assigned type")
560 }
561 clos.Left.Right = x
562 delete(prealloc, n)
563 }
564
565 return walkexpr(clos, init)
566 }
567
View as plain text