summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rwxr-xr-xcompiler/ast.nim10
-rw-r--r--compiler/ccgcalls.nim71
-rwxr-xr-xcompiler/ccgexprs.nim30
-rwxr-xr-xcompiler/ccgstmts.nim19
-rwxr-xr-xcompiler/ccgtypes.nim32
-rwxr-xr-xcompiler/cgen.nim33
-rw-r--r--compiler/lambdalifting.nim183
-rwxr-xr-xcompiler/msgs.nim3
-rwxr-xr-xcompiler/renderer.nim4
-rwxr-xr-xcompiler/semexprs.nim14
-rwxr-xr-xcompiler/semstmts.nim5
-rwxr-xr-xcompiler/semthreads.nim2
-rwxr-xr-xcompiler/transf.nim10
-rwxr-xr-xcompiler/trees.nim2
-rwxr-xr-xcompiler/types.nim11
15 files changed, 303 insertions, 126 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 45b1ebfd0..65d7bccf1 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -189,7 +189,8 @@ type
     nkProcTy,             # proc type
     nkEnumTy,             # enum body
     nkEnumFieldDef,       # `ident = expr` in an enumeration
-    nkReturnToken         # token used for interpretation
+    nkReturnToken,        # token used for interpretation
+    nkClosure             # (prc, env)-pair (internally used for code gen)
   TNodeKinds* = set[TNodeKind]
 
 type
@@ -977,11 +978,12 @@ proc hasSonWith(n: PNode, kind: TNodeKind): bool =
   result = false
 
 proc containsNode*(n: PNode, kinds: TNodeKinds): bool =
+  if n == nil: return
   case n.kind
   of nkEmpty..nkNilLit: result = n.kind in kinds
   else:
     for i in countup(0, sonsLen(n) - 1):
-      if containsNode(n.sons[i], kinds): return true
+      if n.kind in kinds or containsNode(n.sons[i], kinds): return true
 
 proc hasSubnodeWith(n: PNode, kind: TNodeKind): bool = 
   case n.kind
@@ -1037,6 +1039,10 @@ proc isGenericRoutine*(s: PSym): bool =
     result = s.ast != nil and s.ast[genericParamsPos].kind != nkEmpty
   else: nil
 
+proc isRoutine*(s: PSym): bool {.inline.} =
+  result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod,
+                      skConverter}
+
 iterator items*(n: PNode): PNode =
   for i in 0.. <n.len: yield n.sons[i]
 
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index b468c4dee..d6e2d67b7 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -48,7 +48,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, pl: PRope) =
       if d.k == locNone: getTemp(p, typ.sons[0], d)
       assert(d.t != nil)        # generate an assignment to d:
       var list: TLoc
-      initLoc(list, locCall, nil, OnUnknown)
+      initLoc(list, locCall, d.t, OnUnknown)
       list.r = pl
       genAssignment(p, d, list, {}) # no need for deep copying
   else:
@@ -137,6 +137,67 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
     if i < length - 1: app(pl, ", ")
   fixupCall(p, le, ri, d, pl)
 
+proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
+
+  proc getRawProcType(p: BProc, t: PType): PRope = 
+    var d = copyType(t, t.owner, false)
+    d.callConv = ccDefault
+    result = getTypeDesc(p.module, d)
+
+  proc addComma(r: PRope): PRope =
+    result = if r == nil: r else: con(r, ", ")
+
+  const CallPattern = "$1.ClEnv? $1.ClPrc($3$1.ClEnv) : (($4)($1.ClPrc))($2)"
+  var op: TLoc
+  initLocExpr(p, ri.sons[0], op)
+  var pl: PRope
+  var typ = ri.sons[0].typ
+  assert(typ.kind == tyProc)
+  var length = sonsLen(ri)
+  for i in countup(1, length - 1):
+    assert(sonsLen(typ) == sonsLen(typ.n))
+    if i < sonsLen(typ):
+      assert(typ.n.sons[i].kind == nkSym)
+      app(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym))
+    else:
+      app(pl, genArgNoParam(p, ri.sons[i]))
+    if i < length - 1: app(pl, ", ")
+  
+  template genCallPattern =
+    appf(p.s[cpsStmts], CallPattern, op.r, pl, pl.addComma, rawProc)
+
+  let rawProc = getRawProcType(p, typ)
+  if typ.sons[0] != nil:
+    if isInvalidReturnType(typ.sons[0]):
+      if sonsLen(ri) > 1: app(pl, ", ")
+      # beware of 'result = p(result)'. We may need to allocate a temporary:
+      if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
+        # Great, we can use 'd':
+        if d.k == locNone: getTemp(p, typ.sons[0], d)
+        elif d.k notin {locExpr, locTemp} and not hasNoInit(ri):
+          # reset before pass as 'result' var:
+          resetLoc(p, d)
+        app(pl, addrLoc(d))
+        genCallPattern()
+        appf(p.s[cpsStmts], ";$n")
+      else:
+        var tmp: TLoc
+        getTemp(p, typ.sons[0], tmp)
+        app(pl, addrLoc(tmp))        
+        genCallPattern()
+        appf(p.s[cpsStmts], ";$n")
+        genAssignment(p, d, tmp, {}) # no need for deep copying
+    else:
+      if d.k == locNone: getTemp(p, typ.sons[0], d)
+      assert(d.t != nil)        # generate an assignment to d:
+      var list: TLoc
+      initLoc(list, locCall, d.t, OnUnknown)
+      list.r = ropef(CallPattern, op.r, pl, pl.addComma, rawProc)
+      genAssignment(p, d, list, {}) # no need for deep copying
+  else:
+    genCallPattern()
+    appf(p.s[cpsStmts], ";$n")
+  
 proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
   var op, a: TLoc
   initLocExpr(p, ri.sons[0], op)
