summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2013-05-03 23:51:43 +0200
committerAraq <rumpf_a@web.de>2013-05-03 23:51:43 +0200
commitfa0a327dd6f8f18ce935f6bd8993e4af474d1231 (patch)
tree444367f8069a14968bbc4c9e1d6986c281c6862b
parent8e5d6834cc51062a9d249fd7730432d32b7f6adc (diff)
downloadNim-fa0a327dd6f8f18ce935f6bd8993e4af474d1231.tar.gz
completed expr/stmt unification
-rw-r--r--compiler/ccgexprs.nim222
-rw-r--r--compiler/ccgstmts.nim198
-rw-r--r--compiler/cgen.nim14
-rw-r--r--compiler/parser.nim6
-rw-r--r--compiler/renderer.nim5
-rw-r--r--compiler/sem.nim6
-rw-r--r--compiler/semdestruct.nim213
-rw-r--r--compiler/semexprs.nim177
-rw-r--r--compiler/semstmts.nim469
-rw-r--r--todo.txt5
10 files changed, 642 insertions, 673 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 78173b5e8..a50b39d47 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -9,9 +9,6 @@
 
 # included from cgen.nim
 
-proc lenField: PRope {.inline.} = 
-  result = toRope(if gCmd != cmdCompileToCpp: "Sup.len" else: "len")
-
 # -------------------------- constant expressions ------------------------
 
 proc intLiteral(i: biggestInt): PRope =
@@ -207,8 +204,6 @@ proc asgnComplexity(n: PNode): int =
         result += asgnComplexity(t)
     else: nil
 
-proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
-
 proc optAsgnLoc(a: TLoc, t: PType, field: PRope): TLoc =
   result.k = locField
   result.s = a.s
@@ -345,11 +340,6 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
   else: InternalError("genAssignment(" & $ty.kind & ')')
 
-proc expr(p: BProc, e: PNode, d: var TLoc)
-proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
-  initLoc(result, locNone, e.typ, OnUnknown)
-  expr(p, e, result)
-
 proc getDestLoc(p: BProc, d: var TLoc, typ: PType) =
   if d.k == locNone: getTemp(p, typ, d)
 
@@ -377,15 +367,15 @@ proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) =
     d.a = -1
 
 proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
-  var b: TLoc
+  var a, b: TLoc
   if d.k != locNone: InternalError(e.info, "binaryStmt")
-  InitLocExpr(p, e.sons[1], d)
+  InitLocExpr(p, e.sons[1], a)
   InitLocExpr(p, e.sons[2], b)
-  lineCg(p, cpsStmts, frmt, rdLoc(d), rdLoc(b))
+  lineCg(p, cpsStmts, frmt, rdLoc(a), rdLoc(b))
 
 proc unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   var a: TLoc
-  if (d.k != locNone): InternalError(e.info, "unaryStmt")
+  if d.k != locNone: InternalError(e.info, "unaryStmt")
   InitLocExpr(p, e.sons[1], a)
   lineCg(p, cpsStmts, frmt, [rdLoc(a)])
 
@@ -840,43 +830,6 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   else:
     genAssignment(p, d, tmp, {}) # no need for deep copying
 
-proc genIfExpr(p: BProc, n: PNode, d: var TLoc) =
-  #
-  #  if (!expr1) goto L1;
-  #  thenPart
-  #  goto LEnd
-  #  L1:
-  #  if (!expr2) goto L2;
-  #  thenPart2
-  #  goto LEnd
-  #  L2:
-  #  elsePart
-  #  Lend:
-  #
-  var
-    it: PNode
-    a, tmp: TLoc
-    Lend, Lelse: TLabel
-  getTemp(p, n.typ, tmp)      # force it into a temp!
-  Lend = getLabel(p)
-  for i in countup(0, sonsLen(n) - 1):
-    it = n.sons[i]
-    if it.len == 2:
-      initLocExpr(p, it.sons[0], a)
-      Lelse = getLabel(p)
-      lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), Lelse])
-      expr(p, it.sons[1], tmp)
-      lineF(p, cpsStmts, "goto $1;$n", [Lend])
-      fixLabel(p, Lelse)
-    elif it.len == 1:
-      expr(p, it.sons[0], tmp)
-    else: internalError(n.info, "genIfExpr()")
-  fixLabel(p, Lend)
-  if d.k == locNone:
-    d = tmp
-  else:
-    genAssignment(p, d, tmp, {}) # no need for deep copying
-
 proc genEcho(p: BProc, n: PNode) =
   # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)``
   # is threadsafe.
@@ -888,8 +841,6 @@ proc genEcho(p: BProc, n: PNode) =
   linefmt(p, cpsStmts, "printf($1$2);$n",
           makeCString(repeatStr(n.len-1, "%s") & tnl), args)
 
-include ccgcalls
-
 proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
   #   <Nimrod code>
   #   s = 'Hello ' & name & ', how do you feel?' & 'z'
@@ -1790,11 +1741,10 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
   else:
     putIntoDest(p, d, t, tmp)
 
-proc genBlock(p: BProc, t: PNode, d: var TLoc)
-proc expr(p: BProc, e: PNode, d: var TLoc) =
-  case e.kind
+proc expr(p: BProc, n: PNode, d: var TLoc) =
+  case n.kind
   of nkSym:
-    var sym = e.sym
+    var sym = n.sym
     case sym.Kind
     of skMethod:
       if sym.getBody.kind == nkEmpty or sfDispatcher in sym.flags:
@@ -1807,22 +1757,22 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
     of skProc, skConverter, skIterator:
       genProc(p.module, sym)
       if sym.loc.r == nil or sym.loc.t == nil:
-        InternalError(e.info, "expr: proc not init " & sym.name.s)
+        InternalError(n.info, "expr: proc not init " & sym.name.s)
       putLocIntoDest(p, d, sym.loc)
     of skConst:
       if sfFakeConst in sym.flags:
         if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
         putLocIntoDest(p, d, sym.loc)
       elif isSimpleConst(sym.typ):
-        putIntoDest(p, d, e.typ, genLiteral(p, sym.ast, sym.typ))
+        putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ))
       else:
         genComplexConst(p, sym, d)
     of skEnumField:
-      putIntoDest(p, d, e.typ, toRope(sym.position))
+      putIntoDest(p, d, n.typ, toRope(sym.position))
     of skVar, skForVar, skResult, skLet:
       if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
       if sym.loc.r == nil or sym.loc.t == nil:
-        InternalError(e.info, "expr: var not init " & sym.name.s)
+        InternalError(n.info, "expr: var not init " & sym.name.s)
       if sfThread in sym.flags:
         AccessThreadLocalVar(p, sym)
         if emulatedThreadVars(): 
@@ -1833,75 +1783,129 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
         putLocIntoDest(p, d, sym.loc)
     of skTemp:
       if sym.loc.r == nil or sym.loc.t == nil:
-        InternalError(e.info, "expr: temp not init " & sym.name.s)
+        InternalError(n.info, "expr: temp not init " & sym.name.s)
       putLocIntoDest(p, d, sym.loc)
     of skParam:
       if sym.loc.r == nil or sym.loc.t == nil:
-        InternalError(e.info, "expr: param not init " & sym.name.s)
+        InternalError(n.info, "expr: param not init " & sym.name.s)
       putLocIntoDest(p, d, sym.loc)
-    else: InternalError(e.info, "expr(" & $sym.kind & "); unknown symbol")
+    else: InternalError(n.info, "expr(" & $sym.kind & "); unknown symbol")
+  of nkNilLit:
+    if not isEmptyType(n.typ):
+      putIntoDest(p, d, n.typ, genLiteral(p, n))
   of nkStrLit..nkTripleStrLit, nkIntLit..nkUInt64Lit,
-     nkFloatLit..nkFloat128Lit, nkNilLit, nkCharLit:
-    putIntoDest(p, d, e.typ, genLiteral(p, e))
+     nkFloatLit..nkFloat128Lit, nkCharLit:
+    putIntoDest(p, d, n.typ, genLiteral(p, n))
   of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand,
      nkCallStrLit:
-    if e.sons[0].kind == nkSym and e.sons[0].sym.magic != mNone:
-      genMagicExpr(p, e, d, e.sons[0].sym.magic)
+    genLineDir(p, n)
+    if n.sons[0].kind == nkSym and n.sons[0].sym.magic != mNone:
+      genMagicExpr(p, n, d, n.sons[0].sym.magic)
     else:
-      genCall(p, e, d)
+      genCall(p, n, d)
   of nkCurly:
-    if isDeepConstExpr(e) and e.len != 0:
-      putIntoDest(p, d, e.typ, genSetNode(p, e))
+    if isDeepConstExpr(n) and n.len != 0:
+      putIntoDest(p, d, n.typ, genSetNode(p, n))
     else:
-      genSetConstr(p, e, d)
+      genSetConstr(p, n, d)
   of nkBracket:
-    if isDeepConstExpr(e) and e.len != 0:
-      exprComplexConst(p, e, d)
-    elif skipTypes(e.typ, abstractVarRange).kind == tySequence:
-      genSeqConstr(p, e, d)
+    if isDeepConstExpr(n) and n.len != 0:
+      exprComplexConst(p, n, d)
+    elif skipTypes(n.typ, abstractVarRange).kind == tySequence:
+      genSeqConstr(p, n, d)
     else:
-      genArrayConstr(p, e, d)
+      genArrayConstr(p, n, d)
   of nkPar:
-    if isDeepConstExpr(e) and e.len != 0:
-      exprComplexConst(p, e, d)
+    if isDeepConstExpr(n) and n.len != 0:
+      exprComplexConst(p, n, d)
     else:
