summary refs log tree commit diff stats
path: root/compiler/lambdalifting.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/lambdalifting.nim')
-rw-r--r--compiler/lambdalifting.nim245
1 files changed, 136 insertions, 109 deletions
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index cf43ba15d..40e5bb6b0 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -7,11 +7,11 @@
 #    distribution, for details about the copyright.
 #
 
-# This include file implements lambda lifting for the transformator.
+# This file implements lambda lifting for the transformator.
 
 import
-  intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
-  idents, renderer, types, magicsys, rodread, lowerings, tables
+  intsets, strutils, options, ast, astalgo, trees, treetab, msgs,
+  idents, renderer, types, magicsys, rodread, lowerings, tables, modulegraphs
 
 discard """
   The basic approach is that captured vars need to be put on the heap and
@@ -125,32 +125,32 @@ proc newCall(a: PSym, b: PNode): PNode =
   result.add newSymNode(a)
   result.add b
 
-proc createStateType(iter: PSym): PType =
+proc createClosureIterStateType*(g: ModuleGraph; iter: PSym): PType =
   var n = newNodeI(nkRange, iter.info)
   addSon(n, newIntNode(nkIntLit, -1))
   addSon(n, newIntNode(nkIntLit, 0))
   result = newType(tyRange, iter)
   result.n = n
-  var intType = nilOrSysInt()
+  var intType = nilOrSysInt(g)
   if intType.isNil: intType = newType(tyInt, iter)
   rawAddSon(result, intType)
 
-proc createStateField(iter: PSym): PSym =
+proc createStateField(g: ModuleGraph; iter: PSym): PSym =
   result = newSym(skField, getIdent(":state"), iter, iter.info)
-  result.typ = createStateType(iter)
+  result.typ = createClosureIterStateType(g, iter)
 
-proc createEnvObj(owner: PSym; info: TLineInfo): PType =
+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(owner, info, final=false)
-  rawAddField(result, createStateField(owner))
+  result = createObj(g, owner, info, final=false)
+  rawAddField(result, createStateField(g, owner))
 
-proc getIterResult(iter: PSym): PSym =
+proc getClosureIterResult*(iter: PSym): PSym =
   if resultPos < iter.ast.len:
     result = iter.ast.sons[resultPos].sym
   else:
     # XXX a bit hacky:
-    result = newSym(skResult, getIdent":result", iter, iter.info)
+    result = newSym(skResult, getIdent":result", iter, iter.info, {})
     result.typ = iter.typ.sons[0]
     incl(result.flags, sfUsed)
     iter.ast.add newSymNode(result)
@@ -166,7 +166,7 @@ proc addHiddenParam(routine: PSym, param: PSym) =
   assert sfFromGeneric in param.flags
   #echo "produced environment: ", param.id, " for ", routine.id
 
-proc getHiddenParam(routine: PSym): PSym =
+proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
   let params = routine.ast.sons[paramsPos]
   let hidden = lastSon(params)
   if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
@@ -174,7 +174,7 @@ proc getHiddenParam(routine: PSym): PSym =
     assert sfFromGeneric in result.flags
   else:
     # writeStackTrace()
-    localError(routine.info, "internal error: could not find env param for " & routine.name.s)
+    localError(g.config, routine.info, "internal error: could not find env param for " & routine.name.s)
     result = routine
 
 proc getEnvParam*(routine: PSym): PSym =
@@ -186,11 +186,12 @@ proc getEnvParam*(routine: PSym): PSym =
 
 proc interestingVar(s: PSym): bool {.inline.} =
   result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and
-    sfGlobal notin s.flags
+    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} or
+                   {tyVar, tyOpenArray, tyVarargs, tyLent} or
       s.kind == skResult
 
 proc isInnerProc(s: PSym): bool =
@@ -207,14 +208,14 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
   result.sons[0] = le
   result.sons[1] = ri
 
-proc makeClosure*(prc: PSym; env: PNode; info: TLineInfo): PNode =
+proc makeClosure*(g: ModuleGraph; prc: PSym; env: PNode; info: TLineInfo): PNode =
   result = newNodeIT(nkClosure, info, prc.typ)
   result.add(newSymNode(prc))
   if env == nil:
-    result.add(newNodeIT(nkNilLit, info, getSysType(tyNil)))
+    result.add(newNodeIT(nkNilLit, info, getSysType(g, info, tyNil)))
   else:
     if env.skipConv.kind == nkClosure:
-      localError(info, "internal error: taking closure of closure")
+      localError(g.config, info, "internal error: taking closure of closure")
     result.add(env)
 
 proc interestingIterVar(s: PSym): bool {.inline.} =
@@ -226,23 +227,23 @@ proc interestingIterVar(s: PSym): bool {.inline.} =
 template isIterator*(owner: PSym): bool =
   owner.kind == skIterator and owner.typ.callConv == ccClosure
 
-proc liftingHarmful(owner: PSym): bool {.inline.} =
+proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} =
   ## lambda lifting can be harmful for JS-like code generators.
   let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
-  result = gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime
+  result = conf.cmd == cmdCompileToJS and not isCompileTime
 
-proc liftIterSym*(n: PNode; owner: PSym): PNode =
+proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   # transforms  (iter)  to  (let env = newClosure[iter](); (iter, env))
-  if liftingHarmful(owner): return n
+  if liftingHarmful(g.config, owner): return n
   let iter = n.sym
   assert iter.isIterator
 
   result = newNodeIT(nkStmtListExpr, n.info, n.typ)
 
-  let hp = getHiddenParam(iter)
+  let hp = getHiddenParam(g, iter)
   var env: PNode
   if owner.isIterator:
-    let it = getHiddenParam(owner)
+    let it = getHiddenParam(g, owner)
     addUniqueField(it.typ.sons[0], hp)
     env = indirectAccess(newSymNode(it), hp, hp.info)
   else:
@@ -254,11 +255,11 @@ proc liftIterSym*(n: PNode; owner: PSym): PNode =
     addVar(v, env)
     result.add(v)
   # add 'new' statement:
-  result.add newCall(getSysSym"internalNew", env)
-  result.add makeClosure(iter, env, n.info)
+  result.add newCall(getSysSym(g, n.info, "internalNew"), env)
+  result.add makeClosure(g, iter, env, n.info)
 
-proc freshVarForClosureIter*(s, owner: PSym): PNode =
-  let envParam = getHiddenParam(owner)
+proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
+  let envParam = getHiddenParam(g, owner)
   let obj = envParam.typ.lastSon
   addField(obj, s)
 
@@ -268,15 +269,19 @@ proc freshVarForClosureIter*(s, owner: PSym): PNode =
   if field != nil:
     result = rawIndirectAccess(access, field, s.info)
   else:
-    localError(s.info, "internal error: cannot generate fresh variable")
+    localError(g.config, s.info, "internal error: cannot generate fresh variable")
     result = access
 
 # ------------------ new stuff -------------------------------------------
 
-proc markAsClosure(owner: PSym; n: PNode) =
+proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
   let s = n.sym
-  if illegalCapture(s) or owner.typ.callConv notin {ccClosure, ccDefault}:
-    localError(n.info, errIllegalCaptureX, s.name.s)
+  if illegalCapture(s):
+    localError(g.config, n.info, "illegal capture '$1' of type <$2> which is declared here: $3" %
+      [s.name.s, typeToString(s.typ), $s.info])
+  elif owner.typ.callConv notin {ccClosure, ccDefault}:
+    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]])
   incl(owner.typ.flags, tfCapturesEnv)
   owner.typ.callConv = ccClosure
 
@@ -285,12 +290,14 @@ type
     processed, capturedVars: IntSet
     ownerToType: Table[int, PType]
     somethingToDo: bool
+    graph: ModuleGraph
 
-proc initDetectionPass(fn: PSym): DetectionPass =
+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
 
 discard """
 proc outer =