@@ -224,7 +285,9 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
     appf(p.s[cpsStmts], ";$n")
 
 proc genCall(p: BProc, e: PNode, d: var TLoc) =
-  if e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags and
+  if e.sons[0].typ.callConv == ccClosure:
+    genClosureCall(p, nil, e, d)
+  elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags and
       e.len >= 2:
     genInfixCall(p, nil, e, d)
   elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags:
@@ -235,7 +298,9 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) =
     if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
 
 proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
-  if ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags and
+  if ri.sons[0].typ.callConv == ccClosure:
+    genClosureCall(p, le, ri, d)
+  elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags and
       ri.len >= 2:
     genInfixCall(p, le, ri, d)
   elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags:
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index ef412d753..6a8156220 100755
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -244,7 +244,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
         appcg(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n",
              [addrLoc(dest), rdLoc(src)])
         if needToKeepAlive in flags: keepAlive(p, dest)
-  of tyTuple, tyObject:
+  of tyTuple, tyObject, tyProc:
     # XXX: check for subtyping?
     if needsComplexAssignment(dest.t):
       genGenericAsgn(p, dest, src, flags)
@@ -274,7 +274,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
            [rdLoc(dest), rdLoc(src), toRope(getSize(dest.t))])
     else:
       appcg(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
-  of tyPtr, tyPointer, tyChar, tyBool, tyProc, tyEnum, tyCString,
+  of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString,
      tyInt..tyFloat128, tyRange:
     appcg(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
   else: InternalError("genAssignment(" & $ty.kind & ')')
@@ -1308,7 +1308,6 @@ proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
       [rdLoc(a)]))
 
 proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
-  # XXX we don't generate keep alive info here
   var a: TLoc
   initLocExpr(p, n.sons[0], a)
   putIntoDest(p, d, skipTypes(n.typ, abstractVar),
@@ -1515,6 +1514,28 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
                       [rdLoc(d), mangleRecFieldName(t.n.sons[i].sym, t)])
         expr(p, it, rec)
 
+proc IsConstClosure(n: PNode): bool {.inline.} =
+  result = n.sons[0].kind == nkSym and isRoutine(n.sons[0].sym) and
+      n.sons[1].kind == nkNilLit
+      
+proc genClosure(p: BProc, n: PNode, d: var TLoc) =
+  assert n.kind == nkClosure
+  
+  if IsConstClosure(n):
+    inc(p.labels)
+    var tmp = con("LOC", toRope(p.labels))
+    appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
+        [getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)])
+    putIntoDest(p, d, n.typ, tmp)
+  else:
+    var tmp, a, b: TLoc
+    initLocExpr(p, n.sons[0], a)
+    initLocExpr(p, n.sons[1], b)
+    getTemp(p, n.typ, tmp)
+    appcg(p, cpsStmts, "$1.ClPrc = $2; $1.ClEnv = $3;$n",
+          tmp.rdLoc, a.rdLoc, b.rdLoc)
+    putLocIntoDest(p, d, tmp)
+
 proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) =
   var arr: TLoc
   if not handleConstExpr(p, n, d):
@@ -1705,6 +1726,7 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
     if sym.loc.r == nil or sym.loc.t == nil:
       InternalError(e.info, "expr: proc not init " & sym.name.s)
     putLocIntoDest(p, d, sym.loc)
+  of nkClosure: genClosure(p, e, d)
   of nkMetaNode: expr(p, e.sons[0], d)
   else: InternalError(e.info, "expr(" & $e.kind & "); unknown node kind")
 
@@ -1751,7 +1773,7 @@ proc genConstExpr(p: BProc, n: PNode): PRope =
     var cs: TBitSet
     toBitSet(n, cs)
     result = genRawSetData(cs, int(getSize(n.typ)))
-  of nkBracket, nkPar:
+  of nkBracket, nkPar, nkClosure:
     var t = skipTypes(n.typ, abstractInst)
     if t.kind == tySequence:
       result = genConstSeq(p, n, t)
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index ad1b5646f..8e7b05c0f 100755
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -57,13 +57,24 @@ proc genSingleVar(p: BProc, a: PNode) =
     genLineDir(p, a)
     loadInto(p, a.sons[0], a.sons[2], v.loc)
 
+proc genClosureVar(p: BProc, a: PNode) =
+  var immediateAsgn = a.sons[2].kind != nkEmpty
+  if immediateAsgn:
+    var v: TLoc
+    initLocExpr(p, a.sons[0], v)
+    genLineDir(p, a)
+    loadInto(p, a.sons[0], a.sons[2], v)
+
 proc genVarStmt(p: BProc, n: PNode) = 
   for i in countup(0, sonsLen(n) - 1): 
     var a = n.sons[i]
     if a.kind == nkCommentStmt: continue 