-      genTupleConstr(p, e, d)
-  of nkObjConstr: genObjConstr(p, e, d)
-  of nkCast: genCast(p, e, d)
-  of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, e, d)
-  of nkHiddenAddr, nkAddr: genAddr(p, e, d)
+      genTupleConstr(p, n, d)
+  of nkObjConstr: genObjConstr(p, n, d)
+  of nkCast: genCast(p, n, d)
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d)
+  of nkHiddenAddr, nkAddr: genAddr(p, n, d)
   of nkBracketExpr:
-    var ty = skipTypes(e.sons[0].typ, abstractVarRange)
+    var ty = skipTypes(n.sons[0].typ, abstractVarRange)
     if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.sons[0], abstractVarRange)
     case ty.kind
-    of tyArray, tyArrayConstr: genArrayElem(p, e, d)
-    of tyOpenArray, tyVarargs: genOpenArrayElem(p, e, d)
-    of tySequence, tyString: genSeqElem(p, e, d)
-    of tyCString: genCStringElem(p, e, d)
-    of tyTuple: genTupleElem(p, e, d)
-    else: InternalError(e.info, "expr(nkBracketExpr, " & $ty.kind & ')')
-  of nkDerefExpr, nkHiddenDeref: genDeref(p, e, d)
-  of nkDotExpr: genRecordField(p, e, d)
-  of nkCheckedFieldExpr: genCheckedRecordField(p, e, d)
-  of nkBlockExpr: genBlock(p, e, d)
-  of nkStmtListExpr: genStmtListExpr(p, e, d)
-  of nkIfExpr: genIfExpr(p, e, d)
-  of nkObjDownConv: downConv(p, e, d)
-  of nkObjUpConv: upConv(p, e, d)
-  of nkChckRangeF: genRangeChck(p, e, d, "chckRangeF")
-  of nkChckRange64: genRangeChck(p, e, d, "chckRange64")
-  of nkChckRange: genRangeChck(p, e, d, "chckRange")
-  of nkStringToCString: convStrToCStr(p, e, d)
-  of nkCStringToString: convCStrToStr(p, e, d)
+    of tyArray, tyArrayConstr: genArrayElem(p, n, d)
+    of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, d)
+    of tySequence, tyString: genSeqElem(p, n, d)
+    of tyCString: genCStringElem(p, n, d)
+    of tyTuple: genTupleElem(p, n, d)
+    else: InternalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
+  of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d)
+  of nkDotExpr: genRecordField(p, n, d)
+  of nkCheckedFieldExpr: genCheckedRecordField(p, n, d)
+  of nkBlockExpr, nkBlockStmt: genBlock(p, n, d)
+  of nkStmtListExpr: genStmtListExpr(p, n, d)
+  of nkStmtList:
+    for i in countup(0, sonsLen(n) - 1): genStmts(p, n.sons[i])
+  of nkIfExpr, nkIfStmt: genIf(p, n, d)
+  of nkObjDownConv: downConv(p, n, d)
+  of nkObjUpConv: upConv(p, n, d)
+  of nkChckRangeF: genRangeChck(p, n, d, "chckRangeF")
+  of nkChckRange64: genRangeChck(p, n, d, "chckRange64")
+  of nkChckRange: genRangeChck(p, n, d, "chckRange")
+  of nkStringToCString: convStrToCStr(p, n, d)
+  of nkCStringToString: convCStrToStr(p, n, d)
   of nkLambdaKinds:
-    var sym = e.sons[namePos].sym
+    var sym = n.sons[namePos].sym
     genProc(p.module, sym)
     if sym.loc.r == nil or sym.loc.t == nil:
-      InternalError(e.info, "expr: proc not init " & sym.name.s)
+      InternalError(n.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")
+  of nkClosure: genClosure(p, n, d)
+  of nkMetaNode: expr(p, n.sons[0], d)
+
+  of nkEmpty:  nil
+  of nkWhileStmt: genWhileStmt(p, n)
+  of nkVarSection, nkLetSection: genVarStmt(p, n)
+  of nkConstSection: genConstStmt(p, n)
+  of nkForStmt: internalError(n.info, "for statement not eliminated")
+  of nkCaseStmt: genCase(p, n, d)
+  of nkReturnStmt: genReturnStmt(p, n)
+  of nkBreakStmt: genBreakStmt(p, n)
+  of nkAsgn: genAsgn(p, n, fastAsgn=false)
+  of nkFastAsgn:
+    # transf is overly aggressive with 'nkFastAsgn', so we work around here.
+    # See tests/run/tcnstseq3 for an example that would fail otherwise.
+    genAsgn(p, n, fastAsgn=p.prc != nil)
+  of nkDiscardStmt:
+    if n.sons[0].kind != nkEmpty:
+      var a: TLoc
+      genLineDir(p, n)
+      initLocExpr(p, n.sons[0], a)
+  of nkAsmStmt: genAsmStmt(p, n)
+  of nkTryStmt:
+    if gCmd == cmdCompileToCpp: genTryCpp(p, n, d)
+    else: genTry(p, n, d)
+  of nkRaiseStmt: genRaiseStmt(p, n)
+  of nkTypeSection:
+    # we have to emit the type information for object types here to support
+    # separate compilation:
+    genTypeSection(p.module, n)
+  of nkCommentStmt, nkIteratorDef, nkIncludeStmt, 
+     nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, 
+     nkFromStmt, nkTemplateDef, nkMacroDef: 
+    nil
+  of nkPragma: genPragma(p, n)
+  of nkProcDef, nkMethodDef, nkConverterDef: 
+    if (n.sons[genericParamsPos].kind == nkEmpty): 
+      var prc = n.sons[namePos].sym
+      if (optDeadCodeElim notin gGlobalOptions and
+          sfDeadCodeElim notin getModule(prc).flags) or
+          ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
+          (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
+          (prc.kind == skMethod): 
+        # we have not only the header: 
+        if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags: 
+          genProc(p.module, prc)
+  of nkParForStmt: genParForStmt(p, n)
+  of nkState: genState(p, n)
+  of nkGotoState: genGotoState(p, n)
+  of nkBreakState: genBreakState(p, n)
+  else: InternalError(n.info, "expr(" & $n.kind & "); unknown node kind")
 
 proc genNamedConstExpr(p: BProc, n: PNode): PRope =
   if n.kind == nkExprColonExpr: result = genConstExpr(p, n.sons[1])
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 59cdbbef4..a8ef8b31a 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -102,6 +102,11 @@ proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} =
   genStmts(p, stmts)
   endBlock(p)
 
+proc exprBlock(p: BProc, n: PNode, d: var TLoc) =
+  startBlock(p)
+  expr(p, n, d)
+  endBlock(p)
+
 template preserveBreakIdx(body: stmt): stmt {.immediate.} =
   var oldBreakIdx = p.breakIdx
   body
@@ -212,7 +217,7 @@ proc genConstStmt(p: BProc, t: PNode) =
           appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", 
                [getTypeDesc(p.module, c.typ), c.loc.r, genConstExpr(p, c.ast)])
     
-proc genIfStmt(p: BProc, n: PNode) = 
+proc genIf(p: BProc, n: PNode, d: var TLoc) =
   #
   #  { if (!expr1) goto L1;
   #   thenPart }
@@ -224,32 +229,36 @@ proc genIfStmt(p: BProc, n: PNode) =
   #  L2:
   #  { elsePart }
   #  Lend:
-  var 
+  var
     a: TLoc
     Lelse: TLabel
+  if not isEmptyType(n.typ) and d.k == locNone:
+    getTemp(p, n.typ, d)
   genLineDir(p, n)
-  var Lend = getLabel(p)
+  let Lend = getLabel(p)
   for i in countup(0, sonsLen(n) - 1): 
-    var it = n.sons[i]
+    let it = n.sons[i]
     if it.len == 2: 
       when newScopeForIf: startBlock(p)
       initLocExpr(p, it.sons[0], a)
       Lelse = getLabel(p)
       inc(p.labels)
       lineFF(p, cpsStmts, "if (!$1) goto $2;$n",
-            "br i1 $1, label %LOC$3, label %$2$n" & "LOC$3: $n",
+            "br i1 $1, label %LOC$3, label %$2$nLOC$3: $n",
             [rdLoc(a), Lelse, toRope(p.labels)])
       when not newScopeForIf: startBlock(p)
-      genStmts(p, it.sons[1])
+      expr(p, it.sons[1], d)
       endBlock(p)
-      if sonsLen(n) > 1: 
+      if sonsLen(n) > 1:
         lineFF(p, cpsStmts, "goto $1;$n", "br label %$1$n", [Lend])
       fixLabel(p, Lelse)
     elif it.len == 1:
-      genSimpleBlock(p, it.sons[0])
-    else: internalError(n.info, "genIfStmt()")
+      startBlock(p)
+      expr(p, it.sons[0], d)
+      endBlock(p)
+    else: internalError(n.info, "genIf()")
   if sonsLen(n) > 1: fixLabel(p, Lend)
-  
+
 proc blockLeaveActions(p: BProc, howMany: int) = 
   var L = p.nestedTryStmts.len
   # danger of endless recursion! we workaround this here by a temp stack
@@ -312,16 +321,15 @@ proc genWhileStmt(p: BProc, t: PNode) =
 proc genBlock(p: BProc, t: PNode, d: var TLoc) =
   preserveBreakIdx:
     p.breakIdx = startBlock(p)
-    if t.sons[0].kind != nkEmpty: 
+    if t.sons[0].kind != nkEmpty:
       # named block?
       assert(t.sons[0].kind == nkSym)
       var sym = t.sons[0].sym
       sym.loc.k = locOther
       sym.loc.a = p.breakIdx
-    if t.kind == nkBlockExpr: genStmtListExpr(p, t.sons[1], d)
-    else: genStmts(p, t.sons[1])
+    expr(p, t.sons[1], d)
     endBlock(p)