@@ -307,7 +314,7 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
   result = c.ownerToType.getOrDefault(owner.id)
   if result.isNil:
     result = newType(tyRef, owner)
-    let obj = createEnvObj(owner, info)
+    let obj = createEnvObj(c.graph, owner, info)
     rawAddSon(result, obj)
     c.ownerToType[owner.id] = result
 
@@ -316,13 +323,13 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
   let obj = refObj.lastSon
   let fieldType = c.getEnvTypeForOwner(dep, info) #getHiddenParam(dep).typ
   if refObj == fieldType:
-    localError(dep.info, "internal error: invalid up reference computed")
+    localError(c.graph.config, dep.info, "internal error: invalid up reference computed")
 
   let upIdent = getIdent(upName)
   let upField = lookupInRecord(obj.n, upIdent)
   if upField != nil:
     if upField.typ != fieldType:
-      localError(dep.info, "internal error: up references do not agree")
+      localError(c.graph.config, dep.info, "internal error: up references do not agree")
   else:
     let result = newSym(skField, upIdent, obj.owner, obj.owner.info)
     result.typ = fieldType
@@ -364,7 +371,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
     cp.typ = t
     addHiddenParam(fn, cp)
   elif cp.typ != t and fn.kind != skIterator:
-    localError(fn.info, "internal error: inconsistent environment type")
+    localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
   #echo "adding closure to ", fn.name.s
 
 proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