-    if a.kind == nkIdentDefs: 
-      assert(a.sons[0].kind == nkSym)
-      genSingleVar(p, a)
+    if a.kind == nkIdentDefs:
+      # can be a lifted var nowadays ...
+      if a.sons[0].kind == nkSym:
+        genSingleVar(p, a)
+      else:
+        genClosureVar(p, a)
     else:
       genVarTuple(p, a)
 
@@ -704,7 +715,7 @@ proc genStmts(p: BProc, t: PNode) =
   of nkReturnStmt: genReturnStmt(p, t)
   of nkBreakStmt: genBreakStmt(p, t)
   of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand, 
-     nkCallStrLit: 
+     nkCallStrLit, nkClosure: 
     genLineDir(p, t)
     initLocExpr(p, t, a)
   of nkAsgn: genAsgn(p, t, fastAsgn=false)
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index a2faf3cbf..c7002954a 100755
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -94,7 +94,7 @@ proc mapType(typ: PType): TCTypeKind =
     else: result = ctPtr
   of tyPointer: result = ctPtr
   of tySequence: result = ctNimSeq
-  of tyProc: result = ctProc
+  of tyProc: result = if typ.callConv != ccClosure: ctProc else: ctStruct
   of tyString: result = ctNimStr
   of tyCString: result = ctCString
   of tyInt..tyFloat128:
@@ -215,7 +215,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var PRope,
     appff(params, " Result", " @Result", [])
   if t.callConv == ccClosure: 
     if params != nil: app(params, ", ")
-    app(params, "void* ClPart")
+    app(params, "void* ClEnv")
   if tfVarargs in t.flags: 
     if params != nil: app(params, ", ")
     app(params, "...")
@@ -331,7 +331,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
     appf(result, "} $1;$n", [uname])
   of nkSym: 
     field = n.sym
-    assert(field.ast == nil)
+    #assert(field.ast == nil)
     sname = mangleRecFieldName(field, rectype)
     if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname])
     else: ae = sname
@@ -436,9 +436,10 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
       if t.callConv != ccClosure: # procedure vars may need a closure!
         appf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n", 
              [toRope(CallingConvToStr[t.callConv]), rettype, result, desc])
-      else: 
-        appf(m.s[cfsTypes], "typedef struct $1 {$n" &
-            "N_CDECL_PTR($2, PrcPart) $3;$n" & "void* ClPart;$n};$n", 
+      else:
+        appf(m.s[cfsTypes], "typedef struct {$n" &
+            "N_CDECL_PTR($2, ClPrc) $3;$n" & 
+            "void* ClEnv;$n} $1;$n",
              [result, rettype, desc])
   of tySequence: 
     # we cannot use getTypeForward here because then t would be associated
@@ -673,7 +674,8 @@ proc genTupleInfo(m: BModule, typ: PType, name: PRope) =
       var tmp2 = getNimNode(m)
       appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, toRope(i), tmp2])
       appf(m.s[cfsTypeInit3], "$1.kind = 1;$n" &
-          "$1.offset = offsetof($2, Field$3);$n" & "$1.typ = $4;$n" &
+          "$1.offset = offsetof($2, Field$3);$n" & 
+          "$1.typ = $4;$n" &
           "$1.name = \"Field$3\";$n", 
            [tmp2, getTypeDesc(m, typ), toRope(i), genTypeInfo(m, a)])
     appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", 
@@ -736,6 +738,14 @@ proc genSetInfo(m: BModule, typ: PType, name: PRope) =
 proc genArrayInfo(m: BModule, typ: PType, name: PRope) = 
   genTypeInfoAuxBase(m, typ, name, genTypeInfo(m, typ.sons[1]))
 
+proc fakeClosureType(owner: PSym): PType =
+  # we generate the same RTTI as for a tuple[pointer, ref tuple[]]
+  result = newType(tyTuple, owner)
+  result.addSon(newType(tyPointer, owner))
+  var r = newType(tyRef, owner)
+  r.addSon(newType(tyTuple, owner))
+  result.addSon(r)
+
 proc genTypeInfo(m: BModule, typ: PType): PRope = 
   var t = getUniqueType(typ)
   # gNimDat contains all the type information nowadays:
@@ -750,9 +760,13 @@ proc genTypeInfo(m: BModule, typ: PType): PRope =
   if dataGenerated: return 
   case t.kind
   of tyEmpty: result = toRope"0"
-  of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128, 
-     tyVar: 
+  of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyFloat128, tyVar:
     genTypeInfoAuxBase(gNimDat, t, result, toRope"0")
+  of tyProc:
+    if t.callConv != ccClosure:
+      genTypeInfoAuxBase(gNimDat, t, result, toRope"0")
+    else:
+      genTupleInfo(gNimDat, fakeClosureType(t.owner), result)
   of tyRef, tyPtr, tySequence, tyRange: genTypeInfoAux(gNimDat, t, result)
   of tyArrayConstr, tyArray: genArrayInfo(gNimDat, t, result)
   of tySet: genSetInfo(gNimDat, t, result)
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index d60f11639..9784b21bb 100755
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -75,8 +75,10 @@ proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) =
     if a.r == nil: a.r = r
   
 proc isSimpleConst(typ: PType): bool =