-  
+
 proc genParForStmt(p: BProc, t: PNode) =
   assert(sonsLen(t) == 3)
   inc(p.withinLoop)
@@ -411,42 +419,45 @@ proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
       initLocExpr(p, b.sons[i], x)
       lineCg(p, cpsStmts, eqFormat, [rdCharLoc(e), rdCharLoc(x), labl])
 
-proc genCaseSecondPass(p: BProc, t: PNode, labId, until: int): TLabel = 
+proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc, 
+                       labId, until: int): TLabel = 
   var Lend = getLabel(p)
-  for i in 1..until: 
+  for i in 1..until:
     lineF(p, cpsStmts, "LA$1: ;$n", [toRope(labId + i)])
     if t.sons[i].kind == nkOfBranch:
       var length = sonsLen(t.sons[i])
-      genSimpleBlock(p, t.sons[i].sons[length - 1])
+      exprBlock(p, t.sons[i].sons[length - 1], d)
       lineF(p, cpsStmts, "goto $1;$n", [Lend])
-    else: 
-      genSimpleBlock(p, t.sons[i].sons[0])
+    else:
+      exprBlock(p, t.sons[i].sons[0], d)
   result = Lend
 
-proc genIfForCaseUntil(p: BProc, t: PNode, rangeFormat, eqFormat: TFormatStr,
-                       until: int, a: TLoc): TLabel = 
+proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
+                       rangeFormat, eqFormat: TFormatStr,
+                       until: int, a: TLoc): TLabel =
   # generate a C-if statement for a Nimrod case statement
   var labId = p.labels
-  for i in 1..until: 
+  for i in 1..until:
     inc(p.labels)
     if t.sons[i].kind == nkOfBranch: # else statement
-      genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat, 
+      genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat,
                            con("LA", toRope(p.labels)))
-    else: 
+    else:
       lineF(p, cpsStmts, "goto LA$1;$n", [toRope(p.labels)])
-  if until < t.len-1: 
+  if until < t.len-1:
     inc(p.labels)
     var gotoTarget = p.labels
     lineF(p, cpsStmts, "goto LA$1;$n", [toRope(gotoTarget)])
-    result = genCaseSecondPass(p, t, labId, until)
+    result = genCaseSecondPass(p, t, d, labId, until)
     lineF(p, cpsStmts, "LA$1: ;$n", [toRope(gotoTarget)])
   else:
-    result = genCaseSecondPass(p, t, labId, until)
+    result = genCaseSecondPass(p, t, d, labId, until)
 
-proc genCaseGeneric(p: BProc, t: PNode, rangeFormat, eqFormat: TFormatStr) = 
+proc genCaseGeneric(p: BProc, t: PNode, d: var TLoc, 
+                    rangeFormat, eqFormat: TFormatStr) = 
   var a: TLoc
   initLocExpr(p, t.sons[0], a)
-  var Lend = genIfForCaseUntil(p, t, rangeFormat, eqFormat, sonsLen(t)-1, a)
+  var Lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, sonsLen(t)-1, a)
   fixLabel(p, Lend)
 
 proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel, 
@@ -461,12 +472,12 @@ proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
     appcg(p.module, branches[j], "if (#eqStrings($1, $2)) goto $3;$n", 
          [rdLoc(e), rdLoc(x), labl])
 
-proc genStringCase(p: BProc, t: PNode) = 
+proc genStringCase(p: BProc, t: PNode, d: var TLoc) =
   # count how many constant strings there are in the case:
   var strings = 0
-  for i in countup(1, sonsLen(t) - 1): 
+  for i in countup(1, sonsLen(t) - 1):
     if t.sons[i].kind == nkOfBranch: inc(strings, sonsLen(t.sons[i]) - 1)
-  if strings > stringCaseThreshold: 
+  if strings > stringCaseThreshold:
     var bitMask = math.nextPowerOfTwo(strings) - 1
     var branches: seq[PRope]
     newSeq(branches, bitMask + 1)
@@ -484,23 +495,17 @@ proc genStringCase(p: BProc, t: PNode) =
     linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n", 
             rdLoc(a), toRope(bitMask))
     for j in countup(0, high(branches)):
-      when false:
-        let interior = cast[int](interiorAllocatedPtr(addr(branches[0])))+
-                                 2*sizeof(pointer)
-        let brn = cast[int](cast[pointer](branches))
-        if interior != brn:
-          echo "BUG! ", interior, "-", brn
       if branches[j] != nil:
         lineF(p, cpsStmts, "case $1: $n$2break;$n", 
              [intLiteral(j), branches[j]])
     lineF(p, cpsStmts, "}$n") # else statement:
-    if t.sons[sonsLen(t) - 1].kind != nkOfBranch: 
+    if t.sons[sonsLen(t)-1].kind != nkOfBranch: 
       lineF(p, cpsStmts, "goto LA$1;$n", [toRope(p.labels)]) 
     # third pass: generate statements
-    var Lend = genCaseSecondPass(p, t, labId, sonsLen(t)-1)
+    var Lend = genCaseSecondPass(p, t, d, labId, sonsLen(t)-1)
     fixLabel(p, Lend)
-  else: 
-    genCaseGeneric(p, t, "", "if (#eqStrings($1, $2)) goto $3;$n")
+  else:
+    genCaseGeneric(p, t, d, "", "if (#eqStrings($1, $2)) goto $3;$n")
   
 proc branchHasTooBigRange(b: PNode): bool = 
   for i in countup(0, sonsLen(b)-2): 
@@ -535,14 +540,14 @@ proc genCaseRange(p: BProc, branch: PNode) =
     else:
       lineF(p, cpsStmts, "case $1:$n", [genLiteral(p, branch[j])])
 
-proc genOrdinalCase(p: BProc, n: PNode) = 
+proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
   # analyse 'case' statement:
   var splitPoint = IfSwitchSplitPoint(p, n)
   
   # generate if part (might be empty):
   var a: TLoc
   initLocExpr(p, n.sons[0], a)
-  var Lend = if splitPoint > 0: genIfForCaseUntil(p, n, 
+  var Lend = if splitPoint > 0: genIfForCaseUntil(p, n, d,
                     rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n",
                     eqFormat = "if ($1 == $2) goto $3;$n", 
                     splitPoint, a) else: nil
@@ -555,28 +560,29 @@ proc genOrdinalCase(p: BProc, n: PNode) =
       var branch = n[i]
       if branch.kind == nkOfBranch: 
         genCaseRange(p, branch)
-        genSimpleBlock(p, branch.lastSon)
       else: 
         # else part of case statement:
         lineF(p, cpsStmts, "default:$n")
-        genSimpleBlock(p, branch[0])
         hasDefault = true
+      exprBlock(p, branch.lastSon, d)
       lineF(p, cpsStmts, "break;$n")
     if (hasAssume in CC[ccompiler].props) and not hasDefault: 
       lineF(p, cpsStmts, "default: __assume(0);$n")
     lineF(p, cpsStmts, "}$n")
   if Lend != nil: fixLabel(p, Lend)
   
-proc genCaseStmt(p: BProc, t: PNode) = 
+proc genCase(p: BProc, t: PNode, d: var TLoc) = 
   genLineDir(p, t)
+  if not isEmptyType(t.typ) and d.k == locNone:
+    getTemp(p, t.typ, d)
   case skipTypes(t.sons[0].typ, abstractVarRange).kind
-  of tyString: 
-    genStringCase(p, t)
+  of tyString:
+    genStringCase(p, t, d)
   of tyFloat..tyFloat128: 
-    genCaseGeneric(p, t, "if ($1 >= $2 && $1 <= $3) goto $4;$n", 
-                   "if ($1 == $2) goto $3;$n") 
-  else: 
-    genOrdinalCase(p, t)
+    genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n", 
+                            "if ($1 == $2) goto $3;$n")
+  else:
+    genOrdinalCase(p, t, d)
   
 proc hasGeneralExceptSection(t: PNode): bool = 
   var length = sonsLen(t)
@@ -588,7 +594,7 @@ proc hasGeneralExceptSection(t: PNode): bool =
     inc(i)
   result = false
 
-proc genTryStmtCpp(p: BProc, t: PNode) =
+proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
   # code to generate:
   #
   # XXX: There should be a standard dispatch algorithm
@@ -609,6 +615,8 @@ proc genTryStmtCpp(p: BProc, t: PNode) =
   #      }
   #  }
   #  finallyPart();
+  if not isEmptyType(t.typ) and d.k == locNone:
+    getTemp(p, t.typ, d)
   var
     exc: PRope
     i, length, blen: int
@@ -617,7 +625,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) =
   discard cgsym(p.module, "E_Base")
   add(p.nestedTryStmts, t)
   startBlock(p, "try {$n")
-  genStmts(p, t.sons[0])
+  expr(p, t.sons[0], d)
   length = sonsLen(t)
   endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc]))
   if optStackTrace in p.Options:
@@ -631,7 +639,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) =
     if blen == 1:
       # general except section:
       catchAllPresent = true
-      genSimpleBlock(p, t.sons[i].sons[0])
+      exprBlock(p, t.sons[i].sons[0], d)
     else:
       var orExpr: PRope = nil
       for j in countup(0, blen - 2):
@@ -641,7 +649,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) =
               "#isObj($1.exp->m_type, $2)",
               [exc, genTypeInfo(p.module, t.sons[i].sons[j].typ)])
       lineF(p, cpsStmts, "if ($1) ", [orExpr])
-      genSimpleBlock(p, t.sons[i].sons[blen-1])
+      exprBlock(p, t.sons[i].sons[blen-1], d)
     inc(i)
   
   # reraise the exception if there was no catch all
@@ -651,7 +659,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) =
     startBlock(p)
     var finallyBlock = t.lastSon
     if finallyBlock.kind == nkFinally:
-      genStmts(p, finallyBlock.sons[0])
+      expr(p, finallyBlock.sons[0], d)
     line(p, cpsStmts, ~"throw;$n")
     endBlock(p)
   
@@ -660,9 +668,9 @@ proc genTryStmtCpp(p: BProc, t: PNode) =
   
   discard pop(p.nestedTryStmts)
   if (i < length) and (t.sons[i].kind == nkFinally):
-    genSimpleBlock(p, t.sons[i].sons[0])
+    exprBlock(p, t.sons[i].sons[0], d)
   
-proc genTryStmt(p: BProc, t: PNode) = 
+proc genTry(p: BProc, t: PNode, d: var TLoc) = 
   # code to generate:
   #
   # XXX: There should be a standard dispatch algorithm
@@ -691,6 +699,8 @@ proc genTryStmt(p: BProc, t: PNode) =
   #  if (exception not cleared)
   #    propagateCurrentException();
   #
+  if not isEmptyType(t.typ) and d.k == locNone:
+    getTemp(p, t.typ, d)
   genLineDir(p, t)
   var safePoint = getTempName()
   discard cgsym(p.module, "E_Base")
@@ -700,7 +710,7 @@ proc genTryStmt(p: BProc, t: PNode) =
   startBlock(p, "if ($1.status == 0) {$n", [safePoint])
   var length = sonsLen(t)
   add(p.nestedTryStmts, t)
-  genStmts(p, t.sons[0])
+  expr(p, t.sons[0], d)
   linefmt(p, cpsStmts, "#popSafePoint();$n")
   endBlock(p)
   startBlock(p, "else {$n")
@@ -716,7 +726,7 @@ proc genTryStmt(p: BProc, t: PNode) =
       if i > 1: lineF(p, cpsStmts, "else")
       startBlock(p)
       linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint)
-      genStmts(p, t.sons[i].sons[0])
+      expr(p, t.sons[i].sons[0], d)
       linefmt(p, cpsStmts, "#popCurrentException();$n")
       endBlock(p)
     else:
@@ -730,7 +740,7 @@ proc genTryStmt(p: BProc, t: PNode) =
       if i > 1: line(p, cpsStmts, "else ")
       startBlock(p, "if ($1) {$n", [orExpr])
       linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint)
-      genStmts(p, t.sons[i].sons[blen-1])
+      expr(p, t.sons[i].sons[blen-1], d)
       linefmt(p, cpsStmts, "#popCurrentException();$n")
       endBlock(p)
     inc(i)
@@ -738,7 +748,7 @@ proc genTryStmt(p: BProc, t: PNode) =
   discard pop(p.nestedTryStmts)
   endBlock(p) # end of else block
   if i < length and t.sons[i].kind == nkFinally:
-    genSimpleBlock(p, t.sons[i].sons[0])
+    exprBlock(p, t.sons[i].sons[0], d)
   linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint)
 
 proc genAsmOrEmitStmt(p: BProc, t: PNode): PRope = 
@@ -862,61 +872,5 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
 
 proc genStmts(p: BProc, t: PNode) = 
   var a: TLoc
-  case t.kind
-  of nkEmpty: 
-    nil
-  of nkStmtList: 
-    for i in countup(0, sonsLen(t) - 1): genStmts(p, t.sons[i])
-  of nkBlockStmt: genBlock(p, t, a)
-  of nkIfStmt: genIfStmt(p, t)
-  of nkWhileStmt: genWhileStmt(p, t)
-  of nkVarSection, nkLetSection: genVarStmt(p, t)
-  of nkConstSection: genConstStmt(p, t)
-  of nkForStmt: internalError(t.info, "for statement not eliminated")
-  of nkCaseStmt: genCaseStmt(p, t)
-  of nkReturnStmt: genReturnStmt(p, t)
-  of nkBreakStmt: genBreakStmt(p, t)
-  of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand, 
-     nkCallStrLit, nkClosure: 
-    genLineDir(p, t)
-    initLocExpr(p, t, a)
-  of nkAsgn: genAsgn(p, t, fastAsgn=false)
-  of nkFastAsgn: 
-    # transf is overly aggressive with 'nkFastAsgn', so we work around here.
-    # See tests/run/tcnstseq3 for an example that would fail otherwise.
-    genAsgn(p, t, fastAsgn=p.prc != nil)
-  of nkDiscardStmt:
-    if t.sons[0].kind != nkEmpty:
-      genLineDir(p, t)
-      initLocExpr(p, t.sons[0], a)
-  of nkAsmStmt: genAsmStmt(p, t)
-  of nkTryStmt: 
-    if gCmd == cmdCompileToCpp: genTryStmtCpp(p, t)
-    else: genTryStmt(p, t)
-  of nkRaiseStmt: genRaiseStmt(p, t)
-  of nkTypeSection: 
-    # we have to emit the type information for object types here to support
-    # separate compilation:
-    genTypeSection(p.module, t)
-  of nkCommentStmt, nkNilLit, nkIteratorDef, nkIncludeStmt, 
-     nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, 
-     nkFromStmt, nkTemplateDef, nkMacroDef: 
-    nil
-  of nkPragma: genPragma(p, t)
-  of nkProcDef, nkMethodDef, nkConverterDef: 
-    if (t.sons[genericParamsPos].kind == nkEmpty): 
-      var prc = t.sons[namePos].sym
-      if (optDeadCodeElim notin gGlobalOptions and
-          sfDeadCodeElim notin getModule(prc).flags) or
-          ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
-          (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
-          (prc.kind == skMethod): 
-        # we have not only the header: 
-        if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags: 
-          genProc(p.module, prc)
-  of nkParForStmt: genParForStmt(p, t)
-  of nkState: genState(p, t)
-  of nkGotoState: genGotoState(p, t)
-  of nkBreakState: genBreakState(p, t)
-  else: internalError(t.info, "genStmts(" & $t.kind & ')')
-  
+  expr(p, t, a)
+  InternalAssert a.k notin {locNone, locTemp}
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 753576aa0..cb796e456 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -552,9 +552,21 @@ proc genVarPrototype(m: BModule, sym: PSym)
 proc requestConstImpl(p: BProc, sym: PSym)
 proc genProc(m: BModule, prc: PSym)
 proc genStmts(p: BProc, t: PNode)
+proc expr(p: BProc, n: PNode, d: var TLoc)
 proc genProcPrototype(m: BModule, sym: PSym)
+proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc)
+proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
+proc intLiteral(i: biggestInt): PRope
+proc genLiteral(p: BProc, n: PNode): PRope
 
-include "ccgexprs.nim", "ccgstmts.nim"
+proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
+  initLoc(result, locNone, e.typ, OnUnknown)
+  expr(p, e, result)
+
+proc lenField: PRope {.inline.} =
+  result = toRope(if gCmd != cmdCompileToCpp: "Sup.len" else: "len")
+
+include ccgcalls, "ccgstmts.nim", "ccgexprs.nim"
 
 # ----------------------------- dynamic library handling -----------------
 # We don't finalize dynamic libs as this does the OS for us.
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 4a3377681..e2167f460 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -913,15 +913,15 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
 proc parseExpr(p: var TParser): PNode = 
   #| expr = (ifExpr
   #|       | whenExpr
-  #|       | caseExpr)
+  #|       | caseExpr
+  #|       | tryStmt)
   #|       / simpleExpr
   case p.tok.tokType:
   of tkIf: result = parseIfExpr(p, nkIfExpr)
   of tkWhen: result = parseIfExpr(p, nkWhenExpr)
   of tkCase: result = parseCase(p)
+  of tkTry: result = parseTry(p)
   else: result = simpleExpr(p)
-  # XXX needs proper support:
-  #of tkTry: result = parseTry(p)
 
 proc parseObject(p: var TParser): PNode
 proc parseDistinct(p: var TParser): PNode
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 1333e40c4..d68d2e549 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -939,10 +939,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     gsub(g, n.sons[0])
     put(g, tkDotDot, "..")
     gsub(g, n.sons[1])
-  of nkDerefExpr: 
+  of nkDerefExpr:
     gsub(g, n.sons[0])
-    putWithSpace(g, tkOpr, "^") 
-    # unfortunately this requires a space, because ^. would be only one opr
+    put(g, tkOpr, "[]")
   of nkAccQuoted:
     put(g, tkAccent, "`")
     if n.len > 0: gsub(g, n.sons[0])
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 791ca2793..cd332ad90 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -68,8 +68,10 @@ proc commonType*(x, y: PType): PType =
   var a = skipTypes(x, {tyGenericInst})
   var b = skipTypes(y, {tyGenericInst})
   result = x
-  if a.kind in {tyExpr, tyNil}: return y
-  elif b.kind in {tyExpr, tyNil}: return x
+  if a.kind in {tyExpr, tyNil}: result = y
+  elif b.kind in {tyExpr, tyNil}: result = x
+  elif a.kind == tyStmt: result = a
+  elif b.kind == tyStmt: result = b
   elif b.kind in {tyArray, tyArrayConstr, tySet, tySequence} and 
       a.kind == b.kind:
     # check for seq[empty] vs. seq[int]
diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim
new file mode 100644
index 000000000..2383ac649
--- /dev/null
+++ b/compiler/semdestruct.nim
@@ -0,0 +1,213 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2013 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements destructors.
+
+
+# special marker values that indicates that we are
+# 1) AnalyzingDestructor: currently analyzing the type for destructor 
+# generation (needed for recursive types)
+# 2) DestructorIsTrivial: completed the analysis before and determined
+# that the type has a trivial destructor
+var AnalyzingDestructor, DestructorIsTrivial: PSym
+new(AnalyzingDestructor)
+new(DestructorIsTrivial)
+
+var
+  destructorName = getIdent"destroy_"
+  destructorParam = getIdent"this_"
+  destructorPragma = newIdentNode(getIdent"destructor", UnknownLineInfo())
+  rangeDestructorProc*: PSym
+
+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 destroyField(c: PContext, field: PSym, holder: PNode): PNode =
+  if instantiateDestructor(c, field.typ):
+    result = newNode(nkCall, field.info, @[
+      useSym(field.typ.destructor),
+      newNode(nkDotExpr, field.info, @[holder, useSym(field)])])
+
+proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
+  var nonTrivialFields = 0
+  result = newNode(nkCaseStmt, n.info, @[])
+  # case x.kind
+  result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]]))
+  for i in countup(1, n.len - 1):
+    # of A, B:
+    var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2])
+    let recList = n[i].lastSon
+    var destroyRecList = newNode(nkStmtList, n[i].info, @[])
+    template addField(f: expr): stmt =
+      let stmt = destroyField(c, f, holder)
+      if stmt != nil:
+        destroyRecList.addSon(stmt)
+        inc nonTrivialFields
+        
+    case recList.kind
+    of nkSym:
+      addField(recList.sym)
+    of nkRecList:
+      for j in countup(0, recList.len - 1):
+        addField(recList[j].sym)
+    else:
+      internalAssert false
+      
+    caseBranch.addSon(destroyRecList)
+    result.addSon(caseBranch)
+  # maybe no fields were destroyed?
+  if nonTrivialFields == 0:
+    result = nil
+ 
+proc generateDestructor(c: PContext, t: PType): PNode =
+  ## generate a destructor for a user-defined object or tuple type
+  ## returns nil if the destructor turns out to be trivial
+  
+  template addLine(e: expr): stmt =
+    if result == nil: result = newNode(nkStmtList)
+    result.addSon(e)
+
+  # XXX: This may be true for some C-imported types such as
+  # Tposix_spawnattr
+  if t.n == nil or t.n.sons == nil: return
+  internalAssert t.n.kind == nkRecList
+  let destructedObj = newIdentNode(destructorParam, UnknownLineInfo())
+  # call the destructods of all fields
+  for s in countup(0, t.n.sons.len - 1):
+    case t.n.sons[s].kind
+    of nkRecCase:
+      let stmt = destroyCase(c, t.n.sons[s], destructedObj)
+      if stmt != nil: addLine(stmt)
+    of nkSym:
+      let stmt = destroyField(c, t.n.sons[s].sym, destructedObj)
+      if stmt != nil: addLine(stmt)
+    else:
+      internalAssert false
+  # base classes' destructors will be automatically called by
+  # semProcAux for both auto-generated and user-defined destructors
+
+proc instantiateDestructor(c: PContext, typ: PType): bool =
+  # returns true if the type already had a user-defined
+  # destructor or if the compiler generated a default
+  # member-wise one
+  var t = skipTypes(typ, {tyConst, tyMutable})
+  
+  if t.destructor != nil:
+    # XXX: This is not entirely correct for recursive types, but we need
+    # it temporarily to hide the "destroy is already defined" problem
+    return t.destructor notin [AnalyzingDestructor, DestructorIsTrivial]
+  
+  case t.kind
+  of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
+    if instantiateDestructor(c, t.sons[0]):
+      if rangeDestructorProc == nil:
+        rangeDestructorProc = SymtabGet(c.tab, getIdent"nimDestroyRange")
+      t.destructor = rangeDestructorProc
+      return true
+    else:
+      return false
+  of tyTuple, tyObject:
+    t.destructor = AnalyzingDestructor
+    let generated = generateDestructor(c, t)
+    if generated != nil:
+      internalAssert t.sym != nil
+      var i = t.sym.info
+      let fullDef = newNode(nkProcDef, i, @[
+        newIdentNode(destructorName, i),
+        emptyNode,
+        emptyNode,
+        newNode(nkFormalParams, i, @[
+          emptyNode,
+          newNode(nkIdentDefs, i, @[
+            newIdentNode(destructorParam, i),
+            useSym(t.sym),
+            emptyNode]),
+          ]),
+        newNode(nkPragma, i, @[destructorPragma]),
+        emptyNode,
+        generated
+        ])
+      discard semProc(c, fullDef)
+      internalAssert t.destructor != nil
+      return true
+    else:
+      t.destructor = DestructorIsTrivial
+      return false
+  else:
+    return false
+
+proc insertDestructors(c: PContext,
+                       varSection: PNode): tuple[outer, inner: PNode] =
+  # Accepts a var or let section.
+  #
+  # When a var section has variables with destructors
+  # the var section is split up and finally blocks are inserted
+  # immediately after all "destructable" vars
+  #
+  # In case there were no destrucable variables, the proc returns
+  # (nil, nil) and the enclosing stmt-list requires no modifications.
+  #
+  # Otherwise, after the try blocks are created, the rest of the enclosing
+  # stmt-list should be inserted in the most `inner` such block (corresponding
+  # to the last variable).
+  #
+  # `outer` is a statement list that should replace the original var section.
+  # It will include the new truncated var section followed by the outermost
+  # try block.
+  let totalVars = varSection.sonsLen
+  for j in countup(0, totalVars - 1):
+    let
+      varId = varSection[j][0]
+      varTyp = varId.sym.typ
+      info = varId.info
+
+    if varTyp != nil and instantiateDestructor(c, varTyp) and 
+        sfGlobal notin varId.sym.flags:
+      var tryStmt = newNodeI(nkTryStmt, info)
+
+      if j < totalVars - 1:
+        var remainingVars = newNodeI(varSection.kind, info)
+        remainingVars.sons = varSection.sons[(j+1)..(-1)]
+        let (outer, inner) = insertDestructors(c, remainingVars)
+        if outer != nil:
+          tryStmt.addSon(outer)
+          result.inner = inner
+        else:
+          result.inner = newNodeI(nkStmtList, info)
+          result.inner.addSon(remainingVars)
+          tryStmt.addSon(result.inner)
+      else:
+        result.inner = newNodeI(nkStmtList, info)
+        tryStmt.addSon(result.inner)
+
+      tryStmt.addSon(
+        newNode(nkFinally, info, @[
+          semStmt(c, newNode(nkCall, info, @[
+            useSym(varTyp.destructor),
+            useSym(varId.sym)]))]))
+
+      result.outer = newNodeI(nkStmtList, info)
+      varSection.sons.setLen(j+1)
+      result.outer.addSon(varSection)
+      result.outer.addSon(tryStmt)
+
+      return
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index e2a3c0978..cba4ed081 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -19,24 +19,6 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode =
 
 proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
 
-proc performProcvarCheck(c: PContext, n: PNode, s: PSym) =
-  var smoduleId = getModule(s).id
-  if sfProcVar notin s.flags and s.typ.callConv == ccDefault and
-      smoduleId != c.module.id and smoduleId != c.friendModule.id: 
-    LocalError(n.info, errXCannotBePassedToProcVar, s.name.s)
-
-proc semProcvarCheck(c: PContext, n: PNode) =
-  let n = n.skipConv
-  if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skIterator,
-                                        skConverter}:
-    performProcvarCheck(c, n, n.sym)
-
-proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
-  if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}:
-    if instantiateDestructor(c, n.typ):
-      LocalError(n.info, errGenerated,
-        "usage of a type with a destructor in a non destructible context")
-
 proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   # same as 'semExprWithType' but doesn't check for proc vars
   result = semExpr(c, n, flags)
@@ -781,16 +763,6 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode =
   # and check 'arg' for semantics again:
   addSon(result, semExpr(c, arg))
 
-proc discardCheck(result: PNode) =
-  if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
-    if result.kind == nkNilLit:
-      # XXX too much work and fixing would break bootstrapping:
-      #Message(n.info, warnNilStatement)
-      result.typ = nil
-    elif not ImplicitlyDiscardable(result) and result.typ.kind != tyError and
-        gCmd != cmdInteractive:
-      localError(result.info, errDiscardValue)
-
 proc semExprNoType(c: PContext, n: PNode): PNode =
   result = semExpr(c, n, {efWantStmt})
   discardCheck(result)
@@ -1125,14 +1097,16 @@ proc semAsgn(c: PContext, n: PNode): PNode =
     var
       rhs = semExprWithType(c, n.sons[1], 
         if lhsIsResult: {efAllowDestructor} else: {})
-    if lhsIsResult and lhs.sym.typ.kind == tyGenericParam:
-      if matchTypeClass(lhs.typ, rhs.typ):
-        InternalAssert c.p.resultSym != nil
-        lhs.typ = rhs.typ
-        c.p.resultSym.typ = rhs.typ
-        c.p.owner.typ.sons[0] = rhs.typ
-      else:
-        typeMismatch(n, lhs.typ, rhs.typ)
+    if lhsIsResult:
+      n.typ = EnforceVoidContext
+      if lhs.sym.typ.kind == tyGenericParam:
+        if matchTypeClass(lhs.typ, rhs.typ):
+          InternalAssert c.p.resultSym != nil
+          lhs.typ = rhs.typ
+          c.p.resultSym.typ = rhs.typ
+          c.p.owner.typ.sons[0] = rhs.typ
+        else:
+          typeMismatch(n, lhs.typ, rhs.typ)
 
     n.sons[1] = fitNode(c, le, rhs)
     fixAbstractType(c, n)
@@ -1481,52 +1455,13 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
       if result == nil: 
         setResult(it.sons[0])
     else: illFormedAst(n)
-  if result == nil: 
-    result = newNodeI(nkNilLit, n.info) 
+  if result == nil:
+    result = newNodeI(nkEmpty, n.info) 
   # The ``when`` statement implements the mechanism for platform dependent
   # code. Thus we try to ensure here consistent ID allocation after the
   # ``when`` statement.
   IDsynchronizationPoint(200)
 
