5151 s: SymId
5252 typ: SymId
5353 mode: EnvMode
54+ needsHeap: bool
5455
5556 EnvField = object
5657 objType: SymId
6263 typeCache: TypeCache
6364 thisModuleSuffix: string
6465 procStack: seq [SymId ]
65- closureProcs, createsEnv: HashSet [SymId ]
66+ closureProcs, createsEnv, escapes : HashSet [SymId ]
6667 localToEnv: Table [SymId , EnvField ]
6768 env: CurrentEnv
6869
@@ -80,19 +81,21 @@ proc trLocal(c: var Context; dest: var TokenBuf; n: var Cursor) =
8081 tr (c, dest, n)
8182
8283proc trProc (c: var Context ; dest: var TokenBuf ; n: var Cursor ) =
83- c.typeCache.openScope (ProcScope )
84+ # c.typeCache.openScope(ProcScope)
8485 copyInto dest, n:
8586 let symId = n.symId
8687 c.procStack.add (symId)
8788 var isConcrete = true # assume it is concrete
8889 for i in 0 ..< BodyPos :
8990 if i == ParamsPos :
91+ c.typeCache.openProcScope (symId, n)
9092 c.typeCache.registerParams (symId, n)
9193 elif i == TypeVarsPos :
9294 isConcrete = n.substructureKind != TypevarsU
9395 elif i == ProcPragmasPos :
9496 if hasPragma (n, ClosureP ):
9597 c.closureProcs.incl symId
98+ c.escapes.incl symId
9699 takeTree dest, n
97100 if isConcrete:
98101 tr (c, dest, n)
@@ -119,6 +122,17 @@ proc localToField(c: var Context; n: Cursor; local, typ: SymId): SymId =
119122 result = pool.syms.getOrIncl (name)
120123 c.localToEnv[local] = EnvField (objType: typ, field: result , typ: c.typeCache.getType (n))
121124
125+ proc trCall (c: var Context ; dest: var TokenBuf ; n: var Cursor ) =
126+ dest.add n
127+ inc n
128+ if n.kind == Symbol :
129+ # if a closure proc is called, we don't want to see it as "escaping".
130+ dest.add n
131+ inc n
132+ while n.kind != ParRi :
133+ tr (c, dest, n)
134+ dest.takeParRi (n)
135+
122136proc tr (c: var Context ; dest: var TokenBuf ; n: var Cursor ) =
123137 case n.kind
124138 of DotToken , UnknownToken , EofToken , Ident , SymbolDef ,
@@ -158,6 +172,11 @@ proc tr(c: var Context; dest: var TokenBuf; n: var Cursor) =
158172 inc n
159173 else :
160174 takeTree dest, n
175+ elif loc.kind in {ProcY , FuncY , IteratorY , ConverterY , MethodY }:
176+ # usage of a closure proc not within a call? --> The closure does escape:
177+ if c.procStack.len > 0 :
178+ c.escapes.incl n.symId
179+ takeTree dest, n
161180 else :
162181 takeTree dest, n
163182 of ParLe :
@@ -176,7 +195,10 @@ proc tr(c: var Context; dest: var TokenBuf; n: var Cursor) =
176195 trSons (c, dest, n)
177196 c.typeCache.closeScope ()
178197 else :
179- if n.exprKind == TypeofX :
198+ case n.exprKind
199+ of CallKinds :
200+ trCall c, dest, n
201+ of TypeofX :
180202 takeTree dest, n
181203 else :
182204 trSons (c, dest, n)
@@ -214,8 +236,11 @@ proc untypedEnv(dest: var TokenBuf; info: PackedLineInfo; env: CurrentEnv) =
214236 assert env.s != SymId (0 )
215237 case env.mode
216238 of EnvIsLocal :
217- dest.copyIntoKind CastX , info:
218- dest.addRootRef info
239+ if env.needsHeap:
240+ dest.copyIntoKind CastX , info:
241+ dest.addRootRef info
242+ dest.addSymUse env.s, info
243+ else :
219244 dest.addSymUse env.s, info
220245 of EnvIsParam :
221246 # the parameter already has the erased type:
@@ -228,10 +253,13 @@ proc typedEnv(dest: var TokenBuf; info: PackedLineInfo; env: CurrentEnv) =
228253 # the local already has the full type:
229254 dest.addSymUse env.s, info
230255 of EnvIsParam :
231- # the parameter has the erased type:
232- dest.copyIntoKind CastX , info:
233- dest.copyIntoKind RefT , info:
234- dest.addSymUse env.typ, info
256+ if env.needsHeap:
257+ # the parameter has the erased type:
258+ dest.copyIntoKind CastX , info:
259+ dest.copyIntoKind RefT , info:
260+ dest.addSymUse env.typ, info
261+ dest.addSymUse env.s, info
262+ else :
235263 dest.addSymUse env.s, info
236264
237265proc tre (c: var Context ; dest: var TokenBuf ; n: var Cursor )
@@ -258,7 +286,10 @@ proc treLocal(c: var Context; dest: var TokenBuf; n: var Cursor) =
258286 # generate an assignment:
259287 dest.copyIntoKind AsgnS , info:
260288 dest.copyIntoKind DotX , info:
261- dest.copyIntoKind DerefX , info:
289+ if c.env.needsHeap:
290+ dest.copyIntoKind DerefX , info:
291+ dest.typedEnv info, c.env
292+ else :
262293 dest.typedEnv info, c.env
263294 dest.addSymUse fld.field, info
264295 tre c, dest, n # value
@@ -273,16 +304,20 @@ proc treLocal(c: var Context; dest: var TokenBuf; n: var Cursor) =
273304 tre c, dest, n # type (might grow an environment parameter)
274305 tre c, dest, n # value
275306
276- proc addEnvParam (dest: var TokenBuf ; info: PackedLineInfo ) =
307+ proc addEnvParam (dest: var TokenBuf ; info: PackedLineInfo ; envTyp: SymId ) =
277308 dest.copyIntoKind ParamU , info:
278309 dest.addSymDef pool.syms.getOrIncl (EnvParamName ), info
279310 dest.addDotToken () # no export marker
280311 dest.addDotToken () # no pragmas
281- dest.copyIntoKind RefT , info:
282- dest.addSymUse pool.syms.getOrIncl (RootObjName ), info
312+ if envTyp == SymId (0 ):
313+ dest.copyIntoKind RefT , info:
314+ dest.addSymUse pool.syms.getOrIncl (RootObjName ), info
315+ else :
316+ dest.copyIntoKind PtrT , info:
317+ dest.addSymUse envTyp, info
283318 dest.addDotToken () # no default value
284319
285- proc treParams (c: var Context ; dest, init: var TokenBuf ; n: var Cursor ; addEnvParam : bool ) =
320+ proc treParams (c: var Context ; dest, init: var TokenBuf ; n: var Cursor ; doAddEnvParam : bool ; envTyp: SymId ) =
286321 copyInto dest, n:
287322 while n.kind != ParRi :
288323 assert n.substructureKind == ParamU
@@ -301,43 +336,54 @@ proc treParams(c: var Context; dest, init: var TokenBuf; n: var Cursor; addEnvPa
301336 # XXX Check here for memory safety violations: Cannot capture a `var T` parameter
302337 init.copyIntoKind AsgnS , n.info:
303338 init.copyIntoKind DotX , n.info:
304- init.copyIntoKind DerefX , n.info:
339+ if envTyp != SymId (0 ):
340+ init.copyIntoKind DerefX , n.info:
341+ init.typedEnv n.info, c.env
342+ else :
305343 init.typedEnv n.info, c.env
306344 init.addSymUse fld.field, n.info
307345 init.addSymUse name, n.info
308346
309- if addEnvParam :
310- addEnvParam dest, n.info
347+ if doAddEnvParam :
348+ addEnvParam dest, n.info, envTyp
311349
312- proc treProcBody (c: var Context ; dest, init: var TokenBuf ; n: var Cursor ; sym: SymId ) =
350+ proc treProcBody (c: var Context ; dest, init: var TokenBuf ; n: var Cursor ; sym: SymId ; needsHeap: bool ) =
313351 if n.stmtKind == StmtsS :
314352 copyInto dest, n:
315353 let oldEnv = c.env
316354 if c.createsEnv.contains (sym):
317- c.env = CurrentEnv (s: pool.syms.getOrIncl (EnvLocalName ), mode: EnvIsLocal , typ: c.envTypeForProc (sym))
355+ let envTyp = c.envTypeForProc (sym)
356+ c.env = CurrentEnv (s: pool.syms.getOrIncl (EnvLocalName ), mode: EnvIsLocal , typ: envTyp)
318357 dest.copyIntoKind VarS , NoLineInfo :
319358 dest.addSymDef c.env.s, NoLineInfo
320359 dest.addDotToken () # no export marker
321360 dest.addDotToken () # no pragmas
322- dest.copyIntoKind RefT , NoLineInfo :
323- dest.addSymUse c.env.typ, NoLineInfo
324- dest.copyIntoKind NewobjX , NoLineInfo :
361+ if needsHeap:
325362 dest.copyIntoKind RefT , NoLineInfo :
326363 dest.addSymUse c.env.typ, NoLineInfo
364+ dest.copyIntoKind NewobjX , NoLineInfo :
365+ dest.copyIntoKind RefT , NoLineInfo :
366+ dest.addSymUse c.env.typ, NoLineInfo
367+ else :
368+ dest.addSymUse c.env.typ, NoLineInfo
369+ dest.addDotToken () # no default value
327370 # init the environment via the `=wasMoved` hooks:
328371 for _, field in c.localToEnv:
329372 if field.objType == c.env.typ:
330373 dest.copyIntoKind WasmovedX , NoLineInfo :
331374 dest.copyIntoKind HaddrX , NoLineInfo :
332375 dest.copyIntoKind DotX , NoLineInfo :
333- dest.copyIntoKind DerefX , NoLineInfo :
376+ if needsHeap:
377+ dest.copyIntoKind DerefX , NoLineInfo :
378+ dest.addSymUse c.env.s, NoLineInfo
379+ else :
334380 dest.addSymUse c.env.s, NoLineInfo
335381 dest.addSymUse field.field, NoLineInfo
336382
337383 elif c.closureProcs.contains (sym):
338- c.env = CurrentEnv (s: pool.syms.getOrIncl (EnvParamName ), mode: EnvIsParam , typ: c.envTypeForProc (sym))
384+ c.env = CurrentEnv (s: pool.syms.getOrIncl (EnvParamName ), mode: EnvIsParam , typ: c.envTypeForProc (sym), needsHeap: needsHeap )
339385 else :
340- c.env = CurrentEnv (s: SymId (0 ), mode: EnvIsParam , typ: SymId (0 ))
386+ c.env = CurrentEnv (s: SymId (0 ), mode: EnvIsParam , typ: SymId (0 ), needsHeap: needsHeap )
341387 dest.add init
342388 while n.kind != ParRi :
343389 tre (c, dest, n)
@@ -350,26 +396,31 @@ proc treProc(c: var Context; dest: var TokenBuf; n: var Cursor) =
350396 copyInto dest, n:
351397 var isConcrete = true # assume it is concrete
352398 let sym = n.symId
399+ c.procStack.add (sym)
400+ let closureOwner = c.procStack[0 ]
401+ let needsHeap = c.escapes.contains (closureOwner)
353402 for i in 0 ..< BodyPos :
354403 if i == ParamsPos :
355404 c.typeCache.openProcScope (sym, n)
356- treParams c, dest, init, n, c.closureProcs.contains (sym)
405+ let envType = if needsHeap: SymId (0 ) else : c.envTypeForProc (closureOwner)
406+ treParams c, dest, init, n, c.closureProcs.contains (sym), envType
357407 else :
358408 if i == TypeVarsPos :
359409 isConcrete = n.substructureKind != TypevarsU
360410 takeTree dest, n
361411
362412 if isConcrete:
363- treProcBody (c, dest, init, n, sym)
413+ treProcBody (c, dest, init, n, sym, needsHeap )
364414 else :
365415 takeTree dest, n
416+ discard c.procStack.pop ()
366417 c.typeCache.closeScope ()
367418
368419proc treParamsWithEnv (c: var Context ; dest: var TokenBuf ; n: var Cursor ) =
369420 copyInto dest, n:
370421 while n.kind != ParRi :
371422 tre (c, dest, n)
372- addEnvParam dest, NoLineInfo
423+ addEnvParam dest, NoLineInfo , SymId ( 0 )
373424
374425proc isStaticCall (c: var Context ;s: SymId ): bool =
375426 let res = tryLoadSym (s)
@@ -419,8 +470,12 @@ proc genCall(c: var Context; dest: var TokenBuf; n: var Cursor) =
419470 if wantsEnv:
420471 if isStatic:
421472 if c.env.s != SymId (0 ):
422- # use the current environment as the last parameter:
423- untypedEnv dest, info, c.env
473+ if c.env.needsHeap:
474+ # use the current environment as the last parameter:
475+ untypedEnv dest, info, c.env
476+ else :
477+ dest.copyIntoKind AddrX , info:
478+ untypedEnv dest, info, c.env
424479 else :
425480 # can happen for toplevel closures that have been declared .closure for interop
426481 # We have no environment here, so pass `nil` instead:
@@ -444,14 +499,14 @@ proc treProcType(c: var Context; dest: var TokenBuf; n: var Cursor) =
444499 let usesWrapper = n.typeKind == ProctypeT
445500 if usesWrapper:
446501 inc n
447- for i in 1 .. 4 : dest. takeTree n
502+ for i in 1 .. 4 : skip n
448503 if n.typeKind == ParamsT :
449504 treParamsWithEnv (c, dest, n)
450505 else :
451506 assert n.kind == DotToken
452507 inc n
453508 dest.addParLe ParamsT , info
454- addEnvParam dest, info
509+ addEnvParam dest, info, SymId ( 0 )
455510 dest.addParRi ()
456511 dest.takeTree n # return type
457512 # pragmas:
@@ -517,9 +572,13 @@ proc tre(c: var Context; dest: var TokenBuf; n: var Cursor) =
517572 inc n
518573 dest.copyIntoKind DotX , info:
519574 dest.copyIntoKind DerefX , info:
520- dest.copyIntoKind CastX , info:
521- dest.copyIntoKind RefT , info:
522- dest.takeTree n # type
575+ if c.env.needsHeap:
576+ dest.copyIntoKind CastX , info:
577+ dest.copyIntoKind RefT , info:
578+ dest.takeTree n # type
579+ dest.addSymUse c.env.s, info
580+ else :
581+ skip n # type
523582 dest.addSymUse c.env.s, info
524583 assert n.kind == Symbol
525584 dest.takeTree n # the symbol
0 commit comments