-  result = skipTypes(typ, abstractVar).kind notin
-      {tyTuple, tyObject, tyArray, tyArrayConstr, tySet, tySequence}
+  let t = skipTypes(typ, abstractVar)
+  result = t.kind notin
+      {tyTuple, tyObject, tyArray, tyArrayConstr, tySet, tySequence} and not
+      (t.kind == tyProc and t.callConv == ccClosure)
 
 proc useHeader(m: BModule, sym: PSym) = 
   if lfHeader in sym.loc.Flags: 
@@ -187,7 +189,7 @@ proc rdLoc(a: TLoc): PRope =
 
 proc addrLoc(a: TLoc): PRope =
   result = a.r
-  if lfIndirect notin a.flags and mapType(a.t) != ctArray: 
+  if lfIndirect notin a.flags and mapType(a.t) != ctArray:
     result = con("&", result)
 
 proc rdCharLoc(a: TLoc): PRope =
@@ -196,7 +198,7 @@ proc rdCharLoc(a: TLoc): PRope =
   if skipTypes(a.t, abstractRange).kind == tyChar:
     result = ropef("((NU8)($1))", [result])
 
-proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc, 
+proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
                    takeAddr: bool) =
   case analyseObjectWithTypeField(t)
   of frNone:
@@ -223,11 +225,12 @@ type
 
 proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
 
-const
-  complexValueType = {tyArray, tyArrayConstr, tySet, tyTuple, tyObject}
+proc isComplexValueType(t: PType): bool {.inline.} =
+  result = t.kind in {tyArray, tyArrayConstr, tySet, tyTuple, tyObject} or
+    (t.kind == tyProc and t.callConv == ccClosure)
 
 proc zeroVar(p: BProc, loc: TLoc, containsGCref: bool) = 
-  if skipTypes(loc.t, abstractVarRange).Kind notin ComplexValueType: 
+  if not isComplexValueType(skipTypes(loc.t, abstractVarRange)):
     if containsGcref and p.WithInLoop > 0:
       appf(p.s[cpsInit], "$1 = 0;$n", [rdLoc(loc)])
       var nilLoc: TLoc
@@ -249,8 +252,8 @@ proc zeroVar(p: BProc, loc: TLoc, containsGCref: bool) =
            [addrLoc(loc), rdLoc(loc)])
       genObjectInit(p, cpsStmts, loc.t, loc, true)
 
-proc zeroTemp(p: BProc, loc: TLoc) = 
-  if skipTypes(loc.t, abstractVarRange).Kind notin complexValueType: 
+proc zeroTemp(p: BProc, loc: TLoc) =
+  if not isComplexValueType(skipTypes(loc.t, abstractVarRange)):
     appf(p.s[cpsStmts], "$1 = 0;$n", [rdLoc(loc)])
     when false:
       var nilLoc: TLoc
@@ -313,7 +316,7 @@ proc keepAlive(p: BProc, toKeepAlive: TLoc) =
     result.s = OnStack
     result.flags = {}
 