@@ -390,9 +397,13 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
         addClosureParam(c, owner, n.info)
         if interestingIterVar(s):
           if not c.capturedVars.containsOrIncl(s.id):
-            let obj = getHiddenParam(owner).typ.lastSon
+            let obj = getHiddenParam(c.graph, owner).typ.lastSon
             #let obj = c.getEnvTypeForOwner(s.owner).lastSon
-            addField(obj, s)
+
+            if s.name == getIdent(":state"):
+              obj.n[0].sym.id = -s.id
+            else:
+              addField(obj, s)
       # but always return because the rest of the proc is only relevant when
       # ow != owner:
       return
@@ -410,7 +421,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
       """
       # mark 'owner' as taking a closure:
       c.somethingToDo = true
-      markAsClosure(owner, n)
+      markAsClosure(c.graph, owner, n)
       addClosureParam(c, owner, n.info)
       #echo "capturing ", n.info
       # variable 's' is actually captured:
@@ -435,7 +446,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
           """
           let up = w.skipGenericOwner
           #echo "up for ", w.name.s, " up ", up.name.s
-          markAsClosure(w, n)
+          markAsClosure(c.graph, w, n)
           addClosureParam(c, w, n.info) # , ow
           createUpField(c, w, up, n.info)
           w = up
@@ -462,10 +473,10 @@ proc initLiftingPass(fn: PSym): LiftingPass =
   result.processed.incl(fn.id)
   result.envVars = initTable[int, PNode]()
 
-proc accessViaEnvParam(n: PNode; owner: PSym): PNode =
+proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   let s = n.sym
   # Type based expression construction for simplicity:
-  let envParam = getHiddenParam(owner)
+  let envParam = getHiddenParam(g, owner)
   if not envParam.isNil:
     var access = newSymNode(envParam)
     while true:
@@ -477,7 +488,7 @@ proc accessViaEnvParam(n: PNode; owner: PSym): PNode =
       let upField = lookupInRecord(obj.n, getIdent(upName))
       if upField == nil: break
       access = rawIndirectAccess(access, upField, n.info)
-  localError(n.info, "internal error: environment misses: " & s.name.s)
+  localError(g.config, n.info, "internal error: environment misses: " & s.name.s)
   result = n
 
 proc newEnvVar(owner: PSym; typ: PType): PNode =
@@ -496,22 +507,22 @@ proc newEnvVar(owner: PSym; typ: PType): PNode =
 proc setupEnvVar(owner: PSym; d: DetectionPass;
                  c: var LiftingPass): PNode =
   if owner.isIterator:
-    return getHiddenParam(owner).newSymNode
+    return getHiddenParam(d.graph, owner).newSymNode
   result = c.envvars.getOrDefault(owner.id)
   if result.isNil:
     let envVarType = d.ownerToType.getOrDefault(owner.id)
     if envVarType.isNil:
-      localError owner.info, "internal error: could not determine closure type"
+      localError d.graph.config, owner.info, "internal error: could not determine closure type"
     result = newEnvVar(owner, envVarType)
     c.envVars[owner.id] = result
 
-proc getUpViaParam(owner: PSym): PNode =
-  let p = getHiddenParam(owner)
+proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode =
+  let p = getHiddenParam(g, owner)
   result = p.newSymNode
   if owner.isIterator:
     let upField = lookupInRecord(p.typ.lastSon.n, getIdent(upName))
     if upField == nil:
-      localError(owner.info, "could not find up reference for closure iter")
+      localError(g.config, owner.info, "could not find up reference for closure iter")
     else:
       result = rawIndirectAccess(result, upField, p.info)
 
@@ -521,7 +532,7 @@ proc rawClosureCreation(owner: PSym;
 
   var env: PNode
   if owner.isIterator:
-    env = getHiddenParam(owner).newSymNode
+    env = getHiddenParam(d.graph, owner).newSymNode
   else:
     env = setupEnvVar(owner, d, c)
     if env.kind == nkSym:
@@ -529,7 +540,7 @@ proc rawClosureCreation(owner: PSym;
       addVar(v, env)
       result.add(v)
     # add 'new' statement:
-    result.add(newCall(getSysSym"internalNew", env))
+    result.add(newCall(getSysSym(d.graph, env.info, "internalNew"), env))
     # add assignment statements for captured parameters:
     for i in 1..<owner.typ.n.len:
       let local = owner.typ.n[i].sym
@@ -540,7 +551,7 @@ proc rawClosureCreation(owner: PSym;
 
   let upField = lookupInRecord(env.typ.lastSon.n, getIdent(upName))
   if upField != nil:
-    let up = getUpViaParam(owner)
+    let up = getUpViaParam(d.graph, owner)
     if up != nil and upField.typ == up.typ:
       result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
                  up, env.info))
@@ -548,7 +559,7 @@ proc rawClosureCreation(owner: PSym;
     #  result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
     #             oldenv, env.info))
     else:
-      localError(env.info, "internal error: cannot create up reference")
+      localError(d.graph.config, env.info, "internal error: cannot create up reference")
 
 proc closureCreationForIter(iter: PNode;
                             d: DetectionPass; c: var LiftingPass): PNode =
@@ -556,10 +567,10 @@ proc closureCreationForIter(iter: PNode;
   let owner = iter.sym.skipGenericOwner
   var v = newSym(skVar, getIdent(envName), owner, iter.info)
   incl(v.flags, sfShadowed)
-  v.typ = getHiddenParam(iter.sym).typ
+  v.typ = getHiddenParam(d.graph, iter.sym).typ
   var vnode: PNode
   if owner.isIterator:
-    let it = getHiddenParam(owner)
+    let it = getHiddenParam(d.graph, owner)
     addUniqueField(it.typ.sons[0], v)
     vnode = indirectAccess(newSymNode(it), v, v.info)
   else:
@@ -567,7 +578,7 @@ proc closureCreationForIter(iter: PNode;
     var vs = newNodeI(nkVarSection, iter.info)
     addVar(vs, vnode)
     result.add(vs)
-  result.add(newCall(getSysSym"internalNew", vnode))
+  result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
 
   let upField = lookupInRecord(v.typ.lastSon.n, getIdent(upName))
   if upField != nil:
@@ -576,8 +587,8 @@ proc closureCreationForIter(iter: PNode;
       result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
                  u, iter.info))
     else:
-      localError(iter.info, "internal error: cannot create up reference for iter")
-  result.add makeClosure(iter.sym, vnode, iter.info)
+      localError(d.graph.config, iter.info, "internal error: cannot create up reference for iter")
+  result.add makeClosure(d.graph, iter.sym, vnode, iter.info)
 
 proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
                      c: var LiftingPass): PNode =
@@ -587,11 +598,11 @@ proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
   if field != nil:
     result = rawIndirectAccess(access, field, n.info)
   else:
-    localError(n.info, "internal error: not part of closure object type")
+    localError(d.graph.config, n.info, "internal error: not part of closure object type")
     result = n
 
-proc getStateField(owner: PSym): PSym =
-  getHiddenParam(owner).typ.sons[0].n.sons[0].sym
+proc getStateField*(g: ModuleGraph; owner: PSym): PSym =
+  getHiddenParam(g, owner).typ.sons[0].n.sons[0].sym
 
 proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
                       c: var LiftingPass): PNode
@@ -599,8 +610,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
 proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
                     c: var LiftingPass): PNode =
   if c.inContainer > 0:
-    localError(n.info, "invalid control flow: 'yield' within a constructor")
-  let state = getStateField(owner)
+    localError(d.graph.config, n.info, "invalid control flow: 'yield' within a constructor")
+  let state = getStateField(d.graph, owner)
   assert state != nil
   assert state.typ != nil
   assert state.typ.n != nil
@@ -610,20 +621,22 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
   var stateAsgnStmt = newNodeI(nkAsgn, n.info)
   stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)),
                     state, n.info))
-  stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
+  stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo,
+                    getSysType(d.graph, n.info, tyInt)))
 
   var retStmt = newNodeI(nkReturnStmt, n.info)
   if n.sons[0].kind != nkEmpty:
     var a = newNodeI(nkAsgn, n.sons[0].info)
     var retVal = liftCapturedVars(n.sons[0], owner, d, c)
-    addSon(a, newSymNode(getIterResult(owner)))
+    addSon(a, newSymNode(getClosureIterResult(owner)))
     addSon(a, retVal)
     retStmt.add(a)
   else:
     retStmt.add(emptyNode)
 
   var stateLabelStmt = newNodeI(nkState, n.info)
-  stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
+  stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo,
+                     getSysType(d.graph, n.info, tyInt)))
 
   result = newNodeI(nkStmtList, n.info)
   result.add(stateAsgnStmt)
@@ -632,16 +645,16 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
 
 proc transformReturn(n: PNode; owner: PSym; d: DetectionPass;
                      c: var LiftingPass): PNode =
-  let state = getStateField(owner)
+  let state = getStateField(d.graph, owner)
   result = newNodeI(nkStmtList, n.info)
   var stateAsgnStmt = newNodeI(nkAsgn, n.info)
   stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)),
                     state, n.info))
-  stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt)))
+  stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(d.graph, n.info, tyInt)))
   result.add(stateAsgnStmt)
   result.add(n)
 
-proc wrapIterBody(n: PNode; owner: PSym): PNode =
+proc wrapIterBody(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   if not owner.isIterator: return n
   when false:
     # unfortunately control flow is still convoluted and we can end up
@@ -655,7 +668,7 @@ proc wrapIterBody(n: PNode; owner: PSym): PNode =
   let info = n.info
   result = newNodeI(nkStmtList, info)
   var gs = newNodeI(nkGotoState, info)
-  gs.add(rawIndirectAccess(newSymNode(owner.getHiddenParam), getStateField(owner), info))
+  gs.add(rawIndirectAccess(newSymNode(getHiddenParam(g, owner)), getStateField(g, owner), info))
   result.add(gs)
   var state0 = newNodeI(nkState, info)
   state0.add(newIntNode(nkIntLit, 0))
@@ -664,9 +677,9 @@ proc wrapIterBody(n: PNode; owner: PSym): PNode =
   result.add(n)
 
   var stateAsgnStmt = newNodeI(nkAsgn, info)
-  stateAsgnStmt.add(rawIndirectAccess(newSymNode(owner.getHiddenParam),
-                    getStateField(owner), info))
-  stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt)))
+  stateAsgnStmt.add(rawIndirectAccess(newSymNode(getHiddenParam(g, owner)),
+                    getStateField(g, owner), info))
+  stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(g, info, tyInt)))
   result.add(stateAsgnStmt)
 
 proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