-
-proc semExprBranch(c: PContext, n: PNode): PNode =
-  result = semExpr(c, n)
-  if result.typ != nil:
-    # XXX tyGenericInst here?
-    semProcvarCheck(c, result)
-    if result.typ.kind == tyVar: result = newDeref(result)
-    semDestructorCheck(c, result, {})
-
-proc semIf(c: PContext, n: PNode): PNode = 
-  result = n
-  var typ = CommonTypeBegin
-  var hasElse = false
-  for i in countup(0, sonsLen(n) - 1): 
-    var it = n.sons[i]
-    if it.len == 2:
-      when newScopeForIf: openScope(c.tab)
-      it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
-      when not newScopeForIf: openScope(c.tab)
-      it.sons[1] = semExprBranch(c, it.sons[1])
-      typ = commonType(typ, it.sons[1].typ)
-      closeScope(c.tab)
-    elif it.len == 1:
-      hasElse = true
-      openScope(c.tab)
-      it.sons[0] = semExprBranch(c, it.sons[0])
-      typ = commonType(typ, it.sons[0].typ)
-      closeScope(c.tab)
-    else: illFormedAst(it)
-  if isEmptyType(typ) or not hasElse:
-    for it in n: discardCheck(it.lastSon)
-    result.kind = nkIfStmt
-  else:
-    for it in n:
-      let j = it.len-1
-      it.sons[j] = fitNode(c, typ, it.sons[j])
-    result.kind = nkIfExpr
-    result.typ = typ
-
 proc semSetConstr(c: PContext, n: PNode): PNode = 
   result = newNodeI(nkCurly, n.info)
   result.typ = newTypeS(tySet, c)
@@ -1692,26 +1627,21 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
     it.sons[1] = e
     # XXX object field name check for 'case objects' if the kind is static?
 
-proc semStmtListExpr(c: PContext, n: PNode): PNode = 
-  result = n
-  checkMinSonsLen(n, 1)
-  var length = sonsLen(n)
-  for i in countup(0, length - 2): 
-    n.sons[i] = semStmt(c, n.sons[i])
-  if length > 0: 
-    n.sons[length - 1] = semExprWithType(c, n.sons[length - 1])
-    n.typ = n.sons[length - 1].typ
-
-proc semBlockExpr(c: PContext, n: PNode): PNode = 
+proc semBlock(c: PContext, n: PNode): PNode =
   result = n
   Inc(c.p.nestedBlockCounter)
   checkSonsLen(n, 2)
   openScope(c.tab)            # BUGFIX: label is in the scope of block!
-  if n.sons[0].kind notin {nkEmpty, nkSym}:
-    # nkSym for gensym'ed labels:
-    addDecl(c, newSymS(skLabel, n.sons[0], c))
-  n.sons[1] = semStmtListExpr(c, n.sons[1])
+  if n.sons[0].kind != nkEmpty:
+    var labl = newSymG(skLabel, n.sons[0], c)
+    if sfGenSym notin labl.flags:
+      addDecl(c, labl)
+      n.sons[0] = newSymNode(labl, n.sons[0].info)
+    suggestSym(n.sons[0], labl)
+  n.sons[1] = semExpr(c, n.sons[1])
   n.typ = n.sons[1].typ
+  if isEmptyType(n.typ): n.kind = nkBlockStmt
+  else: n.kind = nkBlockExpr
   closeScope(c.tab)
   Dec(c.p.nestedBlockCounter)
 
@@ -1731,57 +1661,6 @@ proc buildCall(n: PNode): PNode =
   else:
     result = n
 
-proc semCaseExpr(c: PContext, caseStmt: PNode): PNode =
-  # The case expression is simply rewritten to a StmtListExpr:
-  #   var res {.noInit, genSym.}: type(values)
-  #
-  #   case E
-  #   of X: res = value1
-  #   of Y: res = value2
-  # 
-  #   res
-  var
-    info = caseStmt.info
-    resVar = newSym(skVar, idAnon, getCurrOwner(), info)
-    resNode = newSymNode(resVar, info)
-    resType: PType
-
-  resVar.flags = { sfGenSym, sfNoInit }
-
-  for i in countup(1, caseStmt.len - 1):
-    var cs = caseStmt[i]
-    case cs.kind
-    of nkOfBranch, nkElifBranch, nkElse:
-      # the value is always the last son regardless of the branch kind
-      cs.checkMinSonsLen 1
-      var value = cs{-1}
-      if value.kind == nkStmtList: value.kind = nkStmtListExpr
-
-      value = semExprWithType(c, value)
-      if resType == nil:
-        resType = value.typ
-      elif not sameType(resType, value.typ):
-        # XXX: semeType is a bit too harsh.
-        # work on finding a common base type.
-        # this will be useful for arrays/seq too:
-        # [ref DerivedA, ref DerivedB, ref Base]
-        typeMismatch(cs, resType, value.typ)
-
-      cs{-1} = newNode(nkAsgn, cs.info, @[resNode, value])
-    else:
-      IllFormedAst(caseStmt)
-
-  result = newNode(nkStmtListExpr, info, @[
-    newNode(nkVarSection, info, @[
-      newNode(nkIdentDefs, info, @[
-        resNode,
-        symNodeFromType(c, resType, info),
-        emptyNode])]),
-    caseStmt,
-    resNode])
-
-  result = semStmtListExpr(c, result)
-
 proc fixImmediateParams(n: PNode): PNode =
   # XXX: Temporary work-around until we carry out
   # the planned overload resolution reforms
@@ -1946,7 +1825,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkObjConstr: result = semObjConstr(c, n, flags)
   of nkLambdaKinds: result = semLambda(c, n, flags)
   of nkDerefExpr: result = semDeref(c, n)
-  of nkAddr: 
+  of nkAddr:
     result = n
     checkSonsLen(n, 1)
     n.sons[0] = semExprWithType(c, n.sons[0])
@@ -1958,8 +1837,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     n.sons[0] = semExpr(c, n.sons[0], flags)
   of nkCast: result = semCast(c, n)
   of nkIfExpr, nkIfStmt: result = semIf(c, n)
-  of nkStmtListExpr: result = semStmtListExpr(c, n)
-  of nkBlockExpr: result = semBlockExpr(c, n)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv: 
     checkSonsLen(n, 2)
   of nkStringToCString, nkCStringToString, nkObjDownConv, nkObjUpConv: 
@@ -1976,8 +1853,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkStaticExpr:
     result = semStaticExpr(c, n)
   of nkAsgn: result = semAsgn(c, n)
-  of nkBlockStmt: result = semBlock(c, n)
-  of nkStmtList: result = semStmtList(c, n)
+  of nkBlockStmt, nkBlockExpr: result = semBlock(c, n)
+  of nkStmtList, nkStmtListExpr: result = semStmtList(c, n)
   of nkRaiseStmt: result = semRaise(c, n)
   of nkVarSection: result = semVarOrLet(c, n, skVar)
   of nkLetSection: result = semVarOrLet(c, n, skLet)
@@ -1988,9 +1865,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkTryStmt: result = semTry(c, n)
   of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n)
   of nkForStmt, nkParForStmt: result = semFor(c, n)
-  of nkCaseStmt:
-    if efWantStmt in flags: result = semCase(c, n)
-    else: result = semCaseExpr(c, n)
+  of nkCaseStmt: result = semCase(c, n)
   of nkReturnStmt: result = semReturn(c, n)
   of nkAsmStmt: result = semAsm(c, n)
   of nkYieldStmt: result = semYield(c, n)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index e1d8ed5a8..2811c107a 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -40,21 +40,6 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
   elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0): 
     localError(n.info, errInvalidControlFlowX, 
                renderTree(n, {renderNoComments}))
-  
-proc semBlock(c: PContext, n: PNode): PNode = 
-  result = n
-  Inc(c.p.nestedBlockCounter)
-  checkSonsLen(n, 2)
-  openScope(c.tab)            # BUGFIX: label is in the scope of block!
-  if n.sons[0].kind != nkEmpty:
-    var labl = newSymG(skLabel, n.sons[0], c)
-    if sfGenSym notin labl.flags:
-      addDecl(c, labl)
-      n.sons[0] = newSymNode(labl, n.sons[0].info)
-    suggestSym(n.sons[0], labl)
-  n.sons[1] = semStmt(c, n.sons[1])
-  closeScope(c.tab)
-  Dec(c.p.nestedBlockCounter)
 
 proc semAsm(con: PContext, n: PNode): PNode = 
   checkSonsLen(n, 2)
@@ -79,20 +64,113 @@ proc toCover(t: PType): biggestInt =
   else:
     result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc}))
 