-    if skipTypes(toKeepAlive.t, abstractVarRange).Kind notin complexValueType:
+    if not isComplexValueType(skipTypes(toKeepAlive.t, abstractVarRange)):
       appf(p.s[cpsStmts], "$1 = $2;$n", [rdLoc(result), rdLoc(toKeepAlive)])
     else:
       appcg(p, cpsStmts,
@@ -571,6 +574,15 @@ proc initFrame(p: BProc, procname, filename: PRope): PRope =
 proc deinitFrame(p: BProc): PRope =
   result = ropecg(p.module, "#popFrame();$n")
 
+proc closureSetup(p: BProc, prc: PSym) =
+  if prc.typ.callConv != ccClosure: return
+  # prc.ast[paramsPos].last contains the type we're after:
+  var env = lastSon(prc.ast[paramsPos]).sym
+  assignLocalVar(p, env)
+  # generate cast assignment:
+  appcg(p, cpsStmts, "$1 = ($2) ClEnv;$n", rdLoc(env.loc), 
+        getTypeDesc(p.module, env.typ))
+
 proc genProcAux(m: BModule, prc: PSym) =
   var p = newProc(prc, m)
   var header = genProcHeader(m, prc)
@@ -594,6 +606,7 @@ proc genProcAux(m: BModule, prc: PSym) =
   for i in countup(1, sonsLen(prc.typ.n) - 1): 
     var param = prc.typ.n.sons[i].sym
     assignParam(p, param)
+  closureSetup(p, prc)
   genStmts(p, prc.getBody) # modifies p.locals, p.init, etc.
   var generatedProc: PRope
   if sfPure in prc.flags: 
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 8aa490bbb..4a2a8997c 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -9,34 +9,46 @@
 
 # This include file implements lambda lifting for the transformator.
 
-# - Things to consider: Does capturing of 'result' work? (unknown)
-# - Do generic inner procs work? (should)
-# - Does nesting of closures work? (not yet)
-# - Test that iterators within closures work etc.
-
 const
   procDefs = {nkLambda, nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef,
      nkConverterDef}
 
-proc indirectAccess(a, b: PSym): PNode = 
+proc indirectAccess(a, b: PSym, info: TLineInfo): PNode = 
   # returns a[].b as a node
-  var x = newSymNode(a)
-  var y = newSymNode(b)
-  var deref = newNodeI(nkHiddenDeref, x.info)
+  let x = newSymNode(a)
+  var deref = newNodeI(nkHiddenDeref, info)
   deref.typ = x.typ.sons[0]
+  
+  let field = getSymFromList(deref.typ.n, b.name)
   addSon(deref, x)
-  result = newNodeI(nkDotExpr, x.info)
+  result = newNodeI(nkDotExpr, info)
   addSon(result, deref)
-  addSon(result, y)
-  result.typ = y.typ
+  addSon(result, newSymNode(field))
+  result.typ = field.typ
+
+type
+  TCapture = seq[PSym]
 
-proc Incl(container: PNode, s: PSym) =
-  for x in container:
-    if x.sym.id == s.id: return
-  container.add(newSymNode(s))
+proc Capture(cap: var TCapture, s: PSym) = 
+  for x in cap:
+    if x.name.id == s.name.id: return
+  cap.add(s)
 
-proc gatherVars(c: PTransf, n: PNode, owner: PSym, container: PNode) = 
-  # gather used vars for closure generation
+proc captureToTuple(cap: TCapture, owner: PSym): PType =
+  result = newType(tyTuple, owner)
+  result.n = newNodeI(nkRecList, owner.info)
+  for s in cap:
+    var field = newSym(skField, s.name, s.owner)
+    
+    let typ = s.typ
+    field.typ = typ
+    field.position = sonsLen(result)
+    
+    addSon(result.n, newSymNode(field))
+    addSon(result, typ)
+
+proc gatherVars(c: PTransf, n: PNode, outerProc: PSym, cap: var TCapture) = 
+  # gather used vars for closure generation into 'cap'
   case n.kind
   of nkSym:
     var s = n.sym
@@ -45,82 +57,88 @@ proc gatherVars(c: PTransf, n: PNode, owner: PSym, container: PNode) =
     of skVar, skLet: found = sfGlobal notin s.flags
     of skTemp, skForVar, skParam, skResult: found = true
     else: nil
-    if found and owner.id != s.owner.id:
-      incl(container, s)
+    if found and outerProc.id == s.owner.id:
+      #echo "captured: ", s.name.s
+      Capture(cap, s)
   of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil
-  else: 
+  else:
     for i in countup(0, sonsLen(n) - 1): 
-      gatherVars(c, n.sons[i], owner, container)
+      gatherVars(c, n.sons[i], outerProc, cap)
 
-proc replaceVars(c: PTransf, n: PNode, owner, env: PSym) = 
+proc replaceVars(c: PTransf, n: PNode, outerProc, env: PSym) = 
   for i in countup(0, safeLen(n) - 1):
-    if n.kind == nkSym:
-      let s = n.sym
+    let a = n.sons[i]
+    if a.kind == nkSym:
+      let s = a.sym
       var found = false
       case s.kind
       of skVar, skLet: found = sfGlobal notin s.flags
       of skTemp, skForVar, skParam, skResult: found = true
       else: nil
-      if found and owner.id != s.owner.id:
+      if found and outerProc.id == s.owner.id:
         # access through the closure param:
-        n.sons[i] = indirectAccess(env, s)
+        n.sons[i] = indirectAccess(env, s, n.info)
     else:
-      replaceVars(c, n.sons[i], owner, env)
- 
+      replaceVars(c, a, outerProc, env)
+
+proc addFormalParam(routine: PType, param: PSym) =
+  addSon(routine, param.typ)
+  addSon(routine.n, newSymNode(param))
+
 proc addFormalParam(routine: PSym, param: PSym) = 
-  addSon(routine.typ, param.typ)
+  #addFormalParam(routine.typ, param)
   addSon(routine.ast.sons[paramsPos], newSymNode(param))
 
-proc isInnerProc(s, owner: PSym): bool {.inline.} =
+proc isInnerProc(s, outerProc: PSym): bool {.inline.} =
   result = s.kind in {skProc, skMacro, skIterator, skMethod, skConverter} and
-    s.owner.id == owner.id and not isGenericRoutine(s)
+    s.owner.id == outerProc.id and not isGenericRoutine(s) and
+    s.typ.callConv == ccClosure
 
-proc searchForInnerProcs(c: PTransf, n: PNode, owner: PSym, container: PNode) =
+proc searchForInnerProcs(c: PTransf, n: PNode, outerProc: PSym,
+                         cap: var TCapture) =
   case n.kind
   of nkSym:
     let s = n.sym
-    if isInnerProc(s, owner):
-      gatherVars(c, s.getBody, owner, container)
+    if isInnerProc(s, outerProc):
+      gatherVars(c, s.getBody, outerProc, cap)
   of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil
   else:
     for i in 0.. <len(n):
-      searchForInnerProcs(c, n.sons[i], owner, container)
+      searchForInnerProcs(c, n.sons[i], outerProc, cap)
   
-proc makeClosure(c: PTransf, prc, env: PSym): PNode =
-  var tup = newType(tyTuple, c.module)
-  tup.addson(prc.typ)
-  tup.addson(env.typ)
-  result = newNodeIT(nkPar, prc.info, tup)
+proc makeClosure(c: PTransf, prc, env: PSym, info: TLineInfo): PNode =
+  result = newNodeIT(nkClosure, info, prc.typ)
   result.add(newSymNode(prc))
   result.add(newSymNode(env))
   
-proc transformInnerProcs(c: PTransf, n: PNode, owner, env: PSym) =
+proc transformInnerProcs(c: PTransf, n: PNode, outerProc, env: PSym) =
   case n.kind
   of nkSym:
     let innerProc = n.sym
-    if isInnerProc(innerProc, owner):
+    if isInnerProc(innerProc, outerProc):
       # inner proc could capture outer vars:
       var param = newTemp(c, env.typ, n.info)
       param.kind = skParam
       addFormalParam(innerProc, param)
       # 'anon' should be replaced by '(anon, env)':
       IdNodeTablePut(c.transCon.mapping, innerProc, 
-                     makeClosure(c, innerProc, env))
-      
+                     makeClosure(c, innerProc, env, n.info))
       # access all non-local vars through the 'env' param:
       var body = innerProc.getBody
