summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2012-08-13 17:07:49 +0200
committerAraq <rumpf_a@web.de>2012-08-13 17:07:49 +0200
commit244c14db0ba5c71a04be9486704994c774c5648b (patch)
tree7026ea267830246119e49f303ed2dd8320c71e0a
parentf686647f58c67d90f051dedf441f9b3eb913899b (diff)
downloadNim-244c14db0ba5c71a04be9486704994c774c5648b.tar.gz
top level closures should work; transf is not a pass anymore; next steps for first class iterator support
-rwxr-xr-xcompiler/ast.nim4
-rwxr-xr-xcompiler/ccgstmts.nim3
-rwxr-xr-xcompiler/cgen.nim2
-rwxr-xr-xcompiler/evals.nim3
-rw-r--r--compiler/lambdalifting.nim200
-rwxr-xr-xcompiler/main.nim3
-rwxr-xr-xcompiler/msgs.nim6
-rwxr-xr-xcompiler/sem.nim4
-rwxr-xr-xcompiler/semdata.nim32
-rwxr-xr-xcompiler/semexprs.nim17
-rwxr-xr-xcompiler/seminst.nim3
-rwxr-xr-xcompiler/semstmts.nim63
-rwxr-xr-xcompiler/transf.nim169
-rwxr-xr-xlib/core/typeinfo.nim2
-rw-r--r--tests/run/tunittests.nim4
-rw-r--r--tests/run/uclosures.nim3
-rwxr-xr-xtodo.txt8
17 files changed, 295 insertions, 231 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index ea88322a7..d37fbe1f0 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -340,7 +340,8 @@ type
                       # because for instantiations of objects, structural
                       # type equality has to be used
     tfAll,            # type class requires all constraints to be met (default)
-    tfAny             # type class requires any constraint to be met
+    tfAny,            # type class requires any constraint to be met
+    tfCapturesEnv     # whether proc really captures some environment
 
   TTypeFlags* = set[TTypeFlag]
 
@@ -867,6 +868,7 @@ proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[],
   result = newNode(kind)
   result.info = info
   result.typ = typ
+  # XXX use shallowCopy here for ownership transfer:
   result.sons = sons
 
 proc newNodeIT(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = 
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 1f480f024..a1288ea2f 100755
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -93,10 +93,9 @@ template preserveBreakIdx(body: stmt): stmt =
   p.breakIdx = oldBreakIdx
 
 proc genState(p: BProc, n: PNode) =
-  internalAssert n.len == 2 and n.sons[0].kind == nkIntLit
+  internalAssert n.len == 1 and n.sons[0].kind == nkIntLit
   let idx = n.sons[0].intVal
   lineCg(p, cpsStmts, "STATE$1: ;$n", [idx.toRope])
-  genStmts(p, n.sons[1])
 
 proc genGotoState(p: BProc, n: PNode) =
   # we resist the temptation to translate it into duff's device as it later
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 06018fa99..654df5ea3 100755
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -627,7 +627,7 @@ proc deinitFrame(p: BProc): PRope =
   result = ropecg(p.module, "\t#popFrame();$n")
 
 proc closureSetup(p: BProc, prc: PSym) =
-  if prc.typ.callConv != ccClosure: return
+  if tfCapturesEnv notin prc.typ.flags: return
   # prc.ast[paramsPos].last contains the type we're after:
   var ls = lastSon(prc.ast[paramsPos])
   if ls.kind != nkSym:
diff --git a/compiler/evals.nim b/compiler/evals.nim
index 8ccab9ff4..2a5426852 100755
--- a/compiler/evals.nim
+++ b/compiler/evals.nim
@@ -1366,7 +1366,8 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
   inc(gNestedEvals)
 
 proc tryEval(c: PEvalContext, n: PNode): PNode =
-  var n = transform(c.module, n)
+  #internalAssert nfTransf in n.flags
+  var n = transformExpr(c.module, n)
   gWhileCounter = evalMaxIterations
   gNestedEvals = evalMaxRecDepth
   result = evalAux(c, n, {})
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index aaba47c70..341f95798 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -211,6 +211,7 @@ proc addHiddenParam(routine: PSym, param: PSym) =
   var params = routine.ast.sons[paramsPos]
   param.position = params.len
   addSon(params, newSymNode(param))
+  incl(routine.typ.flags, tfCapturesEnv)
   #echo "produced environment: ", param.id, " for ", routine.name.s
 
 proc isInnerProc(s, outerProc: PSym): bool {.inline.} =
@@ -234,12 +235,22 @@ proc dummyClosureParam(o: POuterContext, i: PInnerContext) =
     IdTablePut(o.lambdasToEnv, i.fn, e)
   if i.closureParam == nil: addClosureParam(i, e)
 
+proc illegalCapture(s: PSym): bool {.inline.} =
+  result = skipTypes(s.typ, abstractInst).kind in {tyVar, tyOpenArray} or
+      s.kind == skResult
+
 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
+  
+  if illegalCapture(local) or o.fn.id != local.owner.id:
+    # Currently captures are restricted to a single level of nesting:
+    LocalError(info, errIllegalCaptureX, local.name.s)
+  i.fn.typ.callConv = ccClosure
+  incl(i.fn.typ.flags, tfCapturesEnv)
 
   # we need to remember which inner most closure belongs to this lambda:
   var e = o.currentEnv
@@ -269,12 +280,13 @@ proc interestingVar(s: PSym): bool {.inline.} =
 
 proc gatherVars(o: POuterContext, i: PInnerContext, n: PNode) = 
   # gather used vars for closure generation
+  if n == nil: return
   case n.kind
   of nkSym:
     var s = n.sym
     if interestingVar(s) and i.fn.id != s.owner.id:
       captureVar(o, i, s, n.info)
-    elif isInnerProc(s, o.fn) and s.typ.callConv == ccClosure and s != i.fn:
+    elif isInnerProc(s, o.fn) and tfCapturesEnv in s.typ.flags and s != i.fn:
       # call to some other inner proc; we need to track the dependencies for
       # this:
       let env = PEnv(IdTableGet(o.lambdasToEnv, i.fn))
@@ -287,6 +299,28 @@ proc gatherVars(o: POuterContext, i: PInnerContext, n: PNode) =
     for k in countup(0, sonsLen(n) - 1): 
       gatherVars(o, i, n.sons[k])
 
+proc generateThunk(prc: PNode, dest: PType): PNode =
+  ## Converts 'prc' into '(thunk, nil)' so that it's compatible with
+  ## a closure.
+  
+  # we cannot generate a proper thunk here for GC-safety reasons (see internal
+  # documentation):
+  if gCmd == cmdCompileToEcmaScript: return prc
+  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, getSysType(tyNil)))
+
+proc transformOuterConv(n: PNode): PNode =
+  # numeric types need range checks:
+  var dest = skipTypes(n.typ, abstractVarRange)
+  var source = skipTypes(n.sons[1].typ, abstractVarRange)
+  if dest.kind == tyProc:
+    if dest.callConv == ccClosure and source.callConv == ccDefault:
+      result = generateThunk(n.sons[1], dest)
+
 proc makeClosure(prc, env: PSym, info: TLineInfo): PNode =
   result = newNodeIT(nkClosure, info, prc.typ)
   result.add(newSymNode(prc))
