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.nim52
1 files changed, 39 insertions, 13 deletions
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 2b0aba147..219fead2c 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -218,8 +218,29 @@ proc isInnerProc(s, outerProc: PSym): bool {.inline.} =
     s.owner == outerProc and not isGenericRoutine(s)
   #s.typ.callConv == ccClosure
 
+proc addClosureParam(i: PInnerContext, e: PEnv) =
+  var cp = newSym(skParam, getIdent(paramname), i.fn)
+  cp.info = i.fn.info
+  incl(cp.flags, sfFromGeneric)
+  cp.typ = newType(tyRef, i.fn)
+  addSon(cp.typ, e.tup)
+  i.closureParam = cp
+  addHiddenParam(i.fn, i.closureParam)
+  #echo "closure param added for ", i.fn.name.s, " ", i.fn.id
+
+proc dummyClosureParam(o: POuterContext, i: PInnerContext) =
+  var e = o.currentEnv
+  if IdTableGet(o.lambdasToEnv, i.fn) == nil:
+    IdTablePut(o.lambdasToEnv, i.fn, e)
+  if i.closureParam == nil: addClosureParam(i, e)
+
 proc captureVar(o: POuterContext, i: PInnerContext, local: PSym, 
                 info: TLineInfo) =
+  # for inlined variables the owner is still wrong, so it can happen that it's
+  # not a captured variable at all ... *sigh* 
+  var it = PEnv(IdTableGet(o.localsToEnv, local))
+  if it == nil: return
+
   # we need to remember which inner most closure belongs to this lambda:
   var e = o.currentEnv
   if IdTableGet(o.lambdasToEnv, i.fn) == nil:
@@ -227,19 +248,10 @@ proc captureVar(o: POuterContext, i: PInnerContext, local: PSym,
 
   # variable already captured:
   if IdNodeTableGet(i.localsToAccess, local) != nil: return
-  if i.closureParam == nil:
-    var cp = newSym(skParam, getIdent(paramname), i.fn)
-    cp.info = i.fn.info
-    incl(cp.flags, sfFromGeneric)
-    cp.typ = newType(tyRef, i.fn)
-    addSon(cp.typ, e.tup)
-    i.closureParam = cp
-    addHiddenParam(i.fn, i.closureParam)
+  if i.closureParam == nil: addClosureParam(i, e)
   
   # check which environment `local` belongs to:
   var access = newSymNode(i.closureParam)
-  var it = PEnv(IdTableGet(o.localsToEnv, local))
-  assert it != nil
   addCapturedVar(it, local)
   if it == e:
     # common case: local directly in current environment:
@@ -325,6 +337,9 @@ proc searchForInnerProcs(o: POuterContext, n: PNode) =
       var inner = newInnerContext(n.sym)
       let body = n.sym.getBody
       gatherVars(o, inner, body)
+      # dummy closure param needed?
+      if inner.closureParam == nil and n.sym.typ.callConv == ccClosure:
+        dummyClosureParam(o, inner)
       let ti = transformInnerProc(o, inner, body)
       if ti != nil: n.sym.ast.sons[bodyPos] = ti
   of nkLambdaKinds:
@@ -425,9 +440,19 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
     if closure != nil:
       # we need to replace the lambda with '(lambda, env)': 
       let a = closure.closure
-      assert a != nil
-      return makeClosure(local, a, n.info)
-  
+      if a != nil:
+        return makeClosure(local, a, n.info)
+      else:
+        # can happen for dummy closures:
+        var scope = closure.attachedNode
+        assert scope.kind == nkStmtList
+        if scope.sons[0].kind == nkEmpty:
+          # change the empty node to contain the closure construction:
+          scope.sons[0] = generateClosureCreation(o, closure)
+        let x = closure.closure
+        assert x != nil
+        return makeClosure(local, x, n.info)
+    
     if not contains(o.capturedVars, local.id): return
     var env = PEnv(IdTableGet(o.localsToEnv, local))
     if env == nil: return
@@ -474,5 +499,6 @@ proc liftLambdas(fn: PSym, body: PNode): PNode =
 
 proc liftLambdas*(n: PNode): PNode =
   assert n.kind in procDefs
+  if gCmd == cmdCompileToEcmaScript: return n
   var s = n.sons[namePos].sym
   result = liftLambdas(s, s.getBody)