-      replaceVars(c, body, innerProc, param)
+      # XXX does not work with recursion!
+      replaceVars(c, body, outerProc, param)
+      innerProc.ast.sons[bodyPos] = body
   of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil
   else:
     for i in 0.. <len(n):
-      transformInnerProcs(c, n.sons[i], owner, env)
+      transformInnerProcs(c, n.sons[i], outerProc, env)
 
 proc newCall(a, b: PSym): PNode =
   result = newNodeI(nkCall, a.info)
   result.add newSymNode(a)
   result.add newSymNode(b)
 
-proc createEnvStmt(c: PTransf, varList: PNode, env: PSym): PTransNode =
+proc createEnvStmt(c: PTransf, varList: TCapture, env: PSym): PTransNode =
   # 'varlist' can contain parameters or variables. We don't eliminate yet
   # local vars that end up in an environment. This could even be a for loop
   # var!
@@ -129,17 +147,33 @@ proc createEnvStmt(c: PTransf, varList: PNode, env: PSym): PTransNode =
   addVar(v, newSymNode(env))
   result.add(v.ptransNode)
   # add 'new' statement:
-  result.add(newCall(getSysSym"new", env).ptransnode)
+  result.add(newCall(getSysSym"internalNew", env).ptransnode)
   
   # add assignment statements:
   for v in varList:
-    assert v.kind == nkSym
-    let fieldAccess = indirectAccess(env, v.sym)
-    if v.sym.kind == skParam:
+    let fieldAccess = indirectAccess(env, v, env.info)
+    if v.kind == skParam:
       # add ``env.param = param``
-      result.add(newAsgnStmt(c, fieldAccess, v.ptransNode))
-    IdNodeTablePut(c.transCon.mapping, v.sym, fieldAccess)
+      result.add(newAsgnStmt(c, fieldAccess, newSymNode(v).ptransNode))
+    IdNodeTablePut(c.transCon.mapping, v, fieldAccess)
+  
+proc transformProcFin(c: PTransf, n: PNode, s: PSym): PTransNode =
+  # to be safe: XXX this a mystery how it could ever happen that: s.ast != n.
+  s.ast.sons[bodyPos] = n.sons[bodyPos]
+  if n.kind == nkMethodDef: methodDef(s, false)
   
+  # should 's' be replaced by a tuple ('s', env)?
+  var tc = c.transCon
+  var repl: PNode = nil
+  while tc != nil:
+    repl = IdNodeTableGet(tc.mapping, s)
+    if repl != nil: break
+    tc = tc.next
+  if repl != nil:
+    result = PTransNode(repl)
+  else:
+    result = PTransNode(n)
+
 proc transformProc(c: PTransf, n: PNode): PTransNode =
   # don't process generics:
   if n.sons[genericParamsPos].kind != nkEmpty:
@@ -147,34 +181,32 @@ proc transformProc(c: PTransf, n: PNode): PTransNode =
   
   var s = n.sons[namePos].sym
   var body = s.getBody
+  if body.kind == nkEmpty:
+    return PTransNode(n)    
+  
   if not containsNode(body, procDefs):
     # fast path: no inner procs, so no closure needed:
     n.sons[bodyPos] = PNode(transform(c, body))
-    if n.kind == nkMethodDef: methodDef(s, false)
-    return PTransNode(n)
+    return transformProcFin(c, n, s)
 
-  var closure = newNodeI(nkRecList, n.info)
-  searchForInnerProcs(c, body, s, closure)
+  # create environment:
+  var cap: TCapture = @[]
+  searchForInnerProcs(c, body, s, cap)
   
-  if closure.len == 0:
+  if cap.len == 0:
     # fast path: no captured variables, so no closure needed:
     n.sons[bodyPos] = PNode(transform(c, body))
-    if n.kind == nkMethodDef: methodDef(s, false)
-    return PTransNode(n)
+    return transformProcFin(c, n, s)
   
-  # create environment:
-  var envDesc = newType(tyObject, s)
-  envDesc.n = closure
-  addSon(envDesc, nil) # no super class
   var envType = newType(tyRef, s)