@@ -339,6 +373,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode) =
       gatherVars(o, inner, body)
       # dummy closure param needed?
       if inner.closureParam == nil and n.sym.typ.callConv == ccClosure:
+        assert tfCapturesEnv notin n.sym.typ.flags
         dummyClosureParam(o, inner)
       # only transform if it really needs a closure:
       if inner.closureParam != nil:
@@ -437,7 +472,7 @@ proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode =
                newSymNode(getClosureVar(o, e))))
 
 proc transformOuterProc(o: POuterContext, n: PNode): PNode =
-  # XXX I with I knew where these 'nil' nodes come from: 'array[.. |X]'
+  # XXX I wish I knew where these 'nil' nodes come from: 'array[.. |X]'
   if n == nil: return nil
   case n.kind
   of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil
@@ -481,17 +516,20 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
      nkIteratorDef: 
     # don't recurse here:
     nil
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    let x = transformOuterProc(o, n.sons[1])
+    if x != nil: n.sons[1] = x
+    result = transformOuterConv(n)
   else:
     for i in countup(0, sonsLen(n) - 1):
       let x = transformOuterProc(o, n.sons[i])
       if x != nil: n.sons[i] = x
 
 proc liftLambdas*(fn: PSym, body: PNode): PNode =
-  if body.kind == nkEmpty:
+  if body.kind == nkEmpty or gCmd == cmdCompileToEcmaScript:
     # ignore forward declaration:
     result = body
-  elif (fn.typ == nil or fn.typ.callConv != ccClosure) and 
-      not containsNode(body, procDefs):
+  elif not containsNode(body, procDefs):
     # fast path: no inner procs, so no closure needed:
     result = body
   else:
@@ -506,40 +544,144 @@ proc liftLambdas*(fn: PSym, body: PNode): PNode =
       let param = params.sons[i].sym
       IdTablePut(o.localsToEnv, param, o.currentEnv)
     searchForInnerProcs(o, body)
-    let a = transformOuterProc(o, body)
+    discard transformOuterProc(o, body)
     result = ex
-  
-# XXX should 's' be replaced by a tuple ('s', env)?
-
-proc liftLambdas*(n: PNode): PNode =
-  assert n.kind in procDefs
-  var s = n.sons[namePos].sym
-  if gCmd == cmdCompileToEcmaScript: return s.getBody
-  result = liftLambdas(s, s.getBody)
 
-proc transformIterator*(fn: PSym, body: PNode): PNode =
-  if body.kind == nkEmpty:
-    # ignore forward declaration:
+proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
+  if body.kind == nkEmpty or gCmd == cmdCompileToEcmaScript:
     result = body
