explicit ID generation for easier IC (#15559)

* refactoring: idents don't need inheritance
* refactoring: adding an IdGenerator (part 1)
* refactoring: adding an IdGenerator (part 2)
* refactoring: adding an IdGenerator (part 3)
* refactoring: adding an IdGenerator (part 4)
* refactoring: adding an IdGenerator (part 5)
* refactoring: adding an IdGenerator (part 5)
* IdGenerator must be a ref type; hello world works again
* make bootstrapping work again
* progress: add back the 'exactReplica' ideas
* added back the missing exactReplica hacks
* make tcompilerapi work again
* make important packages green
* attempt to fix the build for 32 bit machines (probably need a better solution here)
This commit is contained in:
Andreas Rumpf 2020-10-25 08:50:47 +01:00 • committed by GitHub
commit 226595515c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
67 changed files with 853 additions and 903 deletions

View file

@ -126,32 +126,32 @@ proc newCall(a: PSym, b: PNode): PNode =
result.add newSymNode(a)
result.add b
proc createClosureIterStateType*(g: ModuleGraph; iter: PSym): PType =
proc createClosureIterStateType*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PType =
var n = newNodeI(nkRange, iter.info)
n.add newIntNode(nkIntLit, -1)
n.add newIntNode(nkIntLit, 0)
result = newType(tyRange, iter)
result = newType(tyRange, nextId(idgen), iter)
result.n = n
var intType = nilOrSysInt(g)
if intType.isNil: intType = newType(tyInt, iter)
if intType.isNil: intType = newType(tyInt, nextId(idgen), iter)
rawAddSon(result, intType)
proc createStateField(g: ModuleGraph; iter: PSym): PSym =
result = newSym(skField, getIdent(g.cache, ":state"), iter, iter.info)
result.typ = createClosureIterStateType(g, iter)
proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
result = newSym(skField, getIdent(g.cache, ":state"), nextId(idgen), iter, iter.info)
result.typ = createClosureIterStateType(g, iter, idgen)
proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType =
proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType =
# YYY meh, just add the state field for every closure for now, it's too
# hard to figure out if it comes from a closure iterator:
result = createObj(g, owner, info, final=false)
rawAddField(result, createStateField(g, owner))
result = createObj(g, idgen, owner, info, final=false)
rawAddField(result, createStateField(g, owner, idgen))
proc getClosureIterResult*(g: ModuleGraph; iter: PSym): PSym =
proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
if resultPos < iter.ast.len:
result = iter.ast[resultPos].sym
else:
# XXX a bit hacky:
result = newSym(skResult, getIdent(g.cache, ":result"), iter, iter.info, {})
result = newSym(skResult, getIdent(g.cache, ":result"), nextId(idgen), iter, iter.info, {})
result.typ = iter.typ[0]
incl(result.flags, sfUsed)
iter.ast.add newSymNode(result)
@ -209,7 +209,7 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
result[0] = le
result[1] = ri
proc makeClosure*(g: ModuleGraph; prc: PSym; env: PNode; info: TLineInfo): PNode =
proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; info: TLineInfo): PNode =
result = newNodeIT(nkClosure, info, prc.typ)
result.add(newSymNode(prc))
if env == nil:
@ -219,7 +219,7 @@ proc makeClosure*(g: ModuleGraph; prc: PSym; env: PNode; info: TLineInfo): PNode
localError(g.config, info, "internal error: taking closure of closure")
result.add(env)
#if isClosureIterator(result.typ):
createTypeBoundOps(g, nil, result.typ, info)
createTypeBoundOps(g, nil, result.typ, info, idgen)
if tfHasAsgn in result.typ.flags or optSeqDestructors in g.config.globalOptions:
prc.flags.incl sfInjectDestructors
@ -237,13 +237,13 @@ proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} =
let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
result = conf.backend == backendJs and not isCompileTime
proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; owner: PSym) =
createTypeBoundOps(g, nil, refType.lastSon, info)
createTypeBoundOps(g, nil, refType, info)
proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen: IdGenerator; owner: PSym) =
createTypeBoundOps(g, nil, refType.lastSon, info, idgen)
createTypeBoundOps(g, nil, refType, info, idgen)
if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
owner.flags.incl sfInjectDestructors
proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
if liftingHarmful(g.config, owner): return n
let iter = n.sym
@ -255,10 +255,10 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
var env: PNode
if owner.isIterator:
let it = getHiddenParam(g, owner)
addUniqueField(it.typ.skipTypes({tyOwned})[0], hp, g.cache)
addUniqueField(it.typ.skipTypes({tyOwned})[0], hp, g.cache, idgen)
env = indirectAccess(newSymNode(it), hp, hp.info)
else:
let e = newSym(skLet, iter.name, owner, n.info)
let e = newSym(skLet, iter.name, nextId(idgen), owner, n.info)
e.typ = hp.typ
e.flags = hp.flags
env = newSymNode(e)
@ -267,13 +267,13 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
result.add(v)
# add 'new' statement:
result.add newCall(getSysSym(g, n.info, "internalNew"), env)
createTypeBoundOpsLL(g, env.typ, n.info, owner)
result.add makeClosure(g, iter, env, n.info)
createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner)
result.add makeClosure(g, idgen, iter, env, n.info)
proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode =
let envParam = getHiddenParam(g, owner)
let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr})
addField(obj, s, g.cache)
addField(obj, s, g.cache, idgen)
var access = newSymNode(envParam)
assert obj.kind == tyObject
@ -305,13 +305,15 @@ type
ownerToType: Table[int, PType]
somethingToDo: bool
graph: ModuleGraph
idgen: IdGenerator
proc initDetectionPass(g: ModuleGraph; fn: PSym): DetectionPass =
proc initDetectionPass(g: ModuleGraph; fn: PSym; idgen: IdGenerator): DetectionPass =
result.processed = initIntSet()
result.capturedVars = initIntSet()
result.ownerToType = initTable[int, PType]()
result.processed.incl(fn.id)
result.graph = g
result.idgen = idgen
discard """
proc outer =
@ -327,15 +329,15 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
info: TLineInfo): PType =
result = c.ownerToType.getOrDefault(owner.id)
if result.isNil:
result = newType(tyRef, owner)
let obj = createEnvObj(c.graph, owner, info)
result = newType(tyRef, nextId(c.idgen), owner)
let obj = createEnvObj(c.graph, c.idgen, owner, info)
rawAddSon(result, obj)
c.ownerToType[owner.id] = result
proc asOwnedRef(c: DetectionPass; t: PType): PType =
proc asOwnedRef(c: var DetectionPass; t: PType): PType =
if optOwnedRefs in c.graph.config.globalOptions:
assert t.kind == tyRef
result = newType(tyOwned, t.owner)
result = newType(tyOwned, nextId(c.idgen), t.owner)
result.flags.incl tfHasOwned
result.rawAddSon t
else:
@ -344,7 +346,7 @@ proc asOwnedRef(c: DetectionPass; t: PType): PType =
proc getEnvTypeForOwnerUp(c: var DetectionPass; owner: PSym;
info: TLineInfo): PType =
var r = c.getEnvTypeForOwner(owner, info)
result = newType(tyPtr, owner)
result = newType(tyPtr, nextId(c.idgen), owner)
rawAddSon(result, r.skipTypes({tyOwned, tyRef, tyPtr}))
proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
@ -372,7 +374,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags:
localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor")
else:
let result = newSym(skField, upIdent, obj.owner, obj.owner.info)
let result = newSym(skField, upIdent, nextId(c.idgen), obj.owner, obj.owner.info)
result.typ = fieldType
when false:
if c.graph.config.selectedGC == gcDestructors:
@ -410,7 +412,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
let t = c.getEnvTypeForOwner(owner, info)
if cp == nil:
cp = newSym(skParam, getIdent(c.graph.cache, paramName), fn, fn.info)
cp = newSym(skParam, getIdent(c.graph.cache, paramName), nextId(c.idgen), fn, fn.info)
incl(cp.flags, sfFromGeneric)
cp.typ = t
addHiddenParam(fn, cp)
@ -433,7 +435,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
if innerProc:
if s.isIterator: c.somethingToDo = true
if not c.processed.containsOrIncl(s.id):
let body = transformBody(c.graph, s, cache = true)
let body = transformBody(c.graph, c.idgen, s, cache = true)
detectCapturedVars(body, s, c)
let ow = s.skipGenericOwner
if ow == owner:
@ -446,9 +448,9 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
#let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef, tyPtr})
if s.name.id == getIdent(c.graph.cache, ":state").id:
obj.n[0].sym.id = -s.id
obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
else:
addField(obj, s, c.graph.cache)
addField(obj, s, c.graph.cache, c.idgen)
# direct or indirect dependency:
elif (innerProc and s.typ.callConv == ccClosure) or interestingVar(s):
discard """
@ -470,7 +472,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr})
#getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
addField(obj, s, c.graph.cache)
addField(obj, s, c.graph.cache, c.idgen)
# create required upFields:
var w = owner.skipGenericOwner
if isInnerProc(w) or owner.isIterator:
@ -535,8 +537,8 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
localError(g.config, n.info, "internal error: environment misses: " & s.name.s)
result = n
proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo): PNode =
var v = newSym(skVar, getIdent(cache, envName), owner, info)
proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo; idgen: IdGenerator): PNode =
var v = newSym(skVar, getIdent(cache, envName), nextId(idgen), owner, info)
v.flags = {sfShadowed, sfGeneratedOp}
v.typ = typ
result = newSymNode(v)
@ -548,7 +550,7 @@ proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo): PNo
else:
result = newSymNode(v)
proc setupEnvVar(owner: PSym; d: DetectionPass;
proc setupEnvVar(owner: PSym; d: var DetectionPass;
c: var LiftingPass; info: TLineInfo): PNode =
if owner.isIterator:
return getHiddenParam(d.graph, owner).newSymNode
@ -557,10 +559,10 @@ proc setupEnvVar(owner: PSym; d: DetectionPass;
let envVarType = d.ownerToType.getOrDefault(owner.id)
if envVarType.isNil:
localError d.graph.config, owner.info, "internal error: could not determine closure type"
result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info)
result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info, d.idgen)
c.envVars[owner.id] = result
if optOwnedRefs in d.graph.config.globalOptions:
var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), owner, info)
var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), nextId d.idgen, owner, info)
v.flags = {sfShadowed, sfGeneratedOp}
v.typ = envVarType
c.unownedEnvVars[owner.id] = newSymNode(v)
@ -576,7 +578,7 @@ proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode =
result = rawIndirectAccess(result, upField, p.info)
proc rawClosureCreation(owner: PSym;
d: DetectionPass; c: var LiftingPass;
d: var DetectionPass; c: var LiftingPass;
info: TLineInfo): PNode =
result = newNodeI(nkStmtList, owner.info)
@ -602,7 +604,7 @@ proc rawClosureCreation(owner: PSym;
let env2 = copyTree(env)
env2.typ = unowned.typ
result.add newAsgnStmt(unowned, env2, env.info)
createTypeBoundOpsLL(d.graph, unowned.typ, env.info, owner)
createTypeBoundOpsLL(d.graph, unowned.typ, env.info, d.idgen, owner)
# add assignment statements for captured parameters:
for i in 1..<owner.typ.n.len:
@ -611,7 +613,7 @@ proc rawClosureCreation(owner: PSym;
let fieldAccess = indirectAccess(env, local, env.info)
# add ``env.param = param``
result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info))
createTypeBoundOps(d.graph, nil, fieldAccess.typ, env.info)
createTypeBoundOps(d.graph, nil, fieldAccess.typ, env.info, d.idgen)
if tfHasAsgn in fieldAccess.typ.flags or optSeqDestructors in d.graph.config.globalOptions:
owner.flags.incl sfInjectDestructors
@ -628,28 +630,28 @@ proc rawClosureCreation(owner: PSym;
localError(d.graph.config, env.info, "internal error: cannot create up reference")
# we are not in the sem'check phase anymore! so pass 'nil' for the PContext
# and hope for the best:
createTypeBoundOpsLL(d.graph, env.typ, owner.info, owner)
createTypeBoundOpsLL(d.graph, env.typ, owner.info, d.idgen, owner)
proc finishClosureCreation(owner: PSym; d: DetectionPass; c: LiftingPass;
proc finishClosureCreation(owner: PSym; d: var DetectionPass; c: LiftingPass;
info: TLineInfo; res: PNode) =
if optOwnedRefs in d.graph.config.globalOptions:
let unowned = c.unownedEnvVars[owner.id]
assert unowned != nil
let nilLit = newNodeIT(nkNilLit, info, unowned.typ)
res.add newAsgnStmt(unowned, nilLit, info)
createTypeBoundOpsLL(d.graph, unowned.typ, info, owner)
createTypeBoundOpsLL(d.graph, unowned.typ, info, d.idgen, owner)
proc closureCreationForIter(iter: PNode;
d: DetectionPass; c: var LiftingPass): PNode =
d: var DetectionPass; c: var LiftingPass): PNode =
result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
let owner = iter.sym.skipGenericOwner
var v = newSym(skVar, getIdent(d.graph.cache, envName), owner, iter.info)
var v = newSym(skVar, getIdent(d.graph.cache, envName), nextId(d.idgen), owner, iter.info)
incl(v.flags, sfShadowed)
v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
var vnode: PNode
if owner.isIterator:
let it = getHiddenParam(d.graph, owner)
addUniqueField(it.typ.skipTypes({tyOwned, tyRef, tyPtr}), v, d.graph.cache)
addUniqueField(it.typ.skipTypes({tyOwned, tyRef, tyPtr}), v, d.graph.cache, d.idgen)
vnode = indirectAccess(newSymNode(it), v, v.info)
else:
vnode = v.newSymNode
@ -657,7 +659,7 @@ proc closureCreationForIter(iter: PNode;
addVar(vs, vnode)
result.add(vs)
result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, owner)
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner)
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
if upField != nil:
@ -667,9 +669,9 @@ proc closureCreationForIter(iter: PNode;
u, iter.info))
else:
localError(d.graph.config, iter.info, "internal error: cannot create up reference for iter")
result.add makeClosure(d.graph, iter.sym, vnode, iter.info)
result.add makeClosure(d.graph, d.idgen, iter.sym, vnode, iter.info)
proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
proc accessViaEnvVar(n: PNode; owner: PSym; d: var DetectionPass;
c: var LiftingPass): PNode =
var access = setupEnvVar(owner, d, c, n.info)
if optOwnedRefs in d.graph.config.globalOptions:
@ -685,21 +687,21 @@ proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
proc getStateField*(g: ModuleGraph; owner: PSym): PSym =
getHiddenParam(g, owner).typ.skipTypes({tyOwned, tyRef, tyPtr}).n[0].sym
proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
c: var LiftingPass): PNode
proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
proc symToClosure(n: PNode; owner: PSym; d: var DetectionPass;
c: var LiftingPass): PNode =
let s = n.sym
if s == owner:
# recursive calls go through (lambda, hiddenParam):
let available = getHiddenParam(d.graph, owner)
result = makeClosure(d.graph, s, available.newSymNode, n.info)
result = makeClosure(d.graph, d.idgen, s, available.newSymNode, n.info)
elif s.isIterator:
result = closureCreationForIter(n, d, c)
elif s.skipGenericOwner == owner:
# direct dependency, so use the outer's env variable:
result = makeClosure(d.graph, s, setupEnvVar(owner, d, c, n.info), n.info)
result = makeClosure(d.graph, d.idgen, s, setupEnvVar(owner, d, c, n.info), n.info)
else:
let available = getHiddenParam(d.graph, owner)
let wanted = getHiddenParam(d.graph, s).typ
@ -707,7 +709,7 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
var access = newSymNode(available)
while true:
if access.typ == wanted:
return makeClosure(d.graph, s, access, n.info)
return makeClosure(d.graph, d.idgen, s, access, n.info)
let obj = access.typ.skipTypes({tyOwned, tyRef, tyPtr})
let upField = lookupInRecord(obj.n, getIdent(d.graph.cache, upName))
if upField == nil:
@ -715,7 +717,7 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
return n
access = rawIndirectAccess(access, upField, n.info)
proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
c: var LiftingPass): PNode =
result = n
case n.kind
@ -727,7 +729,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
# echo renderTree(s.getBody, {renderIds})
let oldInContainer = c.inContainer
c.inContainer = 0
var body = transformBody(d.graph, s, cache = false)
var body = transformBody(d.graph, d.idgen, s, cache = false)
body = liftCapturedVars(body, s, d, c)
if c.envVars.getOrDefault(s.id).isNil:
s.transformedBody = body
@ -833,8 +835,9 @@ proc semCaptureSym*(s, owner: PSym) =
# since the analysis is not entirely correct, we don't set 'tfCapturesEnv'
# here
proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNode =
var d = initDetectionPass(g, fn)
proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType;
idgen: IdGenerator): PNode =
var d = initDetectionPass(g, fn, idgen)
var c = initLiftingPass(fn)
# pretend 'fn' is a closure iterator for the analysis:
let oldKind = fn.kind
@ -847,7 +850,8 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNo
fn.transitionRoutineSymKind(oldKind)
fn.typ.callConv = oldCC
proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PNode =
proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
idgen: IdGenerator): PNode =
# XXX backend == backendJs does not suffice! The compiletime stuff needs
# the transformation even when compiling to JS ...
@ -862,7 +866,7 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PN
result = body
tooEarly = true
else:
var d = initDetectionPass(g, fn)
var d = initDetectionPass(g, fn, idgen)
detectCapturedVars(body, fn, d)
if not d.somethingToDo and fn.isIterator:
addClosureParam(d, fn, body.info)
@ -886,7 +890,7 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
# ------------------- iterator transformation --------------------------------
proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym): PNode =
# problem ahead: the iterator could be invoked indirectly, but then
# we don't know what environment to create here:
#
@ -931,7 +935,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
let iter = op.sym
let hp = getHiddenParam(g, iter)
env = newSym(skLet, iter.name, owner, body.info)
env = newSym(skLet, iter.name, nextId(idgen), owner, body.info)
env.typ = hp.typ
env.flags = hp.flags
@ -940,7 +944,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
result.add(v)
# add 'new' statement:
result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode))
createTypeBoundOpsLL(g, env.typ, body.info, owner)
createTypeBoundOpsLL(g, env.typ, body.info, idgen, owner)
elif op.kind == nkStmtListExpr:
let closure = op.lastSon
@ -966,7 +970,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
vpart.add newNodeI(nkEmpty, body.info) # no explicit type
if not env.isNil:
call[0] = makeClosure(g, call[0].sym, env.newSymNode, body.info)
call[0] = makeClosure(g, idgen, call[0].sym, env.newSymNode, body.info)
vpart.add call
v2.add vpart