-  addSon(envType, envDesc)
+  addSon(envType, captureToTuple(cap, s))
   
-  # XXX currently we always do a heap allocation. A simple escape analysis
-  # could turn the closure into a stack allocation. Later versions will 
-  # implement that.
+  # Currently we always do a heap allocation. A simple escape analysis
+  # could turn the closure into a stack allocation. Later versions might 
+  # implement that. This would require backend changes too though.
   var envSym = newTemp(c, envType, s.info)
   
-  var newBody = createEnvStmt(c, closure, envSym)
+  var newBody = createEnvStmt(c, cap, envSym)
   # modify any local proc to gain a new parameter; this also creates the
   # mapping entries that turn (localProc) into (localProc, env):
   transformInnerProcs(c, body, s, envSym)
@@ -183,19 +215,18 @@ proc transformProc(c: PTransf, n: PNode): PTransNode =
   # Careful this transforms the inner procs too!
   newBody.add(transform(c, body))
   n.sons[bodyPos] = newBody.pnode
-  if n.kind == nkMethodDef: methodDef(s, false)
-  result = newBody
+  result = transformProcFin(c, n, s)
 
-proc generateThunk(c: PTransf, prc: PNode, closure: PType): PNode =
+proc generateThunk(c: PTransf, prc: PNode, dest: PType): PNode =
   ## Converts 'prc' into '(thunk, nil)' so that it's compatible with
   ## a closure.
   
   # XXX we hack around here by generating a 'cast' instead of a proper thunk.
-  result = newNodeIT(nkPar, prc.info, closure)
-  var conv = newNodeIT(nkHiddenStdConv, prc.info, closure.sons[0])
+  result = newNodeIT(nkClosure, prc.info, dest)
+  var conv = newNodeIT(nkHiddenStdConv, prc.info, dest)
   conv.add(emptyNode)
   conv.add(prc)
   result.add(conv)
-  result.add(newNodeIT(nkNilLit, prc.info, closure.sons[1]))
+  result.add(newNodeIT(nkNilLit, prc.info, getSysType(tyNil)))
   
 
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index e1deb6e35..43ab7a192 100755
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -93,7 +93,7 @@ type
     errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, 
     errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, 
     errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
-    errThreadvarCannotInit, errWrongSymbolX,
+    errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
     errUser,
     warnCannotOpenFile, 
     warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, 
@@ -325,6 +325,7 @@ const
     errLetNeedsInit: "'let' symbol requires an initialization",
     errThreadvarCannotInit: "a thread var cannot be initialized explicitly",
     errWrongSymbolX: "usage of \'$1\' is a user-defined error", 
+    errIllegalCaptureX: "illegal capture '$1'",
     errUser: "$1", 
     warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
     warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", 
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 6fb5da2a9..dd62363e1 100755
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -339,7 +339,7 @@ proc lsub(n: PNode): int =
   of nkHiddenAddr, nkHiddenDeref: result = lsub(n.sons[0])
   of nkCommand: result = lsub(n.sons[0]) + lcomma(n, 1) + 1
   of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(n) + 3
-  of nkPar, nkCurly, nkBracket: result = lcomma(n) + 2
+  of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(n) + 2
   of nkTableConstr:
     result = if n.len > 0: lcomma(n) + 2 else: len("{:}")
   of nkSymChoice: result = lsons(n) + len("()") + sonsLen(n) - 1
@@ -759,7 +759,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
       if i > 0: put(g, tkOpr, "|")
       gsub(g, n.sons[i], c)
     put(g, tkParRi, ")")
-  of nkPar: 
+  of nkPar, nkClosure: 
     put(g, tkParLe, "(")
     gcomma(g, n, c)
     put(g, tkParRi, ")")
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 32244af85..24161e85e 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -50,6 +50,10 @@ proc inlineConst(n: PNode, s: PSym): PNode {.inline.} =
   result.typ = s.typ
   result.info = n.info
 
+proc illegalCapture(s: PSym): bool {.inline.} =
+  result = skipTypes(s.typ, abstractInst).kind in {tyVar, tyOpenArray} or
+      s.kind == skResult
+
 proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = 
   case s.kind
   of skProc, skMethod, skIterator, skConverter: 
@@ -83,10 +87,16 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
       result = newSymNode(s, n.info)
   of skMacro: result = semMacroExpr(c, n, s)
   of skTemplate: result = semTemplateExpr(c, n, s)
-  of skVar, skLet, skResult:
+  of skVar, skLet, skResult, skParam:
     markUsed(n, s)
     # if a proc accesses a global variable, it is not side effect free:
-    if sfGlobal in s.flags: incl(c.p.owner.flags, sfSideEffect)
+    if sfGlobal in s.flags:
+      incl(c.p.owner.flags, sfSideEffect)
+    elif s.owner != c.p.owner and s.owner.kind != skModule:
+      c.p.owner.typ.callConv = ccClosure
+      if illegalCapture(s) or c.p.next.owner != s.owner:
+        # Currently captures are restricted to a single level of nesting:
+        GlobalError(n.info, errIllegalCaptureX, s.name.s)
     result = newSymNode(s, n.info)
   of skGenericParam:
     if s.ast == nil: InternalError(n.info, "no default for")
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index ab8081031..424950056 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -644,8 +644,6 @@ proc semLambda(c: PContext, n: PNode): PNode =
   else:
     s.typ = newTypeS(tyProc, c)
     addSon(s.typ, nil)