-proc semCase(c: PContext, n: PNode): PNode = 
-  # check selector:
+proc performProcvarCheck(c: PContext, n: PNode, s: PSym) =
+  var smoduleId = getModule(s).id
+  if sfProcVar notin s.flags and s.typ.callConv == ccDefault and
+      smoduleId != c.module.id and smoduleId != c.friendModule.id: 
+    LocalError(n.info, errXCannotBePassedToProcVar, s.name.s)
+
+proc semProcvarCheck(c: PContext, n: PNode) =
+  let n = n.skipConv
+  if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skIterator,
+                                        skConverter}:
+    performProcvarCheck(c, n, n.sym)
+
+proc semProc(c: PContext, n: PNode): PNode
+
+include semdestruct
+
+proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
+  if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}:
+    if instantiateDestructor(c, n.typ):
+      LocalError(n.info, errGenerated,
+        "usage of a type with a destructor in a non destructible context")
+
+proc newDeref(n: PNode): PNode {.inline.} =  
+  result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
+  addSon(result, n)
+
+proc semExprBranch(c: PContext, n: PNode): PNode =
+  result = semExpr(c, n)
+  if result.typ != nil:
+    # XXX tyGenericInst here?
+    semProcvarCheck(c, result)
+    if result.typ.kind == tyVar: result = newDeref(result)
+    semDestructorCheck(c, result, {})
+
+proc semExprBranchScope(c: PContext, n: PNode): PNode =
+  openScope(c.tab)
+  result = semExprBranch(c, n)
+  closeScope(c.tab)
+
+proc ImplicitlyDiscardable(n: PNode): bool =
+  result = isCallExpr(n) and n.sons[0].kind == nkSym and 
+           sfDiscardable in n.sons[0].sym.flags
+
+proc fixNilType(n: PNode) =
+  if isAtom(n):
+    if n.kind != nkNilLit and n.typ != nil:
+      localError(n.info, errDiscardValue)
+  else:
+    for it in n: fixNilType(it)
+  n.typ = nil
+
+proc discardCheck(result: PNode) =
+  if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
+    if result.kind == nkNilLit:
+      # XXX too much work and fixing would break bootstrapping:
+      #Message(n.info, warnNilStatement)
+      result.typ = nil
+    elif not ImplicitlyDiscardable(result) and result.typ.kind != tyError and
+        gCmd != cmdInteractive:
+      if result.typ.kind == tyNil:
+        fixNilType(result)
+      else:
+        localError(result.info, errDiscardValue)
+
+proc semIf(c: PContext, n: PNode): PNode = 
+  result = n
+  var typ = CommonTypeBegin
+  var hasElse = false
+  for i in countup(0, sonsLen(n) - 1): 
+    var it = n.sons[i]
+    if it.len == 2:
+      when newScopeForIf: openScope(c.tab)
+      it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
+      when not newScopeForIf: openScope(c.tab)
+      it.sons[1] = semExprBranch(c, it.sons[1])
+      typ = commonType(typ, it.sons[1].typ)
+      closeScope(c.tab)
+    elif it.len == 1:
+      hasElse = true
+      it.sons[0] = semExprBranchScope(c, it.sons[0])
+      typ = commonType(typ, it.sons[0].typ)
+    else: illFormedAst(it)
+  if isEmptyType(typ) or not hasElse:
+    for it in n: discardCheck(it.lastSon)
+    result.kind = nkIfStmt
+  else:
+    for it in n:
+      let j = it.len-1
+      it.sons[j] = fitNode(c, typ, it.sons[j])
+    result.kind = nkIfExpr
+    result.typ = typ
+
+proc semCase(c: PContext, n: PNode): PNode =
   result = n
   checkMinSonsLen(n, 2)
   openScope(c.tab)
   n.sons[0] = semExprWithType(c, n.sons[0])
   var chckCovered = false
   var covered: biggestint = 0
+  var typ = CommonTypeBegin
+  var hasElse = false
   case skipTypes(n.sons[0].Typ, abstractVarRange-{tyTypeDesc}).Kind
-  of tyInt..tyInt64, tyChar, tyEnum: 
+  of tyInt..tyInt64, tyChar, tyEnum:
     chckCovered = true
-  of tyFloat..tyFloat128, tyString, tyError: 
+  of tyFloat..tyFloat128, tyString, tyError:
     nil
-  else: 
+  else:
     LocalError(n.info, errSelectorMustBeOfCertainTypes)
     return
   for i in countup(1, sonsLen(n) - 1): 
@@ -101,21 +179,81 @@ proc semCase(c: PContext, n: PNode): PNode =
     of nkOfBranch: 
       checkMinSonsLen(x, 2)
       semCaseBranch(c, n, x, i, covered)
-      var length = sonsLen(x)
-      x.sons[length - 1] = semStmtScope(c, x.sons[length - 1])
-    of nkElifBranch: 
+      var last = sonsLen(x)-1
+      x.sons[last] = semExprBranchScope(c, x.sons[last])
+      typ = commonType(typ, x.sons[last].typ)
+    of nkElifBranch:
       chckCovered = false
       checkSonsLen(x, 2)
+      when newScopeForIf: openScope(c.tab)
       x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
-      x.sons[1] = semStmtScope(c, x.sons[1])
-    of nkElse: 
+      when not newScopeForIf: openScope(c.tab)
+      x.sons[1] = semExprBranch(c, x.sons[1])
+      typ = commonType(typ, x.sons[1].typ)
+      closeScope(c.tab)
+    of nkElse:
       chckCovered = false
       checkSonsLen(x, 1)
-      x.sons[0] = semStmtScope(c, x.sons[0])
-    else: illFormedAst(x)
+      x.sons[0] = semExprBranchScope(c, x.sons[0])
+      typ = commonType(typ, x.sons[0].typ)
+      hasElse = true
+    else:
+      illFormedAst(x)
   if chckCovered and (covered != toCover(n.sons[0].typ)): 
     localError(n.info, errNotAllCasesCovered)
   closeScope(c.tab)
+  if isEmptyType(typ) or not hasElse:
+    for i in 1..n.len-1: discardCheck(n.sons[i].lastSon)
+  else:
+    for i in 1..n.len-1:
+      var it = n.sons[i]
+      let j = it.len-1
+      it.sons[j] = fitNode(c, typ, it.sons[j])
+    result.typ = typ
+
+proc semTry(c: PContext, n: PNode): PNode = 
+  result = n
+  inc c.p.inTryStmt
+  checkMinSonsLen(n, 2)
+  var typ = CommonTypeBegin
+  n.sons[0] = semExprBranchScope(c, n.sons[0])
+  typ = commonType(typ, n.sons[0].typ)
+  var check = initIntSet()
+  for i in countup(1, sonsLen(n) - 1): 
+    var a = n.sons[i]
+    checkMinSonsLen(a, 1)
+    var length = sonsLen(a)
+    if a.kind == nkExceptBranch:
+      # XXX what does this do? so that ``except [a, b, c]`` is supported?
+      if length == 2 and a.sons[0].kind == nkBracket:
+        a.sons[0..0] = a.sons[0].sons
+        length = a.sonsLen
+
+      for j in countup(0, length-2):
+        var typ = semTypeNode(c, a.sons[j], nil)
+        if typ.kind == tyRef: typ = typ.sons[0]
+        if typ.kind != tyObject:
+          LocalError(a.sons[j].info, errExprCannotBeRaised)
+        a.sons[j] = newNodeI(nkType, a.sons[j].info)
+        a.sons[j].typ = typ
+        if ContainsOrIncl(check, typ.id):
+          localError(a.sons[j].info, errExceptionAlreadyHandled)
+    elif a.kind != nkFinally: 
+      illFormedAst(n)
+    # last child of an nkExcept/nkFinally branch is a statement:
+    a.sons[length-1] = semExprBranchScope(c, a.sons[length-1])
+    typ = commonType(typ, a.sons[length-1].typ)
+  dec c.p.inTryStmt
+  if isEmptyType(typ):
+    discardCheck(n.sons[0])
+    for i in 1..n.len-1: discardCheck(n.sons[i].lastSon)
+  else:
+    n.sons[0] = fitNode(c, typ, n.sons[0])
+    for i in 1..n.len-1:
+      var it = n.sons[i]
+      let j = it.len-1
+      it.sons[j] = fitNode(c, typ, it.sons[j])
+    result.typ = typ
   
 proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode = 
   result = fitNode(c, typ, n)
@@ -438,10 +576,6 @@ proc semForVars(c: PContext, n: PNode): PNode =
   n.sons[length-1] = SemStmt(c, n.sons[length-1])
   Dec(c.p.nestedLoopCounter)
 
-proc newDeref(n: PNode): PNode {.inline.} =  
-  result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
-  addSon(result, n)
-
 proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
   result = newNodeI(nkCall, arg.info)
   result.add(newIdentNode(it.getIdent, arg.info))
@@ -489,36 +623,6 @@ proc semRaise(c: PContext, n: PNode): PNode =
     if typ.kind != tyRef or typ.sons[0].kind != tyObject: 
       localError(n.info, errExprCannotBeRaised)
 
-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()
-  for i in countup(1, sonsLen(n) - 1): 
-    var a = n.sons[i]
-    checkMinSonsLen(a, 1)
-    var length = sonsLen(a)
-    if a.kind == nkExceptBranch:
-      if length == 2 and a.sons[0].kind == nkBracket:
-        a.sons[0..0] = a.sons[0].sons
-        length = a.sonsLen
-
-      for j in countup(0, length - 2):
-        var typ = semTypeNode(c, a.sons[j], nil)
-        if typ.kind == tyRef: typ = typ.sons[0]
-        if typ.kind != tyObject:
-          LocalError(a.sons[j].info, errExprCannotBeRaised)
-        a.sons[j] = newNodeI(nkType, a.sons[j].info)
-        a.sons[j].typ = typ
-        if ContainsOrIncl(check, typ.id):
-          localError(a.sons[j].info, errExceptionAlreadyHandled)
-    elif a.kind != nkFinally: 
-      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: illFormedAst(n)
   for i in countup(0, sonsLen(n)-1):
@@ -746,22 +850,6 @@ proc activate(c: PContext, n: PNode) =
     else:
       nil
 
-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 maybeAddResult(c: PContext, s: PSym, n: PNode) =
   if s.typ.sons[0] != nil and
       (s.kind != skIterator or s.typ.callConv == ccClosure):
@@ -972,196 +1060,7 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
     result = newNodeI(nkDiscardStmt, n.info, 1)
     result.sons[0] = emptyNode
 