@@ -674,25 +687,25 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
   let s = n.sym
   if s == owner:
     # recursive calls go through (lambda, hiddenParam):
-    let available = getHiddenParam(owner)
-    result = makeClosure(s, available.newSymNode, n.info)
+    let available = getHiddenParam(d.graph, owner)
+    result = makeClosure(d.graph, 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(s, setupEnvVar(owner, d, c), n.info)
+    result = makeClosure(d.graph, s, setupEnvVar(owner, d, c), n.info)
   else:
-    let available = getHiddenParam(owner)
-    let wanted = getHiddenParam(s).typ
+    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(s, access, n.info)
+        return makeClosure(d.graph, s, access, n.info)
       let obj = access.typ.sons[0]
       let upField = lookupInRecord(obj.n, getIdent(upName))
       if upField == nil:
-        localError(n.info, "internal error: no environment found")
+        localError(d.graph.config, n.info, "internal error: no environment found")
         return n
       access = rawIndirectAccess(access, upField, n.info)
 
@@ -708,7 +721,9 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
         #  echo renderTree(s.getBody, {renderIds})
         let oldInContainer = c.inContainer
         c.inContainer = 0
-        let body = wrapIterBody(liftCapturedVars(s.getBody, s, d, c), s)
+        var body = liftCapturedVars(s.getBody, s, d, c)
+        if oldIterTransf in d.graph.config.features:
+          body = wrapIterBody(d.graph, body, s)
         if c.envvars.getOrDefault(s.id).isNil:
           s.ast.sons[bodyPos] = body
         else:
@@ -718,9 +733,9 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
         result = symToClosure(n, owner, d, c)
     elif s.id in d.capturedVars:
       if s.owner != owner:
-        result = accessViaEnvParam(n, owner)
+        result = accessViaEnvParam(d.graph, n, owner)
       elif owner.isIterator and interestingIterVar(s):
-        result = accessViaEnvParam(n, owner)
+        result = accessViaEnvParam(d.graph, n, owner)
       else:
         result = accessViaEnvVar(n, owner, d, c)
   of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom,
@@ -751,9 +766,9 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
       if n[1].kind == nkClosure: result = n[1]
   else:
     if owner.isIterator:
-      if n.kind == nkYieldStmt:
+      if oldIterTransf in d.graph.config.features and n.kind == nkYieldStmt:
         return transformYield(n, owner, d, c)
-      elif n.kind == nkReturnStmt:
+      elif oldIterTransf in d.graph.config.features and n.kind == nkReturnStmt:
         return transformReturn(n, owner, d, c)
       elif nfLL in n.flags:
         # special case 'when nimVm' due to bug #3636:
@@ -786,8 +801,8 @@ proc semCaptureSym*(s, owner: PSym) =
     # since the analysis is not entirely correct, we don't set 'tfCapturesEnv'
     # here
 
-proc liftIterToProc*(fn: PSym; body: PNode; ptrType: PType): PNode =
-  var d = initDetectionPass(fn)
+proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNode =
+  var d = initDetectionPass(g, fn)
   var c = initLiftingPass(fn)
   # pretend 'fn' is a closure iterator for the analysis:
   let oldKind = fn.kind
@@ -796,11 +811,11 @@ proc liftIterToProc*(fn: PSym; body: PNode; ptrType: PType): PNode =
   fn.typ.callConv = ccClosure
   d.ownerToType[fn.id] = ptrType
   detectCapturedVars(body, fn, d)
-  result = wrapIterBody(liftCapturedVars(body, fn, d, c), fn)
+  result = wrapIterBody(g, liftCapturedVars(body, fn, d, c), fn)
   fn.kind = oldKind
   fn.typ.callConv = oldCC
 
-proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
+proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PNode =
   # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
   # the transformation even when compiling to JS ...
 
@@ -808,23 +823,26 @@ proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
   let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
 
   if body.kind == nkEmpty or (
-      gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime) or
+      g.config.cmd == cmdCompileToJS and not isCompileTime) or
       fn.skipGenericOwner.kind != skModule:
+
     # ignore forward declaration:
     result = body
     tooEarly = true
   else:
-    var d = initDetectionPass(fn)
+    var d = initDetectionPass(g, fn)
     detectCapturedVars(body, fn, d)
     if not d.somethingToDo and fn.isIterator:
       addClosureParam(d, fn, body.info)
       d.somethingToDo = true
     if d.somethingToDo:
       var c = initLiftingPass(fn)
-      var newBody = liftCapturedVars(body, fn, d, c)
+      result = liftCapturedVars(body, fn, d, c)
       if c.envvars.getOrDefault(fn.id) != nil:
-        newBody = newTree(nkStmtList, rawClosureCreation(fn, d, c), newBody)
-      result = wrapIterBody(newBody, fn)
+        result = newTree(nkStmtList, rawClosureCreation(fn, d, c), result)
+
+      if oldIterTransf in g.config.features:
+        result = wrapIterBody(g, result, fn)
     else:
       result = body
     #if fn.name.s == "get2":
@@ -832,15 +850,12 @@ proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
     #  echo renderTree(result, {renderIds})
 
 proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
-  if body.kind == nkEmpty or gCmd == cmdCompileToJS:
-    result = body
-  else:
-    # XXX implement it properly
-    result = body
+  # XXX implement it properly
+  result = body
 
 # ------------------- iterator transformation --------------------------------
 
-proc liftForLoop*(body: PNode; owner: PSym): PNode =
+proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
   # problem ahead: the iterator could be invoked indirectly, but then
   # we don't know what environment to create here:
   #
@@ -865,13 +880,14 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
       cl = createClosure()
       while true:
         let i = foo(cl)
-        nkBreakState(cl.state)
+        if (nkBreakState(cl.state)):
+          break
         ...
     """
-  if liftingHarmful(owner): return body
+  if liftingHarmful(g.config, owner): return body
   var L = body.len
   if not (body.kind == nkForStmt and body[L-2].kind in nkCallKinds):
-    localError(body.info, "ignored invalid for loop")
+    localError(g.config, body.info, "ignored invalid for loop")
     return body
   var call = body[L-2]
 
@@ -884,7 +900,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
     # createClosure()
     let iter = op.sym
 
-    let hp = getHiddenParam(iter)
+    let hp = getHiddenParam(g, iter)
     env = newSym(skLet, iter.name, owner, body.info)
     env.typ = hp.typ
     env.flags = hp.flags
@@ -893,7 +909,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
     addVar(v, newSymNode(env))
     result.add(v)
     # add 'new' statement:
-    result.add(newCall(getSysSym"internalNew", env.newSymNode))
+    result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode))
   elif op.kind == nkStmtListExpr:
     let closure = op.lastSon
     if closure.kind == nkClosure:
@@ -903,7 +919,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
 
   var loopBody = newNodeI(nkStmtList, body.info, 3)
   var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
-  whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(tyBool))
+  whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(g, body.info, tyBool))
   whileLoop.sons[1] = loopBody
   result.add whileLoop
 
@@ -918,12 +934,23 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
 
   addSon(vpart, ast.emptyNode) # no explicit type
   if not env.isNil:
-    call.sons[0] = makeClosure(call.sons[0].sym, env.newSymNode, body.info)
+    call.sons[0] = makeClosure(g, call.sons[0].sym, env.newSymNode, body.info)
   addSon(vpart, call)
   addSon(v2, vpart)
 
   loopBody.sons[0] = v2
   var bs = newNodeI(nkBreakState, body.info)
   bs.addSon(call.sons[0])
-  loopBody.sons[1] = bs
+
+  let ibs = newNode(nkIfStmt)
+  let elifBranch = newNode(nkElifBranch)
+  elifBranch.add(bs)
+
+  let br = newNode(nkBreakStmt)
+  br.add(emptyNode)
+
+  elifBranch.add(br)
+  ibs.add(elifBranch)
+
+  loopBody.sons[1] = ibs
   loopBody.sons[2] = body[L-1]