-  # no! do a proper analysis to determine calling convention
-  when false: s.typ.callConv = ccClosure
   if n.sons[pragmasPos].kind != nkEmpty:
     pragma(c, s, n.sons[pragmasPos], lambdaPragmas)
   s.options = gOptions
@@ -697,9 +695,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
                                                # open for parameters
   if proto == nil: 
     s.typ.callConv = lastOptionEntry(c).defaultCC 
-    when false:
-      # do a proper analysis here:
-      if c.p.owner.kind != skModule: s.typ.callConv = ccClosure
     # add it here, so that recursive procs are possible:
     # -2 because we have a scope open for parameters
     if kind in OverloadableSyms: 
diff --git a/compiler/semthreads.nim b/compiler/semthreads.nim
index 59ff4e652..f542e04d6 100755
--- a/compiler/semthreads.nim
+++ b/compiler/semthreads.nim
@@ -360,7 +360,7 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
     if n.sons[0].kind != nkEmpty: result = analyse(c, n.sons[0])
     else: result = toVoid
   of nkAsmStmt, nkPragma, nkIteratorDef, nkProcDef, nkMethodDef,
-     nkConverterDef, nkMacroDef, nkTemplateDef: 
+     nkConverterDef, nkMacroDef, nkTemplateDef, nkLambda: 
       result = toVoid
   of nkExprColonExpr:
     result = analyse(c, n.sons[1])
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 51188928c..be8f7aeb5 100755
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -15,6 +15,7 @@
 # * performes contant folding
 # * converts "continue" to "break"
 # * introduces method dispatchers
+# * performs lambda lifting for closure support
 
 import 
   intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, 
@@ -355,6 +356,8 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
     if n.sons[0].kind == a or n.sons[0].kind == b:
       # addr ( deref ( x )) --> x
       result = PTransNode(n.sons[0].sons[0])
+
+include lambdalifting
   
 proc transformConv(c: PTransf, n: PNode): PTransNode = 
   # numeric types need range checks:
@@ -428,6 +431,11 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
       result[0] = transform(c, n.sons[1])
     else: 
       result = transform(c, n.sons[1])
+  of tyProc:
+    if dest.callConv == ccClosure and source.callConv == ccDefault:
+      result = generateThunk(c, n.sons[1], dest).ptransnode
+    else:
+      result = transformSons(c, n)    
   of tyGenericParam, tyOrdinal: 
     result = transform(c, n.sons[1])
     # happens sometimes for generated assignments, etc.
@@ -510,8 +518,6 @@ proc getMagicOp(call: PNode): TMagic =
   else:
     result = mNone
 
-include lambdalifting
-
 proc transformCase(c: PTransf, n: PNode): PTransNode = 
   # removes `elif` branches of a case stmt
   # adds ``else: nil`` if needed for the code generator
diff --git a/compiler/trees.nim b/compiler/trees.nim
index f393bfc66..57acfba8a 100755
--- a/compiler/trees.nim
+++ b/compiler/trees.nim
@@ -115,7 +115,7 @@ proc isDeepConstExpr*(n: PNode): bool =
     result = true
   of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
     result = isDeepConstExpr(n.sons[1])
-  of nkCurly, nkBracket, nkPar:
+  of nkCurly, nkBracket, nkPar, nkClosure:
     for i in 0 .. <n.len:
       if not isDeepConstExpr(n.sons[i]): return false
     result = true
diff --git a/compiler/types.nim b/compiler/types.nim
index 42acae831..d2359faae 100755
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -276,16 +276,17 @@ proc analyseObjectWithTypeField(t: PType): TTypeFieldResult =
   var marker = InitIntSet()
   result = analyseObjectWithTypeFieldAux(t, marker)
 
-proc isGBCRef(t: PType): bool = 
-  result = t.kind in GcTypeKinds
+proc isGCRef(t: PType): bool =
+  result = t.kind in GcTypeKinds or
+    (t.kind == tyProc and t.callConv == ccClosure)
 
 proc containsGarbageCollectedRef(typ: PType): bool = 
   # returns true if typ contains a reference, sequence or string (all the
   # things that are garbage-collected)
-  result = searchTypeFor(typ, isGBCRef)
+  result = searchTypeFor(typ, isGCRef)
 
 proc isTyRef(t: PType): bool =
-  result = t.kind == tyRef
+  result = t.kind == tyRef or (t.kind == tyProc and t.callConv == ccClosure)
 
 proc containsTyRef*(typ: PType): bool = 
   # returns true if typ contains a 'ref'
@@ -332,6 +333,8 @@ proc canFormAcycleAux(marker: var TIntSet, typ: PType, startId: int): bool =
     nil
 
 proc canFormAcycle(typ: PType): bool = 
+  # XXX as I expect cycles introduced by closures are very rare, we pretend 
+  # they can't happen here.
   var marker = InitIntSet()
   result = canFormAcycleAux(marker, typ, typ.id)