-  # it(a, b) --> (it(a, b), createClosure())
-  # it(a, b) --> ?
-  discard """
-  let c = chain(f, g)
-  
-  for x in c: echo x
-  
+  else:
+    var o = newOuterContext(module)
+    let ex = closureCreationPoint(body)
+    o.currentEnv = newEnv(module, nil, ex)
+    searchForInnerProcs(o, body)
+    discard transformOuterProc(o, body)
+    result = ex
+
+# ------------------- iterator transformation --------------------------------
+
+discard """
   iterator chain[S, T](a, b: *S->T, args: *S): T =
     for x in a(args): yield x
     for x in b(args): yield x
+
+  let c = chain(f, g)
+  for x in c: echo x
   
   # translated to:
-  
-  
-  
   let c = chain( (f, newClosure(f)), (g, newClosure(g)), newClosure(chain))
-  
-  
 """
 
+type
+  TIterContext {.final, pure.} = object
+    iter, closureParam, state, resultSym: PSym
+    capturedVars: TIntSet
+    tup: PType
 
+proc newIterResult(iter: PSym): PSym =
+  result = newSym(skResult, getIdent":result", iter)
+  result.info = iter.info
+  result.typ = iter.typ.sons[0]
+  incl(result.flags, sfUsed)
+
+proc transfIterBody(c: var TIterContext, n: PNode): PNode =
+  # gather used vars for closure generation
+  if n == nil: return nil
+  case n.kind
+  of nkSym:
+    var s = n.sym
+    if interestingVar(s) and c.iter.id == s.owner.id:
+      if not containsOrIncl(c.capturedVars, s.id): addField(c.tup, s)
+      result = indirectAccess(newSymNode(c.closureParam), s, n.info)
+  of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: nil
+  of nkYieldStmt:
+    inc c.state.typ.n.sons[1].intVal
+    let stateNo = c.state.typ.n.sons[1].intVal
+
+    var stateAsgnStmt = newNodeI(nkAsgn, n.info)
+    stateAsgnStmt.add(indirectAccess(newSymNode(c.closureParam),c.state,n.info))
+    stateAsgnStmt.add(newIntNode(nkIntLit, stateNo))
+
+    var retStmt = newNodeI(nkReturnStmt, n.info)
+    if n.sons[0].kind != nkEmpty:
+      var a = newNodeI(nkAsgn, n.sons[0].info)
+      addSon(a, newSymNode(c.resultSym))
+      addSon(a, n.sons[0])
+      retStmt.add(a)
+    else:
+      retStmt.add(emptyNode)
+    
+    var stateLabelStmt = newNodeI(nkState, n.info)
+    stateLabelStmt.add(newIntNode(nkIntLit, stateNo-1))
+    
+    result = newNodeI(nkStmtList, n.info)
+    result.add(stateAsgnStmt)
+    result.add(retStmt)
+    result.add(stateLabelStmt)
+  else:
+    for i in countup(0, sonsLen(n)-1):
+      let x = transfIterBody(c, n.sons[i])
+      if x != nil: n.sons[i] = x
 
+proc getStateType(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
+  rawAddSon(result, getSysType(tyInt))
+
+proc liftIterator*(iter: PSym, body: PNode): PNode =
+  var c: TIterContext
+  c.iter = iter
+  c.capturedVars = initIntSet()
+
+  c.tup = newType(tyTuple, iter)
+  c.tup.n = newNodeI(nkRecList, iter.info)
+
+  var cp = newSym(skParam, getIdent(paramname), iter)
+  cp.info = iter.info
+  incl(cp.flags, sfFromGeneric)
+  cp.typ = newType(tyRef, iter)
+  rawAddSon(cp.typ, c.tup)
+  c.closureParam = cp
+  addHiddenParam(iter, cp)
+
+  c.state = newSym(skField, getIdent(":state"), iter)
+  c.state.typ = getStateType(iter)
+  addField(c.tup, c.state)
+
+  if iter.typ.sons[0] != nil:
+    c.resultSym = newIterResult(iter)
+    iter.ast.add(newSymNode(c.resultSym))
+
+  result = newNodeI(nkStmtList, iter.info)
+  var gs = newNodeI(nkGotoState, iter.info)
+  gs.add(indirectAccess(newSymNode(c.closureParam), c.state, iter.info))
+  result.add(gs)
+  var state0 = newNodeI(nkState, iter.info)
+  state0.add(newIntNode(nkIntLit, 0))
+  result.add(state0)
+  
+  let newBody = transfIterBody(c, body)
+  if newBody != nil:
+    result.add(newBody)
+  else:
+    result.add(body)
+    
+  var state1 = newNodeI(nkState, iter.info)
+  state1.add(newIntNode(nkIntLit, -1))
+  result.add(state1)
+
+proc transformForLoop*(iter: PSym, body: PNode): PNode =
+  discard """
+  for i in foo(): nil
+
+Is transformed to:
+  
+  cl = createClosure()
+  while true:
+    let i = foo(cl)
+    if cl.state == -1: break
+"""
+  
diff --git a/compiler/main.nim b/compiler/main.nim
index 03db3292c..37feabd17 100755
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -15,7 +15,7 @@ import
   os, lists, condsyms, rodread, rodwrite, ropes, trees, 
   wordrecg, sem, semdata, idents, passes, docgen, extccomp,
   cgen, ecmasgen,
-  platform, nimconf, importer, passaux, depends, transf, evals, types, idgen,
+  platform, nimconf, importer, passaux, depends, evals, types, idgen,
   tables, docgen2
 
 const
@@ -90,7 +90,6 @@ proc CompileProject(projectFile = gProjectFull) =
 proc semanticPasses =
   registerPass(verbosePass())
   registerPass(sem.semPass())
-  registerPass(transf.transfPass())
 
 proc CommandGenDepend =
   semanticPasses()
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index fd2205270..f61e03e79 100755
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -25,7 +25,8 @@ type
     errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, 
     errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, 
     errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation, 
-    errExceptionExpected, errExceptionAlreadyHandled, errYieldNotAllowedHere, 
+    errExceptionExpected, errExceptionAlreadyHandled, 
+    errYieldNotAllowedHere, errYieldNotAllowedInTryStmt, 
     errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine, 
     errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel, 
     errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected, 
@@ -149,7 +150,8 @@ const
     errInvalidIndentation: "invalid indentation", 
     errExceptionExpected: "exception expected", 
     errExceptionAlreadyHandled: "exception already handled", 
-    errYieldNotAllowedHere: "\'yield\' only allowed in a loop of an iterator", 
+    errYieldNotAllowedHere: "'yield' only allowed in an iterator",
+    errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
     errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expresions", 
     errCannotReturnExpr: "current routine cannot return an expression", 
     errAttemptToRedefine: "redefinition of \'$1\'", 
diff --git a/compiler/sem.nim b/compiler/sem.nim
index ef4109c68..feb8acdf7 100755
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -14,7 +14,7 @@ import
   wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
   magicsys, parser, nversion, nimsets, semfold, importer,
   procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
-  semthreads, intsets, transf, evals, idgen, aliases
+  semthreads, intsets, transf, evals, idgen, aliases, cgmeth
 
 proc semPass*(): TPass
 # implementation
@@ -182,6 +182,7 @@ proc myOpen(module: PSym, filename: string): PPassContext =
 proc myOpenCached(module: PSym, filename: string, 
                   rd: PRodReader): PPassContext = 
   result = myOpen(module, filename)
+  for m in items(rd.methods): methodDef(m, true)
 
 proc SemStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = 
   result = semStmt(c, n)
@@ -193,6 +194,7 @@ proc SemStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
       # a generic has been added to `a`:
       if result.kind != nkEmpty: addSon(a, result)
       result = a
+  result = transformStmt(c.module, result)
 
 proc RecoverContext(c: PContext) = 
   # clean up in case of a semantic error: We clean up the stacks, etc. This is
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 00943a063..202f752b5 100755
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -31,6 +31,8 @@ type
     resultSym*: PSym          # the result symbol (if we are in a proc)
     nestedLoopCounter*: int   # whether we are in a loop or not
     nestedBlockCounter*: int  # whether we are in a block or not
+    InTryStmt*: int           # whether we are in a try statement; works also
+                              # in standalone ``except`` and ``finally``
     next*: PProcCon           # used for stacking procedure contexts
   
   TInstantiatedSymbol* {.final.} = object
@@ -85,36 +87,6 @@ proc newGenericsCache*(): PGenericsCache =
   initIdTable(result.InstTypes)
   result.generics = @[]
 
-proc tempContext*(c: PContext): PContext =
-  ## generates a temporary context so that side-effects can be rolled-back;
-  ## necessary for ``system.compiles``.
-  new(result)
-  result.module = c.module
-  result.p = c.p
-  # don't use the old cache:
-  result.generics = newGenericsCache()
-  result.friendModule = c.friendModule
-  result.InstCounter = c.InstCounter
-  result.threadEntries = @[]
-  # hrm, 'tab' is expensive to copy ... so we don't. We open a new scope
-  # instead to be able to undo scope changes. Not entirely correct for
-  # explicit 'global' vars though:
-  #shallowCopy(result.tab, c.tab)
-  assign(result.AmbiguousSymbols, c.AmbiguousSymbols)
-  result.InGenericContext = c.InGenericContext
-  result.InUnrolledContext = c.InUnrolledContext
-  result.InCompilesContext = c.InCompilesContext
-  result.converters = c.converters
-  result.semConstExpr = c.semConstExpr
-  result.semExpr = c.semExpr
-  result.semConstBoolExpr = c.semConstBoolExpr
-  assign(result.includedFiles, c.includedFiles) 
-  result.filename = c.filename
-  #shallowCopy(result.userPragmas, c.userPragmas) 
-  # XXX mark it as read-only:
-  result.evalContext = c.evalContext
-  
-
 proc newContext*(module: PSym, nimfile: string): PContext
 
 proc lastOptionEntry*(c: PContext): POptionEntry
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 6ace851c3..96a195485 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -65,10 +65,6 @@ 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: 
@@ -111,13 +107,11 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
       incl(c.p.owner.flags, sfSideEffect)
     elif s.kind == skParam and s.typ.kind == tyExpr:
       return s.typ.n
-    elif s.owner != c.p.owner and 
-        c.p.owner.typ != nil and not IsGenericRoutine(s.owner):
-      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:
-        LocalError(n.info, errIllegalCaptureX, s.name.s)
     result = newSymNode(s, n.info)
+    # We cannot check for access to outer vars for example because it's still
+    # not sure the symbol really ends up being used:
+    # var len = 0 # but won't be called
+    # genericThatUsesLen(x) # marked as taking a closure?
   of skGenericParam:
     if s.ast != nil: result = semExpr(c, s.ast)
     else:
@@ -1179,7 +1173,7 @@ proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
   
   let oldInGenericContext = c.InGenericContext
   let oldInUnrolledContext = c.InUnrolledContext
-  
+  let oldProcCon = c.p
   c.generics = newGenericsCache()
   try:
     discard semExpr(c, n.sons[1])
@@ -1190,6 +1184,7 @@ proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode =
   c.generics = oldGenerics
   c.InGenericContext = oldInGenericContext
   c.InUnrolledContext = oldInUnrolledContext
+  c.p = oldProcCon
   msgs.setInfoContextLen(oldContextLen)
   setlen(gOwners, oldOwnerLen)
   while c.tab.tos > oldTos: rawCloseScope(c.tab)
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 5beac49ab..0e2d38196 100755
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -79,8 +79,7 @@ proc instantiateBody(c: PContext, n: PNode, result: PSym) =
       addResult(c, result.typ.sons[0], n.info, result.kind)
       addResultNode(c, n)
     var b = semStmtScope(c, n.sons[bodyPos])
-    # XXX Bad hack for tests/titer2 and tests/tactiontable
-    n.sons[bodyPos] = transform(c.module, b)
+    n.sons[bodyPos] = transformBody(c.module, b, result)
     #echo "code instantiated ", result.name.s
     excl(result.flags, sfForward)
     popProcCon(c)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index a5967a26d..ace73422d 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -198,15 +198,17 @@ proc SemYieldVarResult(c: PContext, n: PNode, restype: PType) =
           localError(n.sons[0].info, errXExpected, "tuple constructor")
   else: nil
   
-proc SemYield(c: PContext, n: PNode): PNode = 
+proc SemYield(c: PContext, n: PNode): PNode =
   result = n
   checkSonsLen(n, 1)
-  if c.p.owner == nil or c.p.owner.kind != skIterator: 
+  if c.p.owner == nil or c.p.owner.kind != skIterator:
     LocalError(n.info, errYieldNotAllowedHere)
+  elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline:
+    LocalError(n.info, errYieldNotAllowedInTryStmt)
   elif n.sons[0].kind != nkEmpty:
     n.sons[0] = SemExprWithType(c, n.sons[0]) # check for type compatibility:
     var restype = c.p.owner.typ.sons[0]
-    if restype != nil: 
+    if restype != nil:
       n.sons[0] = fitNode(c, restype, n.sons[0])
       if n.sons[0].typ == nil: InternalError(n.info, "semYield")
       SemYieldVarResult(c, n, restype)
@@ -486,6 +488,7 @@ proc semRaise(c: PContext, n: PNode): PNode =
 
 proc semTry(c: PContext, n: PNode): PNode = 
   result = n
+  inc c.p.inTryStmt
   checkMinSonsLen(n, 2)
   n.sons[0] = semStmtScope(c, n.sons[0])
   var check = initIntSet()
@@ -511,6 +514,7 @@ proc semTry(c: PContext, n: PNode): PNode =
       illFormedAst(n) 
     # last child of an nkExcept/nkFinally branch is a statement:
     a.sons[length - 1] = semStmtScope(c, a.sons[length - 1])
+  dec c.p.inTryStmt
 
 proc addGenericParamListToScope(c: PContext, n: PNode) = 
   if n.kind != nkGenericParams: 
@@ -714,20 +718,33 @@ proc semLambda(c: PContext, n: PNode): PNode =
       LocalError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
     pushProcCon(c, s)
     addResult(c, s.typ.sons[0], n.info, skProc)
-    n.sons[bodyPos] = semStmtScope(c, n.sons[bodyPos])
+    let semBody = semStmtScope(c, n.sons[bodyPos])
+    n.sons[bodyPos] = transformBody(c.module, semBody, s)
     addResultNode(c, n)
     popProcCon(c)
-  else: 
+  else:
     LocalError(n.info, errImplOfXexpected, s.name.s)
   sideEffectsCheck(c, s)
-  if s.typ.callConv == ccClosure and s.owner.kind == skModule:
-    localError(s.info, errXCannotBeClosure, s.name.s)
   closeScope(c.tab)           # close scope for parameters
   popOwner()
   result.typ = s.typ
- 
+
 proc instantiateDestructor*(c: PContext, typ: PType): bool
 
+proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
+  let t = s.typ.sons[1].skipTypes({tyVar})
+  t.destructor = s
+  # automatically insert calls to base classes' destructors
+  if n.sons[bodyPos].kind != nkEmpty:
+    for i in countup(0, t.sonsLen - 1):
+      # when inheriting directly from object
+      # there will be a single nil son
+      if t.sons[i] == nil: continue
+      if instantiateDestructor(c, t.sons[i]):
+        n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[
+            useSym(t.sons[i].destructor),
+            n.sons[paramsPos][1][0]]))
+
 proc semProcAux(c: PContext, n: PNode, kind: TSymKind, 
                 validPragmas: TSpecialWords): PNode = 
   result = semProcAnnotation(c, n)
@@ -754,7 +771,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         n.sons[genericParamsPos] = gp
         # check for semantics again:
         # semParamList(c, n.sons[ParamsPos], nil, s)
-  else: 
+  else:
     s.typ = newTypeS(tyProc, c)
     rawAddSon(s.typ, nil)
   var proto = SearchForProc(c, s, c.tab.tos-2) # -2 because we have a scope
@@ -791,19 +808,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     popOwner()
     pushOwner(s)
   s.options = gOptions
-  if sfDestructor in s.flags:
-    let t = s.typ.sons[1].skipTypes({tyVar})
-    t.destructor = s
-    # automatically insert calls to base classes' destructors
-    if n.sons[bodyPos].kind != nkEmpty:
-      for i in countup(0, t.sonsLen - 1):
-        # when inheriting directly from object
-        # there will be a single nil son
-        if t.sons[i] == nil: continue
-        if instantiateDestructor(c, t.sons[i]):
-          n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[
-              useSym(t.sons[i].destructor),
-              n.sons[paramsPos][1][0]]))
+  if sfDestructor in s.flags: doDestructorStuff(c, s, n)
   if n.sons[bodyPos].kind != nkEmpty: 
     # for DLL generation it is annoying to check for sfImportc!
     if sfBorrow in s.flags: 
@@ -815,7 +820,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         addResult(c, s.typ.sons[0], n.info, kind)
       if sfImportc notin s.flags:
         # no semantic checking for importc:
-        n.sons[bodyPos] = semStmtScope(c, n.sons[bodyPos])
+        let semBody = semStmtScope(c, n.sons[bodyPos])
+        # unfortunately we cannot skip this step when in 'system.compiles'
+        # context as it may even be evaluated in 'system.compiles':
+        n.sons[bodyPos] = transformBody(c.module, semBody, s)
       if s.typ.sons[0] != nil and kind != skIterator: addResultNode(c, n)
       popProcCon(c)
     else: 
@@ -827,14 +835,12 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     if sfImportc in s.flags: 
       # so we just ignore the body after semantic checking for importc:
       n.sons[bodyPos] = ast.emptyNode
-  else: 
+  else:
     if proto != nil: LocalError(n.info, errImplOfXexpected, proto.name.s)
     if {sfImportc, sfBorrow} * s.flags == {} and s.magic == mNone: 
       incl(s.flags, sfForward)
     elif sfBorrow in s.flags: semBorrow(c, n, s)
   sideEffectsCheck(c, s)
-  if s.typ.callConv == ccClosure and s.owner.kind == skModule:
-    localError(s.info, errXCannotBeClosure, s.name.s)
   closeScope(c.tab)           # close scope for parameters
   popOwner()
   
@@ -845,7 +851,10 @@ proc semIterator(c: PContext, n: PNode): PNode =
   if t.sons[0] == nil:
     LocalError(n.info, errXNeedsReturnType, "iterator")
   # iterators are either 'inline' or 'closure':
-  if s.typ.callConv != ccInline: s.typ.callConv = ccClosure
+  if s.typ.callConv != ccInline: 
+    s.typ.callConv = ccClosure
+    # and they always at least use the 'env' for the state field:
+    incl(s.typ.flags, tfCapturesEnv)
   if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone:
     LocalError(n.info, errImplOfXexpected, s.name.s)
   
diff --git a/compiler/transf.nim b/compiler/transf.nim
index a31ad2c18..c6c7739d6 100755
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -25,7 +25,6 @@ import
 const 
   genPrefix* = ":tmp"         # prefix for generated names
 
-proc transfPass*(): TPass
 # implementation
 
 type 
@@ -109,54 +108,6 @@ proc transformSons(c: PTransf, n: PNode): PTransNode =
   for i in countup(0, sonsLen(n)-1): 
     result[i] = transform(c, n.sons[i])
 
-# Transforming iterators into non-inlined versions is pretty hard, but
-# unavoidable for not bloating the code too much. If we had direct access to
-# the program counter, things'd be much easier.
-# ::
-#
-#  iterator items(a: string): char =
-#    var i = 0
-#    while i < length(a):
-#      yield a[i]
-#      inc(i)
-#
-#  for ch in items("hello world"): # `ch` is an iteration variable
-#    echo(ch)
-#
-# Should be transformed into::
-#
-#  type
-#    TItemsClosure = record
-#      i: int
-#      state: int
-#  proc items(a: string, c: var TItemsClosure): char =
-#    case c.state
-#    of 0: goto L0 # very difficult without goto!
-#    of 1: goto L1 # can be implemented by GCC's computed gotos
-#
-#    block L0:
-#      c.i = 0
-#      while c.i < length(a):
-#        c.state = 1
-#        return a[i]
-#        block L1: inc(c.i)
-#
-# More efficient, but not implementable::
-#
-#  type
-#    TItemsClosure = record
-#      i: int
-#      pc: pointer
-#
-#  proc items(a: string, c: var TItemsClosure): char =
-#    goto c.pc
-#    c.i = 0
-#    while c.i < length(a):
-#      c.pc = label1
-#      return a[i]
-#      label1: inc(c.i)
-#
-
 proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode = 
   result = newTransNode(nkFastAsgn, PNode(ri).info, 2)
   result[0] = PTransNode(le)
@@ -178,15 +129,6 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
     if result != nil: return
     tc = tc.next
   result = b
-  when false:
-    case b.sym.kind
-    of skConst, skEnumField: 
-      if sfFakeConst notin b.sym.flags:
-        if skipTypes(b.sym.typ, abstractInst).kind notin ConstantDataTypes: 
-          result = getConstExpr(c.module, b)
-          if result == nil: InternalError(b.info, "transformSym: const")
-    else: 
-      nil
 
 proc transformSym(c: PTransf, n: PNode): PTransNode = 
   result = PTransNode(transformSymAux(c, n))
@@ -378,20 +320,6 @@ 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])
-
-proc generateThunk(c: PTransf, prc: PNode, dest: PType): PNode =
-  ## Converts 'prc' into '(thunk, nil)' so that it's compatible with
-  ## a closure.
-  
-  # we cannot generate a proper thunk here for GC-safety reasons (see internal
-  # documentation):
-  if gCmd == cmdCompileToEcmaScript: return prc
-  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, getSysType(tyNil)))
   
 proc transformConv(c: PTransf, n: PNode): PTransNode = 
   # numeric types need range checks:
@@ -466,12 +394,6 @@ 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:
-      let x = transform(c, n.sons[1]).pnode
-      result = generateThunk(c, x, dest).ptransnode
-    else:
-      result = transformSons(c, n)    
   of tyGenericParam, tyOrdinal, tyTypeClass:
     result = transform(c, n.sons[1])
     # happens sometimes for generated assignments, etc.
@@ -657,21 +579,23 @@ proc transform(c: PTransf, n: PNode): PTransNode =
     result = PTransNode(n)
   of nkBracketExpr: result = transformArrayAccess(c, n)
   of procDefs:
-    if n.sons[genericParamsPos].kind == nkEmpty:
-      var s = n.sons[namePos].sym
-      n.sons[bodyPos] = PNode(transform(c, s.getBody))
-      if s.ast.sons[bodyPos] != n.sons[bodyPos]:
-        # somehow this can happen ... :-/
-        s.ast.sons[bodyPos] = n.sons[bodyPos]
-      n.sons[bodyPos] = liftLambdas(n)
-      if n.kind == nkMethodDef: methodDef(s, false)
+    when false:
+      if n.sons[genericParamsPos].kind == nkEmpty:
+        var s = n.sons[namePos].sym
+        n.sons[bodyPos] = PNode(transform(c, s.getBody))
+        if s.ast.sons[bodyPos] != n.sons[bodyPos]:
+          # somehow this can happen ... :-/
+          s.ast.sons[bodyPos] = n.sons[bodyPos]
+        #n.sons[bodyPos] = liftLambdas(s, n)
+        #if n.kind == nkMethodDef: methodDef(s, false)
     result = PTransNode(n)
   of nkMacroDef:
     # XXX no proper closure support yet:
-    if n.sons[genericParamsPos].kind == nkEmpty:
-      var s = n.sons[namePos].sym
-      n.sons[bodyPos] = PNode(transform(c, s.getBody))
-      if n.kind == nkMethodDef: methodDef(s, false)
+    when false:
+      if n.sons[genericParamsPos].kind == nkEmpty:
+        var s = n.sons[namePos].sym
+        n.sons[bodyPos] = PNode(transform(c, s.getBody))
+        if n.kind == nkMethodDef: methodDef(s, false)
     result = PTransNode(n)
   of nkForStmt: 
     inc c.inLoop
@@ -735,38 +659,57 @@ proc transform(c: PTransf, n: PNode): PTransNode =
   if cnst != nil and not dontInlineConstant(n, cnst):
     result = PTransNode(cnst) # do not miss an optimization
 
-proc processTransf(context: PPassContext, n: PNode): PNode = 
+proc processTransf(c: PTransf, n: PNode): PNode = 
   # Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
   # this step! We have to rely that the semantic pass transforms too errornous
   # nodes into an empty node.
-  if passes.skipCodegen(n) or context.fromCache or nfTransf in n.flags: return n
-  var c = PTransf(context)
+  if passes.skipCodegen(n) or c.fromCache or nfTransf in n.flags: return n
   pushTransCon(c, newTransCon(getCurrOwner(c)))
   result = PNode(transform(c, n))
   popTransCon(c)
   incl(result.flags, nfTransf)
 
-proc openTransf(module: PSym, filename: string): PPassContext = 
-  var n: PTransf
-  new(n)
-  n.contSyms = @[]
-  n.breakSyms = @[]
-  n.module = module
-  result = n
-
-proc openTransfCached(module: PSym, filename: string, 
-                      rd: PRodReader): PPassContext = 
-  result = openTransf(module, filename)
-  for m in items(rd.methods): methodDef(m, true)
-
-proc transfPass(): TPass = 
-  initPass(result)
-  result.open = openTransf
-  result.openCached = openTransfCached
-  result.process = processTransf
-  result.close = processTransf # we need to process generics too!
+proc openTransf(module: PSym, filename: string): PTransf = 
+  new(result)
+  result.contSyms = @[]
+  result.breakSyms = @[]
+  result.module = module
+
+when false:
+  proc openTransfCached(module: PSym, filename: string, 
+                        rd: PRodReader): PPassContext = 
+    result = openTransf(module, filename)
+    for m in items(rd.methods): methodDef(m, true)
+
+  proc transfPass(): TPass = 
+    initPass(result)
+    result.open = openTransf
+    result.openCached = openTransfCached
+    result.process = processTransf
+    result.close = processTransf # we need to process generics too!
   
-proc transform*(module: PSym, n: PNode): PNode =
+proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
+  if nfTransf in n.flags or prc.kind in {skTemplate, skMacro}:
+    result = n
+  else:
+    var c = openTransf(module, "")
+    result = processTransf(c, n)
+    if prc.kind != skMacro:
+      # XXX no closures yet for macros:
+      result = liftLambdas(prc, result)
+    if prc.kind == skMethod: methodDef(prc, false)
+    incl(result.flags, nfTransf)
+
+proc transformStmt*(module: PSym, n: PNode): PNode =
+  if nfTransf in n.flags:
+    result = n
+  else:
+    var c = openTransf(module, "")
+    result = processTransf(c, n)
+    result = liftLambdasForTopLevel(module, result)
+    incl(result.flags, nfTransf)
+
+proc transformExpr*(module: PSym, n: PNode): PNode =
   if nfTransf in n.flags:
     result = n
   else:
diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim
index 9030b7b53..3032ccb19 100755
--- a/lib/core/typeinfo.nim
+++ b/lib/core/typeinfo.nim
@@ -455,7 +455,7 @@ proc getFloat*(x: TAny): float =
 
 proc getFloat32*(x: TAny): float32 = 
   ## retrieve the float32 value out of `x`. `x` needs to represent an float32.
-  assert skipRange(x.rawtype).kind == tyFloat64
+  assert skipRange(x.rawtype).kind == tyFloat32
   result = cast[ptr float32](x.value)[]
   
 proc getFloat64*(x: TAny): float64 = 
diff --git a/tests/run/tunittests.nim b/tests/run/tunittests.nim
index e4c92c9e9..c77f691d9 100644
--- a/tests/run/tunittests.nim
+++ b/tests/run/tunittests.nim
@@ -1,3 +1 @@
-import utemplates
-
-# uclosures
+import utemplates, uclosures
diff --git a/tests/run/uclosures.nim b/tests/run/uclosures.nim
index d54b88285..6eea29ca1 100644
--- a/tests/run/uclosures.nim
+++ b/tests/run/uclosures.nim
@@ -4,7 +4,8 @@ test "loop variables are captured by copy":
   var funcs: seq[proc (): int {.closure.}] = @[]
   
   for i in 0..10:
-    funcs.add do -> int: return i * i
+    let ii = i
+    funcs.add do -> int: return ii * ii
 
   check funcs[0]() == 0
   check funcs[3]() == 9
diff --git a/todo.txt b/todo.txt
index 3cce1721c..8515759ec 100755
--- a/todo.txt
+++ b/todo.txt
@@ -1,14 +1,14 @@
 version 0.9.0
 =============
 
-- closure: implement closure support for procs capturing nested
-  module vars
-- implicit deref for parameter matching
+- implement "closure tuple consists of a single 'ref'" optimization
+- implement for loop transformation for first class iterators
 
+- implicit deref for parameter matching; implement ``varargs[T, `$`]``
 - optimize genericAssign in the code generator
+- the lookup rules for generics really are too permissive
 - fix remaining closure bugs:
   - fix evals.nim with closures
-  - implement "closure tuple consists of a single 'ref'" optimization
 
 
 Bugs