-# special marker values that indicates that we are
-# 1) AnalyzingDestructor: currently analyzing the type for destructor 
-# generation (needed for recursive types)
-# 2) DestructorIsTrivial: completed the analysis before and determined
-# that the type has a trivial destructor
-var AnalyzingDestructor, DestructorIsTrivial: PSym
-new(AnalyzingDestructor)
-new(DestructorIsTrivial)
-
-var
-  destructorName = getIdent"destroy_"
-  destructorParam = getIdent"this_"
-  destructorPragma = newIdentNode(getIdent"destructor", UnknownLineInfo())
-  rangeDestructorProc*: PSym
-
-proc destroyField(c: PContext, field: PSym, holder: PNode): PNode =
-  if instantiateDestructor(c, field.typ):
-    result = newNode(nkCall, field.info, @[
-      useSym(field.typ.destructor),
-      newNode(nkDotExpr, field.info, @[holder, useSym(field)])])
-
-proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
-  var nonTrivialFields = 0
-  result = newNode(nkCaseStmt, n.info, @[])
-  # case x.kind
-  result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]]))
-  for i in countup(1, n.len - 1):
-    # of A, B:
-    var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2])
-    let recList = n[i].lastSon
-    var destroyRecList = newNode(nkStmtList, n[i].info, @[])
-    template addField(f: expr): stmt =
-      let stmt = destroyField(c, f, holder)
-      if stmt != nil:
-        destroyRecList.addSon(stmt)
-        inc nonTrivialFields
-        
-    case recList.kind
-    of nkSym:
-      addField(recList.sym)
-    of nkRecList:
-      for j in countup(0, recList.len - 1):
-        addField(recList[j].sym)
-    else:
-      internalAssert false
-      
-    caseBranch.addSon(destroyRecList)
-    result.addSon(caseBranch)
-  # maybe no fields were destroyed?
-  if nonTrivialFields == 0:
-    result = nil
- 
-proc generateDestructor(c: PContext, t: PType): PNode =
-  ## generate a destructor for a user-defined object or tuple type
-  ## returns nil if the destructor turns out to be trivial
-  
-  template addLine(e: expr): stmt =
-    if result == nil: result = newNode(nkStmtList)
-    result.addSon(e)
-
-  # XXX: This may be true for some C-imported types such as
-  # Tposix_spawnattr
-  if t.n == nil or t.n.sons == nil: return
-  internalAssert t.n.kind == nkRecList
-  let destructedObj = newIdentNode(destructorParam, UnknownLineInfo())
-  # call the destructods of all fields
-  for s in countup(0, t.n.sons.len - 1):
-    case t.n.sons[s].kind
-    of nkRecCase:
-      let stmt = destroyCase(c, t.n.sons[s], destructedObj)
-      if stmt != nil: addLine(stmt)
-    of nkSym:
-      let stmt = destroyField(c, t.n.sons[s].sym, destructedObj)
-      if stmt != nil: addLine(stmt)
-    else:
-      internalAssert false
-
-  # base classes' destructors will be automatically called by
-  # semProcAux for both auto-generated and user-defined destructors
-
-proc instantiateDestructor*(c: PContext, typ: PType): bool =
-  # returns true if the type already had a user-defined
-  # destructor or if the compiler generated a default
-  # member-wise one
-  var t = skipTypes(typ, {tyConst, tyMutable})
-  
-  if t.destructor != nil:
-    # XXX: This is not entirely correct for recursive types, but we need
-    # it temporarily to hide the "destroy is already defined" problem
-    return t.destructor notin [AnalyzingDestructor, DestructorIsTrivial]
-  
-  case t.kind
-  of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
-    if instantiateDestructor(c, t.sons[0]):
-      if rangeDestructorProc == nil:
-        rangeDestructorProc = SymtabGet(c.tab, getIdent"nimDestroyRange")
-      t.destructor = rangeDestructorProc
-      return true
-    else:
-      return false
-  of tyTuple, tyObject:
-    t.destructor = AnalyzingDestructor
-    let generated = generateDestructor(c, t)
-    if generated != nil:
-      internalAssert t.sym != nil
-      var i = t.sym.info
-      let fullDef = newNode(nkProcDef, i, @[
-        newIdentNode(destructorName, i),
-        emptyNode,
-        emptyNode,
-        newNode(nkFormalParams, i, @[
-          emptyNode,
-          newNode(nkIdentDefs, i, @[
-            newIdentNode(destructorParam, i),
-            useSym(t.sym),
-            emptyNode]),
-          ]),
-        newNode(nkPragma, i, @[destructorPragma]),
-        emptyNode,
-        generated
-        ])
-      discard semProc(c, fullDef)
-      internalAssert t.destructor != nil
-      return true
-    else:
-      t.destructor = DestructorIsTrivial
-      return false
-  else:
-    return false
-
-proc insertDestructors(c: PContext,
-                       varSection: PNode): tuple[outer, inner: PNode] =
-  # Accepts a var or let section.
-  #
-  # When a var section has variables with destructors
-  # the var section is split up and finally blocks are inserted
-  # immediately after all "destructable" vars
-  #
-  # In case there were no destrucable variables, the proc returns
-  # (nil, nil) and the enclosing stmt-list requires no modifications.
-  #
-  # Otherwise, after the try blocks are created, the rest of the enclosing
-  # stmt-list should be inserted in the most `inner` such block (corresponding
-  # to the last variable).
-  #
-  # `outer` is a statement list that should replace the original var section.
-  # It will include the new truncated var section followed by the outermost
-  # try block.
-  let totalVars = varSection.sonsLen
-  for j in countup(0, totalVars - 1):
-    let
-      varId = varSection[j][0]
-      varTyp = varId.sym.typ
-      info = varId.info
-
-    if varTyp != nil and instantiateDestructor(c, varTyp) and 
-        sfGlobal notin varId.sym.flags:
-      var tryStmt = newNodeI(nkTryStmt, info)
-
-      if j < totalVars - 1:
-        var remainingVars = newNodeI(varSection.kind, info)
-        remainingVars.sons = varSection.sons[(j+1)..(-1)]
-        let (outer, inner) = insertDestructors(c, remainingVars)
-        if outer != nil:
-          tryStmt.addSon(outer)
-          result.inner = inner
-        else:
-          result.inner = newNodeI(nkStmtList, info)
-          result.inner.addSon(remainingVars)
-          tryStmt.addSon(result.inner)
-      else:
-        result.inner = newNodeI(nkStmtList, info)
-        tryStmt.addSon(result.inner)
-
-      tryStmt.addSon(
-        newNode(nkFinally, info, @[
-          semStmt(c, newNode(nkCall, info, @[
-            useSym(varTyp.destructor),
-            useSym(varId.sym)]))]))
-
-      result.outer = newNodeI(nkStmtList, info)
-      varSection.sons.setLen(j+1)
-      result.outer.addSon(varSection)
-      result.outer.addSon(tryStmt)
-
-      return
-
-proc ImplicitlyDiscardable(n: PNode): bool =
-  result = isCallExpr(n) and n.sons[0].kind == nkSym and 
-           sfDiscardable in n.sons[0].sym.flags
+var EnforceVoidContext = PType(kind: tyStmt)
 
 proc semStmtList(c: PContext, n: PNode): PNode =
   # these must be last statements in a block:
@@ -1169,6 +1068,11 @@ proc semStmtList(c: PContext, n: PNode): PNode =
     LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
   result = n
   var length = sonsLen(n)
+  var voidContext = false
+  var last = length-1
+  while last > 0 and n.sons[last].kind in {nkPragma, nkCommentStmt,
+                                           nkNilLit, nkEmpty}:
+    dec last
   for i in countup(0, length - 1):
     case n.sons[i].kind
     of nkFinally, nkExceptBranch:
@@ -1190,7 +1094,16 @@ proc semStmtList(c: PContext, n: PNode): PNode =
       n.sons.setLen(i+1)
       return
     else:
-      n.sons[i] = semStmt(c, n.sons[i])
+      n.sons[i] = semExpr(c, n.sons[i])
+      if n.sons[i].typ == EnforceVoidContext:
+        voidContext = true
+        n.typ = EnforceVoidContext
+      elif i != last or voidContext:
+        discardCheck(n.sons[i])
+      else:
+        n.typ = n.sons[i].typ
+        if not isEmptyType(n.typ):
+          n.kind = nkStmtListExpr
       case n.sons[i].kind
       of nkVarSection, nkLetSection:
         let (outer, inner) = insertDestructors(c, n.sons[i])
@@ -1206,15 +1119,17 @@ proc semStmtList(c: PContext, n: PNode): PNode =
           of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil
           else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
       else: nil
-  
-  # a statement list (s; e) has the type 'e':
-  if result.kind == nkStmtList and result.len > 0:
-    var lastStmt = lastSon(result)
-    if lastStmt.kind != nkNilLit and not ImplicitlyDiscardable(lastStmt):
-      result.typ = lastStmt.typ
-      #localError(lastStmt.info, errGenerated,
-      #  "Last expression must be explicitly returned if it " &
-      #  "is discardable or discarded")
+  if result.len == 1:
+    result = result.sons[0]
+  when false:
+    # a statement list (s; e) has the type 'e':
+    if result.kind == nkStmtList and result.len > 0:
+      var lastStmt = lastSon(result)
+      if lastStmt.kind != nkNilLit and not ImplicitlyDiscardable(lastStmt):
+        result.typ = lastStmt.typ
+        #localError(lastStmt.info, errGenerated,
+        #  "Last expression must be explicitly returned if it " &
+        #  "is discardable or discarded")
 
 proc SemStmt(c: PContext, n: PNode): PNode = 
   # now: simply an alias:
diff --git a/todo.txt b/todo.txt
index 8d9e8e5a8..4aa0527a3 100644
--- a/todo.txt
+++ b/todo.txt
@@ -8,11 +8,6 @@ version 0.9.2
 - CGEN: ``restrict`` pragma + backend support; computed goto support
 - document NimMain and check whether it works for threading
 - make use of commonType relation in expressions
-- further expr/stmt unification:
-  - merge nkStmtListExpr and nkStmtList
-  - merge nkBlockExpr and nkBlockStmt
-  - rewrite nkCaseExpr handling
-  - try except as an expression
 
 Bugs
 ====