diff options
Diffstat (limited to 'compiler/lambdalifting.nim')
-rw-r--r-- | compiler/lambdalifting.nim | 327 |
1 files changed, 183 insertions, 144 deletions
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index c1cd07583..54cdfc5bc 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -10,9 +10,14 @@ # This file implements lambda lifting for the transformator. import - intsets, strutils, options, ast, astalgo, msgs, - idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos, - transf, liftdestructors + options, ast, astalgo, msgs, + idents, renderer, types, magicsys, lowerings, modulegraphs, lineinfos, + transf, liftdestructors, typeallowed + +import std/[strutils, tables, intsets] + +when defined(nimPreviewSlimSystem): + import std/assertions discard """ The basic approach is that captured vars need to be put on the heap and @@ -126,33 +131,35 @@ 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, idgen, iter) result.n = n var intType = nilOrSysInt(g) - if intType.isNil: intType = newType(tyInt, iter) + if intType.isNil: intType = newType(tyInt, 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"), idgen, iter, iter.info) + result.typ = createClosureIterStateType(g, iter, idgen) + +template isIterator*(owner: PSym): bool = + owner.kind == skIterator and owner.typ.callConv == ccClosure -proc createEnvObj(g: ModuleGraph; 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)) +proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType = + result = createObj(g, idgen, owner, info, final=false) + if owner.isIterator or not isDefined(g.config, "nimOptIters"): + 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.typ = iter.typ[0] + result = newSym(skResult, getIdent(g.cache, ":result"), idgen, iter, iter.info, {}) + result.typ = iter.typ.returnType incl(result.flags, sfUsed) iter.ast.add newSymNode(result) @@ -167,37 +174,35 @@ proc addHiddenParam(routine: PSym, param: PSym) = assert sfFromGeneric in param.flags #echo "produced environment: ", param.id, " for ", routine.id -proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym = +proc getEnvParam*(routine: PSym): PSym = let params = routine.ast[paramsPos] let hidden = lastSon(params) if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName: result = hidden.sym assert sfFromGeneric in result.flags else: + result = nil + +proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym = + result = getEnvParam(routine) + if result.isNil: # writeStackTrace() localError(g.config, routine.info, "internal error: could not find env param for " & routine.name.s) result = routine -proc getEnvParam*(routine: PSym): PSym = - let params = routine.ast[paramsPos] - let hidden = lastSon(params) - if hidden.kind == nkSym and hidden.sym.name.s == paramName: - result = hidden.sym - assert sfFromGeneric in result.flags - proc interestingVar(s: PSym): bool {.inline.} = result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and sfGlobal notin s.flags and s.typ.kind notin {tyStatic, tyTypeDesc} proc illegalCapture(s: PSym): bool {.inline.} = - result = skipTypes(s.typ, abstractInst).kind in - {tyVar, tyOpenArray, tyVarargs, tyLent} or - s.kind == skResult + result = classifyViewType(s.typ) != noView or s.kind == skResult proc isInnerProc(s: PSym): bool = if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone: result = s.skipGenericOwner.kind in routineKinds + else: + result = false proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode = # Bugfix: unfortunately we cannot use 'nkFastAsgn' here as that would @@ -209,7 +214,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,46 +224,51 @@ 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 proc interestingIterVar(s: PSym): bool {.inline.} = + # unused with -d:nimOptIters # XXX optimization: Only lift the variable if it lives across # yield/return boundaries! This can potentially speed up # closure iterators quite a bit. result = s.kind in {skResult, skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags -template isIterator*(owner: PSym): bool = - owner.kind == skIterator and owner.typ.callConv == ccClosure - -proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} = +template liftingHarmful(conf: ConfigRef; owner: PSym): bool = ## lambda lifting can be harmful for JS-like code generators. 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) - if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions: - owner.flags.incl sfInjectDestructors - -proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode = + jsNoLambdaLifting in conf.legacyFeatures and conf.backend == backendJs and not isCompileTime + +proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen: IdGenerator; owner: PSym) = + if owner.kind != skMacro: + createTypeBoundOps(g, nil, refType.elementType, info, idgen) + createTypeBoundOps(g, nil, refType, info, idgen) + if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions: + owner.flags.incl sfInjectDestructors + +proc genCreateEnv(env: PNode): PNode = + var c = newNodeIT(nkObjConstr, env.info, env.typ) + c.add newNodeIT(nkType, env.info, env.typ) + let e = copyTree(env) + e.flags.incl nfFirstWrite + result = newAsgnStmt(e, c) + +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 assert iter.isIterator - result = newNodeIT(nkStmtListExpr, n.info, n.typ) - + result = newNodeIT(nkStmtListExpr, n.info, iter.typ) let hp = getHiddenParam(g, iter) 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, idgen, owner, n.info) e.typ = hp.typ e.flags = hp.flags env = newSymNode(e) @@ -266,52 +276,53 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode = addVar(v, env) 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) + #result.add newCall(getSysSym(g, n.info, "internalNew"), env) + result.add genCreateEnv(env) + 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 = + # unused with -d:nimOptIters let envParam = getHiddenParam(g, owner) let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr}) - addField(obj, s, g.cache) + let field = addField(obj, s, g.cache, idgen) var access = newSymNode(envParam) assert obj.kind == tyObject - let field = getFieldFromObj(obj, s) - if field != nil: - result = rawIndirectAccess(access, field, s.info) - else: - localError(g.config, s.info, "internal error: cannot generate fresh variable") - result = access + result = rawIndirectAccess(access, field, s.info) # ------------------ new stuff ------------------------------------------- proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) = let s = n.sym + let isEnv = s.name.id == getIdent(g.cache, ":env").id if illegalCapture(s): localError(g.config, n.info, ("'$1' is of type <$2> which cannot be captured as it would violate memory" & - " safety, declared here: $3; using '-d:nimWorkaround14447' helps in some cases") % - [s.name.s, typeToString(s.typ), g.config$s.info]) - elif owner.typ.callConv notin {ccClosure, ccDefault}: + " safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." & + " Consider using a <ref T> which can be captured.") % + [s.name.s, typeToString(s.typ.skipTypes({tyVar})), g.config$s.info]) + elif not (owner.typ.isClosure or owner.isNimcall and not owner.isExplicitCallConv or isEnv): localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" % - [s.name.s, owner.name.s, CallingConvToStr[owner.typ.callConv]]) + [s.name.s, owner.name.s, $owner.typ.callConv]) incl(owner.typ.flags, tfCapturesEnv) - owner.typ.callConv = ccClosure + if not isEnv: + owner.typ.callConv = ccClosure type DetectionPass = object processed, capturedVars: IntSet ownerToType: Table[int, PType] somethingToDo: bool + inTypeOf: bool graph: ModuleGraph + idgen: IdGenerator -proc initDetectionPass(g: ModuleGraph; fn: PSym): DetectionPass = - result.processed = initIntSet() - result.capturedVars = initIntSet() - result.ownerToType = initTable[int, PType]() - result.processed.incl(fn.id) - result.graph = g +proc initDetectionPass(g: ModuleGraph; fn: PSym; idgen: IdGenerator): DetectionPass = + result = DetectionPass(processed: toIntSet([fn.id]), + capturedVars: initIntSet(), ownerToType: initTable[int, PType](), + graph: g, idgen: idgen + ) discard """ proc outer = @@ -327,15 +338,19 @@ 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) - rawAddSon(result, obj) + let env = getEnvParam(owner) + if env.isNil or not owner.isIterator or not isDefined(c.graph.config, "nimOptIters"): + result = newType(tyRef, c.idgen, owner) + let obj = createEnvObj(c.graph, c.idgen, owner, info) + rawAddSon(result, obj) + else: + result = env.typ 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, c.idgen, t.owner) result.flags.incl tfHasOwned result.rawAddSon t else: @@ -344,7 +359,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, c.idgen, owner) rawAddSon(result, r.skipTypes({tyOwned, tyRef, tyPtr})) proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) = @@ -372,7 +387,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, c.idgen, obj.owner, obj.owner.info) result.typ = fieldType when false: if c.graph.config.selectedGC == gcDestructors: @@ -405,12 +420,15 @@ Consider: """ +proc isTypeOf(n: PNode): bool = + n.kind == nkSym and n.sym.magic in {mTypeOf, mType} + proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) = var cp = getEnvParam(fn) 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), c.idgen, fn, fn.info) incl(cp.flags, sfFromGeneric) cp.typ = t addHiddenParam(fn, cp) @@ -433,24 +451,28 @@ 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, {useCache}) detectCapturedVars(body, s, c) let ow = s.skipGenericOwner + let innerClosure = innerProc and s.typ.callConv == ccClosure and not s.isIterator + let interested = interestingVar(s) if ow == owner: if owner.isIterator: c.somethingToDo = true addClosureParam(c, owner, n.info) - if interestingIterVar(s): - if not c.capturedVars.containsOrIncl(s.id): + if not isDefined(c.graph.config, "nimOptIters") and interestingIterVar(s): + if not c.capturedVars.contains(s.id): + if not c.inTypeOf: c.capturedVars.incl(s.id) let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef, tyPtr}) #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.flags.incl sfNoInit + obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item) else: - addField(obj, s, c.graph.cache) + discard addField(obj, s, c.graph.cache, c.idgen) # direct or indirect dependency: - elif (innerProc and s.typ.callConv == ccClosure) or interestingVar(s): + elif innerClosure or interested: discard """ proc outer() = var x: int @@ -467,10 +489,12 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = addClosureParam(c, owner, n.info) #echo "capturing ", n.info # variable 's' is actually captured: - 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) + if interestingVar(s): + if not c.capturedVars.contains(s.id): + if not c.inTypeOf: c.capturedVars.incl(s.id) + let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr}) + #getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr}) + discard addField(obj, s, c.graph.cache, c.idgen) # create required upFields: var w = owner.skipGenericOwner if isInnerProc(w) or owner.isIterator: @@ -494,16 +518,22 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = w = up of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, - nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt, nkTypeOfExpr: + nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt, + nkTypeOfExpr, nkMixinStmt, nkBindStmt: discard of nkLambdaKinds, nkIteratorDef: if n.typ != nil: detectCapturedVars(n[namePos], owner, c) of nkReturnStmt: detectCapturedVars(n[0], owner, c) + of nkIdentDefs: + detectCapturedVars(n[^1], owner, c) else: + if n.isCallExpr and n[0].isTypeOf: + c.inTypeOf = true for i in 0..<n.len: detectCapturedVars(n[i], owner, c) + c.inTypeOf = false type LiftingPass = object @@ -513,9 +543,8 @@ type unownedEnvVars: Table[int, PNode] # only required for --newruntime proc initLiftingPass(fn: PSym): LiftingPass = - result.processed = initIntSet() - result.processed.incl(fn.id) - result.envVars = initTable[int, PNode]() + result = LiftingPass(processed: toIntSet([fn.id]), + envVars: initTable[int, PNode]()) proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode = let s = n.sym @@ -523,8 +552,8 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode = let envParam = getHiddenParam(g, owner) if not envParam.isNil: var access = newSymNode(envParam) + var obj = access.typ.elementType while true: - let obj = access.typ[0] assert obj.kind == tyObject let field = getFieldFromObj(obj, s) if field != nil: @@ -532,23 +561,24 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode = let upField = lookupInRecord(obj.n, getIdent(g.cache, upName)) if upField == nil: break access = rawIndirectAccess(access, upField, n.info) + obj = access.typ.baseClass 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), idgen, owner, info) v.flags = {sfShadowed, sfGeneratedOp} v.typ = typ result = newSymNode(v) when false: if owner.kind == skIterator and owner.typ.callConv == ccClosure: let it = getHiddenParam(owner) - addUniqueField(it.typ[0], v) + addUniqueField(it.typ.elementType, v) result = indirectAccess(newSymNode(it), v, v.info) 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 +587,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"), d.idgen, owner, info) v.flags = {sfShadowed, sfGeneratedOp} v.typ = envVarType c.unownedEnvVars[owner.id] = newSymNode(v) @@ -576,7 +606,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) @@ -595,14 +625,14 @@ proc rawClosureCreation(owner: PSym; addVar(v, unowned) # add 'new' statement: - result.add(newCall(getSysSym(d.graph, env.info, "internalNew"), env)) + result.add genCreateEnv(env) if optOwnedRefs in d.graph.config.globalOptions: let unowned = c.unownedEnvVars[owner.id] assert unowned != nil 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 +641,8 @@ 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) + if owner.kind != skMacro: + 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,36 +659,36 @@ 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), 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 var vs = newNodeI(nkVarSection, iter.info) addVar(vs, vnode) result.add(vs) - result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode)) - createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, owner) + result.add genCreateEnv(vnode) + 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 +698,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,29 +716,30 @@ 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: + result = nil let available = getHiddenParam(d.graph, owner) let wanted = getHiddenParam(d.graph, s).typ # ugh: call through some other inner proc; 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 +747,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 +759,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, {}) body = liftCapturedVars(body, s, d, c) if c.envVars.getOrDefault(s.id).isNil: s.transformedBody = body @@ -742,13 +774,13 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; elif s.id in d.capturedVars: if s.owner != owner: result = accessViaEnvParam(d.graph, n, owner) - elif owner.isIterator and interestingIterVar(s): + elif owner.isIterator and not isDefined(d.graph.config, "nimOptIters") and interestingIterVar(s): result = accessViaEnvParam(d.graph, n, owner) else: result = accessViaEnvVar(n, owner, d, c) of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom, nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, nkConverterDef, - nkMacroDef, nkFuncDef: + nkMacroDef, nkFuncDef, nkMixinStmt, nkBindStmt: discard of nkClosure: if n[1].kind == nkNilLit: @@ -772,7 +804,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; n[1] = liftCapturedVars(n[1], owner, d, c) if n[1].kind == nkClosure: result = n[1] of nkReturnStmt: - if n[0].kind in {nkAsgn, nkFastAsgn}: + if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}: # we have a `result = result` expression produced by the closure # transform, let's not touch the LHS in order to make the lifting pass # correct when `result` is lifted @@ -782,6 +814,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; of nkTypeOfExpr: result = n else: + if n.isCallExpr and n[0].isTypeOf: + return if owner.isIterator: if nfLL in n.flags: # special case 'when nimVm' due to bug #3636: @@ -822,7 +856,8 @@ proc semCaptureSym*(s, owner: PSym) = var o = owner.skipGenericOwner while o != nil and o.kind != skModule: if s.owner == o: - if owner.typ.callConv in {ccClosure, ccDefault} or owner.kind == skIterator: + if owner.typ.callConv == ccClosure or owner.kind == skIterator or + owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags: owner.typ.callConv = ccClosure propagateClosure(owner.skipGenericOwner, s.owner) else: @@ -832,8 +867,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 @@ -846,22 +882,22 @@ 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 = - # XXX backend == backendJs does not suffice! The compiletime stuff needs - # the transformation even when compiling to JS ... - - # However we can do lifting for the stuff which is *only* compiletime. +proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool; + idgen: IdGenerator; flags: TransformFlags): PNode = let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro - if body.kind == nkEmpty or ( + if body.kind == nkEmpty or (jsNoLambdaLifting in g.config.legacyFeatures and g.config.backend == backendJs and not isCompileTime) or - fn.skipGenericOwner.kind != skModule: + (fn.skipGenericOwner.kind != skModule and force notin flags): # ignore forward declaration: result = body tooEarly = true + if fn.isIterator and isDefined(g.config, "nimOptIters"): + var d = initDetectionPass(g, fn, idgen) + addClosureParam(d, fn, body.info) 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) @@ -885,7 +921,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: # @@ -923,14 +959,14 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode = result = newNodeI(nkStmtList, body.info) # static binding? - var env: PSym + var env: PSym = nil let op = call[0] if op.kind == nkSym and op.sym.isIterator: # createClosure() let iter = op.sym let hp = getHiddenParam(g, iter) - env = newSym(skLet, iter.name, owner, body.info) + env = newSym(skLet, iter.name, idgen, owner, body.info) env.typ = hp.typ env.flags = hp.flags @@ -938,8 +974,8 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode = addVar(v, newSymNode(env)) result.add(v) # add 'new' statement: - result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode)) - createTypeBoundOpsLL(g, env.typ, body.info, owner) + result.add genCreateEnv(env.newSymNode) + createTypeBoundOpsLL(g, env.typ, body.info, idgen, owner) elif op.kind == nkStmtListExpr: let closure = op.lastSon @@ -958,14 +994,17 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode = # gather vars in a tuple: var v2 = newNodeI(nkLetSection, body.info) var vpart = newNodeI(if body.len == 3: nkIdentDefs else: nkVarTuple, body.info) - for i in 0..<body.len-2: - if body[i].kind == nkSym: - body[i].sym.transitionToLet() - vpart.add body[i] + if body.len == 3 and body[0].kind == nkVarTuple: + vpart = body[0] # fixes for (i,j) in walk() # bug #15924 + else: + for i in 0..<body.len-2: + if body[i].kind == nkSym: + body[i].sym.transitionToLet() + vpart.add body[i] - vpart.add newNodeI(nkEmpty, body.info) # no explicit type + 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 |