summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md2
-rw-r--r--compiler/ast.nim8
-rw-r--r--compiler/ccgexprs.nim24
-rw-r--r--compiler/ccgstmts.nim16
-rw-r--r--compiler/ccgtypes.nim4
-rw-r--r--compiler/commands.nim4
-rw-r--r--compiler/destroyer.nim256
-rw-r--r--compiler/dfa.nim8
-rw-r--r--compiler/filter_tmpl.nim18
-rw-r--r--compiler/filters.nim18
-rw-r--r--compiler/lexer.nim30
-rw-r--r--compiler/llstream.nim1
-rw-r--r--compiler/lowerings.nim11
-rw-r--r--compiler/msgs.nim1
-rw-r--r--compiler/nimlexbase.nim2
-rw-r--r--compiler/nversion.nim2
-rw-r--r--compiler/options.nim1
-rw-r--r--compiler/renderer.nim68
-rw-r--r--compiler/rodread.nim7
-rw-r--r--compiler/rodwrite.nim8
-rw-r--r--compiler/semasgn.nim6
-rw-r--r--compiler/semdestruct.nim59
-rw-r--r--compiler/semexprs.nim18
-rw-r--r--compiler/sempass2.nim28
-rw-r--r--compiler/semstmts.nim61
-rw-r--r--compiler/semtypinst.nim16
-rw-r--r--compiler/transf.nim21
-rw-r--r--doc/manual/types.txt6
-rw-r--r--lib/nimbase.h7
-rw-r--r--lib/posix/posix_other.nim2
-rw-r--r--lib/pure/collections/lists.nim137
-rw-r--r--lib/pure/concurrency/threadpool.nim10
-rw-r--r--lib/pure/httpclient.nim22
-rw-r--r--lib/pure/json.nim35
-rw-r--r--lib/pure/math.nim2
-rw-r--r--lib/pure/strscans.nim32
-rw-r--r--lib/pure/unicode.nim2
-rw-r--r--lib/pure/uri.nim13
-rw-r--r--lib/system/alloc.nim15
-rw-r--r--lib/system/gc.nim17
-rw-r--r--lib/system/threads.nim2
-rw-r--r--tests/closure/t1641.nim20
-rw-r--r--tests/cpp/tget_subsystem.nim8
-rw-r--r--tests/destructor/tcustomstrings.nim96
-rw-r--r--tests/effects/tgcsafe2.nim12
-rw-r--r--tests/stdlib/tjsonmacro.nim4
-rw-r--r--tests/testament/categories.nim6
-rw-r--r--tools/nimpretty.nim5
-rw-r--r--web/bountysource.nim140
-rw-r--r--web/bountysource.nim.cfg1
-rw-r--r--web/website.ini2
51 files changed, 696 insertions, 598 deletions
diff --git a/changelog.md b/changelog.md
index 22d763f53..a4161f504 100644
--- a/changelog.md
+++ b/changelog.md
@@ -8,3 +8,5 @@
 - Arrays of char cannot be converted to ``cstring`` anymore, pointers to
   arrays of char can! This means ``$`` for arrays can finally exist
   in ``system.nim`` and do the right thing.
+- JSON: Deprecated `getBVal`, `getFNum`, and `getNum` in favour to 
+  `getBool`, `getFloat`, `getBiggestInt`. Also `getInt` procedure was added.
\ No newline at end of file
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 97ff4b593..6519b698a 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -871,7 +871,8 @@ type
                               # mean that there is no destructor.
                               # see instantiateDestructor in semdestruct.nim
     deepCopy*: PSym           # overriden 'deepCopy' operation
-    assignment*: PSym         # overriden '=' operator
+    assignment*: PSym         # overriden '=' operation
+    sink*: PSym               # overriden '=sink' operation
     methods*: seq[(int,PSym)] # attached methods
     size*: BiggestInt         # the size of the type in bytes
                               # -1 means that the size is unkwown
@@ -1047,6 +1048,8 @@ proc newNode*(kind: TNodeKind): PNode =
 
 proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode =
   result = newNode(kind)
+  if children.len > 0:
+    result.info = children[0].info
   result.sons = @children
 
 proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
@@ -1078,7 +1081,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
   result.info = info
   result.options = gOptions
   result.owner = owner
-  result.offset = - 1
+  result.offset = -1
   result.id = getID()
   when debugIds:
     registerId(result)
@@ -1290,6 +1293,7 @@ proc assignType*(dest, src: PType) =
   dest.align = src.align
   dest.destructor = src.destructor
   dest.deepCopy = src.deepCopy
+  dest.sink = src.sink
   dest.assignment = src.assignment
   dest.lockLevel = src.lockLevel
   # this fixes 'type TLock = TSysLock':
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 88944aea6..254248f9f 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1248,17 +1248,31 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
   if d.k == locNone:
     getTemp(p, n.typ, d)
   # generate call to newSeq before adding the elements per hand:
-  var L = int(lengthOrd(n.sons[1].typ))
-
+  let L = int(lengthOrd(n.sons[1].typ))
   genNewSeqAux(p, d, intLiteral(L))
   initLocExpr(p, n.sons[1], a)
-  for i in countup(0, L - 1):
+  # bug #5007; do not produce excessive C source code:
+  if L < 10:
+    for i in countup(0, L - 1):
+      initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
+      elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i))
+      elem.storage = OnHeap # we know that sequences are on the heap
+      initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage)
+      arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i))
+      genAssignment(p, elem, arr, {afDestIsNil, needToCopy})
+  else:
+    var i: TLoc
+    getTemp(p, getSysType(tyInt), i)
+    let oldCode = p.s(cpsStmts)
+    linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",  i.r, L.rope)
     initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
-    elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i))
+    elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), rdLoc(i))
     elem.storage = OnHeap # we know that sequences are on the heap
     initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage)
-    arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i))
+    arr.r = rfmt(nil, "$1[$2]", rdLoc(a), rdLoc(i))
     genAssignment(p, elem, arr, {afDestIsNil, needToCopy})
+    lineF(p, cpsStmts, "}$n", [])
+
 
 proc genNewFinalize(p: BProc, e: PNode) =
   var
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 74779f598..5434d87b2 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -141,9 +141,13 @@ template preserveBreakIdx(body: untyped): untyped =
   p.breakIdx = oldBreakIdx
 
 proc genState(p: BProc, n: PNode) =
-  internalAssert n.len == 1 and n.sons[0].kind == nkIntLit
-  let idx = n.sons[0].intVal
-  linefmt(p, cpsStmts, "STATE$1: ;$n", idx.rope)
+  internalAssert n.len == 1
+  let n0 = n[0]
+  if n0.kind == nkIntLit:
+    let idx = n.sons[0].intVal
+    linefmt(p, cpsStmts, "STATE$1: ;$n", idx.rope)
+  elif n0.kind == nkStrLit:
+    linefmt(p, cpsStmts, "$1: ;$n", n0.strVal.rope)
 
 proc genGotoState(p: BProc, n: PNode) =
   # we resist the temptation to translate it into duff's device as it later
@@ -157,10 +161,12 @@ proc genGotoState(p: BProc, n: PNode) =
   p.beforeRetNeeded = true
   lineF(p, cpsStmts, "case -1: goto BeforeRet_;$n", [])
   var statesCounter = lastOrd(n.sons[0].typ)
-  if n.len == 2 and n[1].kind == nkIntLit:
+  if n.len >= 2 and n[1].kind == nkIntLit:
     statesCounter = n[1].intVal
+  let prefix = if n.len == 3 and n[2].kind == nkStrLit: n[2].strVal.rope
+               else: rope"STATE"
   for i in 0 .. statesCounter:
-    lineF(p, cpsStmts, "case $1: goto STATE$1;$n", [rope(i)])
+    lineF(p, cpsStmts, "case $2: goto $1$2;$n", [prefix, rope(i)])
   lineF(p, cpsStmts, "}$n", [])
 
 proc genBreakState(p: BProc, n: PNode) =
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index c9705e784..4c85294b2 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -278,7 +278,7 @@ proc ccgIntroducedPtr(s: PSym): bool =
   elif tfByCopy in pt.flags: return false
   case pt.kind
   of tyObject:
-    if (optByRef in s.options) or (getSize(pt) > platform.floatSize * 2):
+    if (optByRef in s.options) or (getSize(pt) > platform.floatSize * 3):
       result = true           # requested anyway
     elif (tfFinal in pt.flags) and (pt.sons[0] == nil):
       result = false          # no need, because no subtyping possible
@@ -286,7 +286,7 @@ proc ccgIntroducedPtr(s: PSym): bool =
       result = true           # ordinary objects are always passed by reference,
                               # otherwise casting doesn't work
   of tyTuple:
-    result = (getSize(pt) > platform.floatSize*2) or (optByRef in s.options)
+    result = (getSize(pt) > platform.floatSize*3) or (optByRef in s.options)
   else: result = false
 
 proc fillResult(param: PNode) =
diff --git a/compiler/commands.nim b/compiler/commands.nim
index ce50b1a79..bae1fda38 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -666,6 +666,10 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     expectArg(switch, arg, pass, info)
     if config != nil:
       config.cppDefine(arg)
+  of "newruntime":
+    expectNoArg(switch, arg, pass, info)
+    newDestructors = true
+    defineSymbol("nimNewRuntime")
   else:
     if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg)
     else: invalidCmdLineOption(pass, switch, info)
diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim
index e7ff00bb9..afa2e5e50 100644
--- a/compiler/destroyer.nim
+++ b/compiler/destroyer.nim
@@ -76,7 +76,10 @@
 ## inefficiencies. A better strategy is to collect all the temporaries
 ## in a single object that we put into a single try-finally that
 ## surrounds the proc body. This means the code stays quite efficient
-## when compiled to C.
+## when compiled to C. In fact, we do the same for variables, so
+## destructors are called when the proc returns, not at scope exit!
+## This makes certains idioms easier to support. (Taking the slice
+## of a temporary object.)
 ##
 ## foo(bar(X(), Y()))
 ## X and Y get destroyed after bar completes:
@@ -94,103 +97,17 @@ import
 
 template hasDestructor(t: PType): bool = tfHasAsgn in t.flags
 
-when false:
-  type
-    VarInfo = object
-      hasInitValue: bool
-      addrTaken: bool
-      assigned: int      # we don't care about the 'var' vs 'let'
-                        # distinction; it's an optimization pass
-      read: int
-      scope: int         # the scope the variable is declared in
-
-    Con = object
-      t: Table[int, VarInfo]
-      owner: PSym
-      scope: int
-
-  const
-    InterestingSyms = {skVar, skResult}
-
-  proc collectData(c: var Con; n: PNode)
-
-  proc collectDef(c: var Con; n: PNode; hasInitValue: bool) =
-    if n.kind == nkSym:
-      c.t[n.sym.id] = VarInfo(hasInitValue: hasInitValue,
-                              addrTaken: false, assigned: 0, read: 0,
-                              scope: scope)
-
-  proc collectVarSection(c: var Con; n: PNode) =
-    for a in n:
-      if a.kind == nkCommentStmt: continue
-      if a.kind == nkVarTuple:
-        collectData(c, a.lastSon)
-        for i in 0 .. a.len-3: collectDef(c, a[i], a.lastSon != nil)
-      else:
-        collectData(c, a.lastSon)
-        if a.lastSon.kind != nkEmpty:
-          collectDef(c, a.sons[0], a.lastSon != nil)
-
-  proc collectData(c: var Con; n: PNode) =
-    case n.kind
-    of nkAsgn, nkFastAsgn:
-      if n[0].kind == nkSym and (let s = n[0].sym; s.owner == c.owner and
-                                s.kind in InterestingSyms):
-        inc c.t[s.id].assigned
-      collectData(c, n[1])
-    of nkSym:
-      if (let s = n[0].sym; s.owner == c.owner and
-          s.kind in InterestingSyms):
-        inc c.t[s.id].read
-    of nkAddr, nkHiddenAddr:
-      var n = n[0]
-      while n.kind == nkBracketExpr: n = n[0]
-      if (let s = n[0].sym; s.owner == c.owner and
-          s.kind in InterestingSyms):
-        c.t[s.id].addrTaken = true
-
-    of nkCallKinds:
-      if n.sons[0].kind == nkSym:
-        let s = n.sons[0].sym
-        if s.magic != mNone:
-          genMagic(c, n, s.magic)
-        else:
-          genCall(c, n)
-      else:
-        genCall(c, n)
-    of nkCharLit..nkNilLit, nkIdent: discard
-    of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr,
-        nkDerefExpr, nkHiddenDeref:
-      collectData(c, n[0])
-    of nkIfStmt, nkIfExpr: genIf(c, n)
-    of nkWhenStmt:
-      # This is "when nimvm" node. Chose the first branch.
-      collectData(c, n.sons[0].sons[1])
-    of nkCaseStmt: genCase(c, n)
-    of nkWhileStmt: genWhile(c, n)
-    of nkBlockExpr, nkBlockStmt: genBlock(c, n)
-    of nkReturnStmt: genReturn(c, n)
-    of nkRaiseStmt: genRaise(c, n)
-    of nkBreakStmt: genBreak(c, n)
-    of nkTryStmt: genTry(c, n)
-    of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
-        nkBracket, nkCurly, nkPar, nkClosure, nkObjConstr:
-      for x in n: collectData(c, x)
-    of nkPragmaBlock: collectData(c, n.lastSon)
-    of nkDiscardStmt: collectData(c, n.sons[0])
-    of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkExprColonExpr, nkExprEqExpr,
-        nkCast:
-      collectData(c, n.sons[1])
-    of nkObjDownConv, nkStringToCString, nkCStringToString:
-      collectData(c, n.sons[0])
-    of nkVarSection, nkLetSection: collectVarSection(c, n)
-    else: discard
+const
+  InterestingSyms = {skVar, skResult, skLet}
 
 type
   Con = object
     owner: PSym
     g: ControlFlowGraph
-    tmps: PType
+    jumpTargets: IntSet
+    tmpObj: PType
+    tmp: PSym
+    destroys, topLevelVars: PNode
 
 proc isHarmlessVar*(s: PSym; c: Con): bool =
   # 's' is harmless if it used only once and its
@@ -224,29 +141,156 @@ proc isHarmlessVar*(s: PSym; c: Con): bool =
   # L3
   #
   # So this analysis is for now overly conservative, but correct.
-  discard
+  var defsite = -1
+  var usages = 0
+  for i in 0..<c.g.len:
+    case c.g[i].kind
+    of def:
+      if c.g[i].sym == s:
+        if defsite < 0: defsite = i
+        else: return false
+    of use:
+      if c.g[i].sym == s:
+        if defsite < 0: return false
+        for j in defsite .. i:
+          # not within the same basic block?
+          if j in c.jumpTargets: return false
+        # if we want to die after the first 'use':
+        if usages > 1: return false
+        inc usages
+    of useWithinCall:
+      if c.g[i].sym == s: return false
+    of goto, fork:
+      discard "we do not perform an abstract interpretation yet"
 
 template interestingSym(s: PSym): bool =
-  s.owner == owner and s.kind in InterestingSyms and hasDestructor(s.typ)
+  s.owner == c.owner and s.kind in InterestingSyms and hasDestructor(s.typ)
+
+proc genSink(t: PType; dest: PNode): PNode =
+  let op = if t.sink != nil: t.sink else: t.assignment
+  assert op != nil
+  result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest))
+
+proc genCopy(t: PType; dest: PNode): PNode =
+  assert t.assignment != nil
+  result = newTree(nkCall, newSymNode(t.assignment), newTree(nkHiddenAddr, dest))
+
+proc genDestroy(t: PType; dest: PNode): PNode =
+  assert t.destructor != nil
+  result = newTree(nkCall, newSymNode(t.destructor), newTree(nkHiddenAddr, dest))
 
-proc p(n, parent: PNode; c: var Con) =
+proc addTopVar(c: var Con; v: PNode) =
+  c.topLevelVars.add newTree(nkIdentDefs, v, emptyNode, emptyNode)
+
+proc p(n: PNode; c: var Con): PNode
+
+template recurse(n, dest) =
+  for i in 0..<n.len:
+    dest.add p(n[i], c)
+
+proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
+  if ri.kind in nkCallKinds:
+    result = genSink(ri.typ, dest)
+    # watch out and no not transform 'ri' twice if it's a call:
+    let ri2 = copyNode(ri)
+    recurse(ri, ri2)
+    result.add ri2
+  elif ri.kind == nkSym and isHarmlessVar(ri.sym, c):
+    result = genSink(ri.typ, dest)
+    result.add p(ri, c)
+  else:
+    result = genCopy(ri.typ, dest)
+    result.add p(ri, c)
+
+proc p(n: PNode; c: var Con): PNode =
   case n.kind
   of nkVarSection, nkLetSection:
     discard "transform; var x = y to  var x; x op y  where op is a move or copy"
+    result = newNodeI(nkStmtList, n.info)
+
+    for i in 0..<n.len:
+      let it = n[i]
+      let L = it.len-1
+      let ri = it[L]
+      if it.kind == nkVarTuple and hasDestructor(ri.typ):
+        let x = lowerTupleUnpacking(it, c.owner)
+        result.add p(x, c)
+      elif it.kind == nkIdentDefs and hasDestructor(it[0].typ):
+        for j in 0..L-2:
+          let v = it[j]
+          doAssert v.kind == nkSym
+          # move the variable declaration to the top of the frame:
+          c.addTopVar v
+          # make sure it's destroyed at the end of the proc:
+          c.destroys.add genDestroy(v.typ, v)
+          if ri.kind != nkEmpty:
+            let r = moveOrCopy(v, ri, c)
+            result.add r
+      else:
+        # keep it, but transform 'ri':
+        var varSection = copyNode(n)
+        var itCopy = copyNode(it)
+        for j in 0..L-1:
+          itCopy.add it[j]
+        itCopy.add p(ri, c)
+        varSection.add itCopy
+        result.add varSection
   of nkCallKinds:
     if n.typ != nil and hasDestructor(n.typ):
       discard "produce temp creation"
+      result = newNodeIT(nkStmtListExpr, n.info, n.typ)
+      let f = newSym(skField, getIdent(":d" & $c.tmpObj.n.len), c.owner, n.info)
+      f.typ = n.typ
+      rawAddField c.tmpObj, f
+      var m = genSink(n.typ, rawDirectAccess(c.tmp, f))
+      var call = copyNode(n)
+      recurse(n, call)
+      m.add call
+      result.add m
+      result.add rawDirectAccess(c.tmp, f)
+      c.destroys.add genDestroy(n.typ, rawDirectAccess(c.tmp, f))
+    else:
+      result = copyNode(n)
+      recurse(n, result)
   of nkAsgn, nkFastAsgn:
     if n[0].kind == nkSym and interestingSym(n[0].sym):
-      discard "use move or assignment"
+      result = moveOrCopy(n[0], n[1], c)
+    else:
+      result = copyNode(n)
+      recurse(n, result)
+  of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
+      nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
+    result = n
   else:
-    for i in 0..<n.len:
-      p(n[i], n, c)
-
-proc injectDestructorCalls*(owner: PSym; n: PNode;
-                            disableExceptions = false): PNode =
-  when false:
-    var c = Con(t: initTable[int, VarInfo](), owner: owner)
-    collectData(c, n)
-  var allTemps = createObj(owner, n.info)
+    result = copyNode(n)
+    recurse(n, result)
+
+proc injectDestructorCalls*(owner: PSym; n: PNode): PNode =
+  var c: Con
+  c.owner = owner
+  c.tmp = newSym(skTemp, getIdent":d", owner, n.info)
+  c.tmpObj = createObj(owner, n.info)
+  c.tmp.typ = c.tmpObj
+  c.destroys = newNodeI(nkStmtList, n.info)
+  c.topLevelVars = newNodeI(nkVarSection, n.info)
   let cfg = constructCfg(owner, n)
+  shallowCopy(c.g, cfg)
+  c.jumpTargets = initIntSet()
+  for i in 0..<c.g.len:
+    if c.g[i].kind in {goto, fork}:
+      c.jumpTargets.incl(i+c.g[i].dest)
+  let body = p(n, c)
+  if c.tmp.typ.n.len > 0:
+    c.addTopVar(newSymNode c.tmp)
+  result = newNodeI(nkStmtList, n.info)
+  if c.topLevelVars.len > 0:
+    result.add c.topLevelVars
+  if c.destroys.len > 0:
+    result.add newTryFinally(body, c.destroys)
+  else:
+    result.add body
+
+  when defined(nimDebugDestroys):
+    echo "------------------------------------"
+    echo owner.name.s, " transformed to: "
+    echo result
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index dd9dd4c79..ca1849a3c 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -235,14 +235,14 @@ proc genTry(c: var Con; n: PNode) =
 
 proc genRaise(c: var Con; n: PNode) =
   gen(c, n.sons[0])
-  c.code.add Instr(n: n, kind: goto, dest: high(int))
+  c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
 
 proc genReturn(c: var Con; n: PNode) =
   if n.sons[0].kind != nkEmpty: gen(c, n.sons[0])
-  c.code.add Instr(n: n, kind: goto, dest: high(int))
+  c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
 
 const
-  InterestingSyms = {skVar, skResult}
+  InterestingSyms = {skVar, skResult, skLet}
 
 proc genUse(c: var Con; n: PNode) =
   var n = n
@@ -279,7 +279,7 @@ proc genMagic(c: var Con; n: PNode; m: TMagic) =
     for i in 2..<n.len: gen(c, n[i])
   of mExit:
     genCall(c, n)
-    c.code.add Instr(n: n, kind: goto, dest: high(int))
+    c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
   else:
     genCall(c, n)
 
diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim
index 361b3d276..ca9a3a801 100644
--- a/compiler/filter_tmpl.nim
+++ b/compiler/filter_tmpl.nim
@@ -13,14 +13,10 @@ import
   llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
   renderer, filters
 
-proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream
-  # #! template(subsChar='$', metaChar='#') | standard(version="0.7.2")
-# implementation
-
 type
   TParseState = enum
     psDirective, psTempl
-  TTmplParser{.final.} = object
+  TTmplParser = object
     inp: PLLStream
     state: TParseState
     info: TLineInfo
@@ -61,6 +57,10 @@ proc scanPar(p: var TTmplParser, d: int) =
 proc withInExpr(p: TTmplParser): bool {.inline.} =
   result = p.par > 0 or p.bracket > 0 or p.curly > 0
 
+const
+  LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '^',
+                          '|', '%', '&', '$', '@', '~', ','}
+
 proc parseLine(p: var TTmplParser) =
   var j = 0
   while p.x[j] == ' ': inc(j)
@@ -77,7 +77,7 @@ proc parseLine(p: var TTmplParser) =
       inc(j)
 
     scanPar(p, j)
-    p.pendingExprLine = withInExpr(p) or llstream.endsWithOpr(p.x)
+    p.pendingExprLine = withInExpr(p) or p.x.endsWith(LineContinuationOprs)
     case keyw
     of "end":
       if p.indent >= 2:
@@ -88,14 +88,14 @@ proc parseLine(p: var TTmplParser) =
       llStreamWrite(p.outp, spaces(p.indent))
       llStreamWrite(p.outp, "#end")
     of "if", "when", "try", "while", "for", "block", "case", "proc", "iterator",
-       "converter", "macro", "template", "method":
+       "converter", "macro", "template", "method", "func":
       llStreamWrite(p.outp, spaces(p.indent))
       llStreamWrite(p.outp, substr(p.x, d))
       inc(p.indent, 2)
     of "elif", "of", "else", "except", "finally":
       llStreamWrite(p.outp, spaces(p.indent - 2))
       llStreamWrite(p.outp, substr(p.x, d))
-    of "wLet", "wVar", "wConst", "wType":
+    of "let", "var", "const", "type":
       llStreamWrite(p.outp, spaces(p.indent))
       llStreamWrite(p.outp, substr(p.x, d))
       if not p.x.contains({':', '='}):
@@ -199,7 +199,7 @@ proc parseLine(p: var TTmplParser) =
           inc(j)
     llStreamWrite(p.outp, "\\n\"")
 
-proc filterTmpl(stdin: PLLStream, filename: string, call: PNode): PLLStream =
+proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
   var p: TTmplParser
   p.info = newLineInfo(filename, 0, 0)
   p.outp = llStreamOpen("")
diff --git a/compiler/filters.nim b/compiler/filters.nim
index d1a6409ff..37df628ed 100644
--- a/compiler/filters.nim
+++ b/compiler/filters.nim
@@ -13,14 +13,6 @@ import
   llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
   renderer
 
-proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream
-proc filterStrip*(stdin: PLLStream, filename: string, call: PNode): PLLStream
-  # helpers to retrieve arguments:
-proc charArg*(n: PNode, name: string, pos: int, default: char): char
-proc strArg*(n: PNode, name: string, pos: int, default: string): string
-proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool
-# implementation
-
 proc invalidPragma(n: PNode) =
   localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments}))
 
@@ -35,26 +27,26 @@ proc getArg(n: PNode, name: string, pos: int): PNode =
     elif i == pos:
       return n.sons[i]
 
-proc charArg(n: PNode, name: string, pos: int, default: char): char =
+proc charArg*(n: PNode, name: string, pos: int, default: char): char =
   var x = getArg(n, name, pos)
   if x == nil: result = default
   elif x.kind == nkCharLit: result = chr(int(x.intVal))
   else: invalidPragma(n)
 
-proc strArg(n: PNode, name: string, pos: int, default: string): string =
+proc strArg*(n: PNode, name: string, pos: int, default: string): string =
   var x = getArg(n, name, pos)
   if x == nil: result = default
   elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
   else: invalidPragma(n)
 
-proc boolArg(n: PNode, name: string, pos: int, default: bool): bool =
+proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool =
   var x = getArg(n, name, pos)
   if x == nil: result = default
   elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "true") == 0: result = true
   elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false
   else: invalidPragma(n)
 
-proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream =
+proc filterStrip*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
   var pattern = strArg(call, "startswith", 1, "")
   var leading = boolArg(call, "leading", 2, true)
   var trailing = boolArg(call, "trailing", 3, true)
@@ -68,7 +60,7 @@ proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream =
       llStreamWriteln(result, line)
   llStreamClose(stdin)
 
-proc filterReplace(stdin: PLLStream, filename: string, call: PNode): PLLStream =
+proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
   var sub = strArg(call, "sub", 1, "")
   if len(sub) == 0: invalidPragma(call)
   var by = strArg(call, "by", 2, "")
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index 68b0164d4..895848e77 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -129,6 +129,7 @@ type
     when defined(nimpretty):
       offsetA*, offsetB*: int   # used for pretty printing so that literals
                                 # like 0b01 or  r"\L" are unaffected
+      commentOffsetA*, commentOffsetB*: int
 
   TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string)
   TLexer* = object of TBaseLexer
@@ -144,6 +145,10 @@ type
     when defined(nimsuggest):
       previousToken: TLineInfo
 
+when defined(nimpretty):
+  var
+    gIndentationWidth*: int
+
 var gLinesCompiled*: int  # all lines that have been compiled
 
 proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} =
@@ -151,6 +156,8 @@ proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} =
   when defined(nimpretty):
     result.offsetA = tok.offsetA
     result.offsetB = tok.offsetB
+    result.commentOffsetA = tok.commentOffsetA
+    result.commentOffsetB = tok.commentOffsetB
 
 proc isKeyword*(kind: TTokType): bool =
   result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh)
@@ -198,6 +205,9 @@ proc initToken*(L: var TToken) =
   L.fNumber = 0.0
   L.base = base10
   L.ident = nil
+  when defined(nimpretty):
+    L.commentOffsetA = 0
+    L.commentOffsetB = 0
 
 proc fillToken(L: var TToken) =
   L.tokType = tkInvalid
@@ -208,6 +218,9 @@ proc fillToken(L: var TToken) =
   L.fNumber = 0.0
   L.base = base10
   L.ident = nil
+  when defined(nimpretty):
+    L.commentOffsetA = 0
+    L.commentOffsetB = 0
 
 proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream;
                  cache: IdentCache) =
@@ -996,18 +1009,27 @@ proc skip(L: var TLexer, tok: var TToken) =
     of '#':
       # do not skip documentation comment:
       if buf[pos+1] == '#': break
+      when defined(nimpretty):
+        tok.commentOffsetA = L.offsetBase + pos
       if buf[pos+1] == '[':
         skipMultiLineComment(L, tok, pos+2, false)
         pos = L.bufpos
         buf = L.buf
+        when defined(nimpretty):
+          tok.commentOffsetB = L.offsetBase + pos
       else:
         tokenBegin(tok, pos)
         while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
         tokenEndIgnore(tok, pos+1)
+        when defined(nimpretty):
+          tok.commentOffsetB = L.offsetBase + pos + 1
     else:
       break                   # EndOfFile also leaves the loop
   tokenEndPrevious(tok, pos-1)
   L.bufpos = pos
+  when defined(nimpretty):
+    if gIndentationWidth <= 0:
+      gIndentationWidth = tok.indent
 
 proc rawGetTok*(L: var TLexer, tok: var TToken) =
   template atTokenEnd() {.dirty.} =
@@ -1030,7 +1052,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
   var c = L.buf[L.bufpos]
   tok.line = L.lineNumber
   tok.col = getColNumber(L, L.bufpos)
-  if c in SymStartChars - {'r', 'R', 'l'}:
+  if c in SymStartChars - {'r', 'R'}:
     getSymbol(L, tok)
   else:
     case c
@@ -1047,12 +1069,6 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
     of ',':
       tok.tokType = tkComma
       inc(L.bufpos)
-    of 'l':
-      # if we parsed exactly one character and its a small L (l), this
-      # is treated as a warning because it may be confused with the number 1
-      if L.buf[L.bufpos+1] notin (SymChars + {'_'}):
-        lexMessage(L, warnSmallLshouldNotBeUsed)
-      getSymbol(L, tok)
     of 'r', 'R':
       if L.buf[L.bufpos + 1] == '\"':
         inc(L.bufpos)
diff --git a/compiler/llstream.nim b/compiler/llstream.nim
index 0a1e09fc8..42bbb7600 100644
--- a/compiler/llstream.nim
+++ b/compiler/llstream.nim
@@ -84,7 +84,6 @@ const
   AdditionalLineContinuationOprs = {'#', ':', '='}
 
 proc endsWithOpr*(x: string): bool =
-  # also used by the standard template filter:
   result = x.endsWith(LineContinuationOprs)
 
 proc continueLine(line: string, inTripleString: bool): bool {.inline.} =
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 033472c07..f895e887c 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -70,6 +70,9 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
   lit.intVal = i
   addSon(result, lit)
 
+proc newTryFinally*(body, final: PNode): PNode =
+  result = newTree(nkTryStmt, body, newTree(nkFinally, final))
+
 proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
   let value = n.lastSon
   result = newNodeI(nkStmtList, n.info)
@@ -139,6 +142,14 @@ proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
   addSon(result, newSymNode(field))
   result.typ = field.typ
 
+proc rawDirectAccess*(obj, field: PSym): PNode =
+  # returns a.field as a node
+  assert field.kind == skField
+  result = newNodeI(nkDotExpr, field.info)
+  addSon(result, newSymNode obj)
+  addSon(result, newSymNode field)
+  result.typ = field.typ
+
 proc lookupInRecord(n: PNode, id: int): PSym =
   result = nil
   case n.kind
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index c988141e5..8d43103db 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -500,6 +500,7 @@ type
     fileIndex*: int32
     when defined(nimpretty):
       offsetA*, offsetB*: int
+      commentOffsetA*, commentOffsetB*: int
 
   TErrorOutput* = enum
     eStdOut
diff --git a/compiler/nimlexbase.nim b/compiler/nimlexbase.nim
index c395a3709..2e7416645 100644
--- a/compiler/nimlexbase.nim
+++ b/compiler/nimlexbase.nim
@@ -123,7 +123,7 @@ proc fillBaseLexer(L: var TBaseLexer, pos: int): int =
     result = pos + 1          # nothing to do
   else:
     fillBuffer(L)
-    L.offsetBase += pos
+    L.offsetBase += pos + 1
     L.bufpos = 0
     result = 0
   L.lineStart = result
diff --git a/compiler/nversion.nim b/compiler/nversion.nim
index 4d4fe6c95..85265a7c0 100644
--- a/compiler/nversion.nim
+++ b/compiler/nversion.nim
@@ -13,5 +13,5 @@
 const
   MaxSetElements* = 1 shl 16  # (2^16) to support unicode character sets?
   VersionAsString* = system.NimVersion
-  RodFileVersion* = "1222"       # modify this if the rod-format changes!
+  RodFileVersion* = "1223"       # modify this if the rod-format changes!
 
diff --git a/compiler/options.nim b/compiler/options.nim
index 4888cc6ba..86e6006d7 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -144,6 +144,7 @@ var
   isServing*: bool = false
   gNoNimblePath* = false
   gExperimentalMode*: bool
+  newDestructors*: bool
 
 proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
 proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 4fbac45ab..f3b4527df 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -37,6 +37,7 @@ type
     inGenericParams: bool
     checkAnon: bool        # we're in a context that can contain sfAnon
     inPragma: int
+    pendingNewlineCount: int
     when defined(nimpretty):
       origContent: string
 
@@ -62,9 +63,31 @@ proc renderDefinitionName*(s: PSym, noQuotes = false): string =
   else:
     result = '`' & x & '`'
 
+when not defined(nimpretty):
+  const
+    IndentWidth = 2
+    longIndentWid = IndentWidth * 2
+else:
+  template IndentWidth: untyped = lexer.gIndentationWidth
+  template longIndentWid: untyped = IndentWidth() * 2
+
+proc minmaxLine(n: PNode): (int, int) =
+  case n.kind
+  of nkTripleStrLit:
+    result = (n.info.line.int, n.info.line.int + countLines(n.strVal))
+  of nkCommentStmt:
+    result = (n.info.line.int, n.info.line.int + countLines(n.comment))
+  else:
+    result = (n.info.line.int, n.info.line.int)
+  for i in 0 ..< safeLen(n):
+    let (currMin, currMax) = minmaxLine(n[i])
+    if currMin < result[0]: result[0] = currMin
+    if currMax > result[1]: result[1] = currMax
+
+proc lineDiff(a, b: PNode): int =
+  result = minmaxLine(b)[0] - minmaxLine(a)[1]
+
 const
-  IndentWidth = 2
-  longIndentWid = 4
   MaxLineLen = 80
   LineCommentColumn = 30
 
@@ -90,7 +113,8 @@ proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
 
 proc addPendingNL(g: var TSrcGen) =
   if g.pendingNL >= 0:
-    addTok(g, tkSpaces, "\n" & spaces(g.pendingNL))
+    let newlines = repeat("\n", clamp(g.pendingNewlineCount, 1, 3))
+    addTok(g, tkSpaces, newlines & spaces(g.pendingNL))
     g.lineLen = g.pendingNL
     g.pendingNL = - 1
     g.pendingWhitespace = -1
@@ -114,11 +138,17 @@ proc putNL(g: var TSrcGen) =
 
 proc optNL(g: var TSrcGen, indent: int) =
   g.pendingNL = indent
-  g.lineLen = indent          # BUGFIX
+  g.lineLen = indent
+  g.pendingNewlineCount = 0
 
 proc optNL(g: var TSrcGen) =
   optNL(g, g.indent)
 
+proc optNL(g: var TSrcGen; a, b: PNode) =
+  g.pendingNL = g.indent
+  g.lineLen = g.indent
+  g.pendingNewlineCount = lineDiff(a, b)
+
 proc indentNL(g: var TSrcGen) =
   inc(g.indent, IndentWidth)
   g.pendingNL = g.indent
@@ -306,10 +336,14 @@ proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
 
 proc atom(g: TSrcGen; n: PNode): string =
   when defined(nimpretty):
+    let comment = if n.info.commentOffsetA < n.info.commentOffsetB:
+                    " " & substr(g.origContent, n.info.commentOffsetA, n.info.commentOffsetB)
+                  else:
+                    ""
     if n.info.offsetA <= n.info.offsetB:
       # for some constructed tokens this can not be the case and we're better
       # off to not mess with the offset then.
-      return substr(g.origContent, n.info.offsetA, n.info.offsetB)
+      return substr(g.origContent, n.info.offsetA, n.info.offsetB) & comment
   var f: float32
   case n.kind
   of nkEmpty: result = ""
@@ -577,12 +611,16 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
   if n.kind == nkEmpty: return
   if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
     if doIndent: indentNL(g)
-    for i in countup(0, sonsLen(n) - 1):
-      optNL(g)
-      if n.sons[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
-        gstmts(g, n.sons[i], c, doIndent=false)
+    let L = n.len
+    for i in 0 .. L-1:
+      if i > 0:
+        optNL(g, n[i-1], n[i])
       else:
-        gsub(g, n.sons[i])
+        optNL(g)
+      if n[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
+        gstmts(g, n[i], c, doIndent=false)
+      else:
+        gsub(g, n[i])
       gcoms(g)
     if doIndent: dedent(g)
   else:
@@ -1384,7 +1422,7 @@ proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string =
 
 proc `$`*(n: PNode): string = n.renderTree
 
-proc renderModule*(n: PNode, filename: string,
+proc renderModule*(n: PNode, infile, outfile: string,
                    renderFlags: TRenderFlags = {}) =
   var
     f: File
@@ -1392,9 +1430,9 @@ proc renderModule*(n: PNode, filename: string,
   initSrcGen(g, renderFlags)
   when defined(nimpretty):
     try:
-      g.origContent = readFile(filename)
+      g.origContent = readFile(infile)
     except IOError:
-      rawMessage(errCannotOpenFile, filename)
+      rawMessage(errCannotOpenFile, infile)
 
   for i in countup(0, sonsLen(n) - 1):
     gsub(g, n.sons[i])
@@ -1406,11 +1444,11 @@ proc renderModule*(n: PNode, filename: string,
   gcoms(g)
   if optStdout in gGlobalOptions:
     write(stdout, g.buf)
-  elif open(f, filename, fmWrite):
+  elif open(f, outfile, fmWrite):
     write(f, g.buf)
     close(f)
   else:
-    rawMessage(errCannotOpenFile, filename)
+    rawMessage(errCannotOpenFile, outfile)
 
 proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) =
   initSrcGen(r, renderFlags)
diff --git a/compiler/rodread.nim b/compiler/rodread.nim
index 31b54d760..2546aa77a 100644
--- a/compiler/rodread.nim
+++ b/compiler/rodread.nim
@@ -336,10 +336,13 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
   if r.s[r.pos] == '\17':
     inc(r.pos)
     result.assignment = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-  while r.s[r.pos] == '\18':
+  if r.s[r.pos] == '\18':
+    inc(r.pos)
+    result.sink = rrGetSym(r, decodeVInt(r.s, r.pos), info)
+  while r.s[r.pos] == '\19':
     inc(r.pos)
     let x = decodeVInt(r.s, r.pos)
-    doAssert r.s[r.pos] == '\19'
+    doAssert r.s[r.pos] == '\20'
     inc(r.pos)
     let y = rrGetSym(r, decodeVInt(r.s, r.pos), info)
     result.methods.safeAdd((x, y))
diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim
index fb50c6473..1bc136acf 100644
--- a/compiler/rodwrite.nim
+++ b/compiler/rodwrite.nim
@@ -245,10 +245,14 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
     add(result, '\17')
     encodeVInt(t.assignment.id, result)
     pushSym(w, t.assignment)
-  for i, s in items(t.methods):
+  if t.sink != nil:
     add(result, '\18')
-    encodeVInt(i, result)
+    encodeVInt(t.sink.id, result)
+    pushSym(w, t.sink)
+  for i, s in items(t.methods):
     add(result, '\19')
+    encodeVInt(i, result)
+    add(result, '\20')
     encodeVInt(s.id, result)
     pushSym(w, s)
   encodeLoc(w, t.loc, result)
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
index dbb2a140b..caed11341 100644
--- a/compiler/semasgn.nim
+++ b/compiler/semasgn.nim
@@ -92,13 +92,13 @@ proc newAsgnStmt(le, ri: PNode): PNode =
   result.sons[0] = le
   result.sons[1] = ri
 
-proc newDestructorCall(op: PSym; x: PNode): PNode =
+proc newOpCall(op: PSym; x: PNode): PNode =
   result = newNodeIT(nkCall, x.info, op.typ.sons[0])
   result.add(newSymNode(op))
   result.add x
 
 proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
-  result = newAsgnStmt(x, newDestructorCall(op, y))
+  result = newAsgnStmt(x, newOpCall(op, y))
 
 proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
   case c.kind
@@ -107,7 +107,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
     if op != nil:
       markUsed(c.info, op, c.c.graph.usageSym)
       styleCheckUse(c.info, op)
-      body.add newDestructorCall(op, x)
+      body.add newOpCall(op, x)
       result = true
   of attachedAsgn:
     if tfHasAsgn in t.flags:
diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim
index b09404b39..51109ec37 100644
--- a/compiler/semdestruct.nim
+++ b/compiler/semdestruct.nim
@@ -184,62 +184,3 @@ proc createDestructorCall(c: PContext, s: PSym): PNode =
       useSym(destructableT.destructor, c.graph.usageSym),
       useSym(s, c.graph.usageSym)]))
     result = newNode(nkDefer, s.info, @[call])
-
-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 or sfGlobal in varId.sym.flags: continue
-    let destructableT = instantiateDestructor(c, varTyp)
-
-    if destructableT != nil:
-      var tryStmt = newNodeI(nkTryStmt, info)
-
-      if j < totalVars - 1:
-        var remainingVars = newNodeI(varSection.kind, info)
-        remainingVars.sons = varSection.sons[(j+1)..varSection.len-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(destructableT.destructor, c.graph.usageSym),
-            useSym(varId.sym, c.graph.usageSym)]))]))
-
-      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 c336afc89..180754168 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1296,10 +1296,13 @@ proc takeImplicitAddr(c: PContext, n: PNode): PNode =
 proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
   if le.kind == nkHiddenDeref:
     var x = le.sons[0]
-    if x.typ.kind == tyVar and x.kind == nkSym and x.sym.kind == skResult:
-      n.sons[0] = x # 'result[]' --> 'result'
-      n.sons[1] = takeImplicitAddr(c, ri)
-      x.typ.flags.incl tfVarIsPtr
+    if x.typ.kind == tyVar and x.kind == nkSym:
+      if x.sym.kind == skResult:
+        n.sons[0] = x # 'result[]' --> 'result'
+        n.sons[1] = takeImplicitAddr(c, ri)
+      if x.sym.kind != skParam:
+        # XXX This is hacky. See bug #4910.
+        x.typ.flags.incl tfVarIsPtr
       #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info
 
 template resultTypeIsInferrable(typ: PType): untyped =
@@ -1383,9 +1386,10 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
           typeMismatch(n.info, lhs.typ, rhs.typ)
 
     n.sons[1] = fitNode(c, le, rhs, n.info)
-    if tfHasAsgn in lhs.typ.flags and not lhsIsResult and
-        mode != noOverloadedAsgn:
-      return overloadedAsgn(c, lhs, n.sons[1])
+    if not newDestructors:
+      if tfHasAsgn in lhs.typ.flags and not lhsIsResult and
+          mode != noOverloadedAsgn:
+        return overloadedAsgn(c, lhs, n.sons[1])
 
     fixAbstractType(c, n)
     asgnToResultVar(c, n, n.sons[0], n.sons[1])
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 17bdf0902..17d9c9840 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -19,18 +19,6 @@ when defined(useDfa):
 #
 # * effect+exception tracking
 # * "usage before definition" checking
-# * checks for invalid usages of compiletime magics (not implemented)
-# * checks for invalid usages of NimNode (not implemented)
-# * later: will do an escape analysis for closures at least
-
-# Predefined effects:
-#   io, time (time dependent), gc (performs GC'ed allocation), exceptions,
-#   side effect (accesses global), store (stores into *type*),
-#   store_unknown (performs some store) --> store(any)|store(x)
-#   load (loads from *type*), recursive (recursive call), unsafe,
-#   endless (has endless loops), --> user effects are defined over *patterns*
-#   --> a TR macro can annotate the proc with user defined annotations
-#   --> the effect system can access these
 
 # ------------------------ exception and tag tracking -------------------------
 
@@ -251,6 +239,7 @@ proc useVar(a: PEffects, n: PNode) =
         (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
       #if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
       markGcUnsafe(a, s)
+      markSideEffect(a, s)
     else:
       markSideEffect(a, s)
 
@@ -593,6 +582,12 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
   if paramType != nil and paramType.kind == tyVar:
     if n.kind == nkSym and isLocalVar(tracked, n.sym):
       makeVolatile(tracked, n.sym)
+  if paramType != nil and paramType.kind == tyProc and tfGcSafe in paramType.flags:
+    let argtype = skipTypes(a.typ, abstractInst)
+    # XXX figure out why this can be a non tyProc here. See httpclient.nim for an
+    # example that triggers it.
+    if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe:
+      localError(n.info, $n & " is not GC safe")
   notNilCheck(tracked, n, paramType)
 
 proc breaksBlock(n: PNode): bool =
@@ -745,7 +740,7 @@ proc track(tracked: PEffects, n: PNode) =
           if not (a.kind == nkSym and a.sym == tracked.owner):
             markSideEffect(tracked, a)
     if a.kind != nkSym or a.sym.magic != mNBindSym:
-      for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
+      for i in 1 ..< len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
     if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
       # may not look like an assignment, but it is:
       let arg = n.sons[1]
@@ -982,9 +977,10 @@ proc trackProc*(s: PSym, body: PNode) =
     message(s.info, warnLockLevel,
       "declared lock level is $1, but real lock level is $2" %
         [$s.typ.lockLevel, $t.maxLockLevel])
-  if s.kind == skFunc:
-    when defined(dfa): dataflowAnalysis(s, body)
-    trackWrites(s, body)
+  when false:
+    if s.kind == skFunc:
+      when defined(dfa): dataflowAnalysis(s, body)
+      trackWrites(s, body)
 
 proc trackTopLevelStmt*(module: PSym; n: PNode) =
   if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index a83de6d27..c6e03cef3 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -100,15 +100,16 @@ 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,nkBracket}:
-    if instantiateDestructor(c, n.typ) != nil:
-      localError(n.info, warnDestructor)
-  # This still breaks too many things:
-  when false:
-    if efDetermineType notin flags and n.typ.kind == tyTypeDesc and
-        c.p.owner.kind notin {skTemplate, skMacro}:
-      localError(n.info, errGenerated, "value expected, but got a type")
+  if not newDestructors:
+    if efAllowDestructor notin flags and
+        n.kind in nkCallKinds+{nkObjConstr,nkBracket}:
+      if instantiateDestructor(c, n.typ) != nil:
+        localError(n.info, warnDestructor)
+    # This still breaks too many things:
+    when false:
+      if efDetermineType notin flags and n.typ.kind == tyTypeDesc and
+          c.p.owner.kind notin {skTemplate, skMacro}:
+        localError(n.info, errGenerated, "value expected, but got a type")
 
 proc semExprBranch(c: PContext, n: PNode): PNode =
   result = semExpr(c, n)
@@ -399,7 +400,7 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
   # in order for this transformation to be correct.
   let L = identDefs.len
   let value = identDefs[L-1]
-  if value.typ != nil and tfHasAsgn in value.typ.flags:
+  if value.typ != nil and tfHasAsgn in value.typ.flags and not newDestructors:
     # the spec says we need to rewrite 'var x = T()' to 'var x: T; x = T()':
     identDefs.sons[L-1] = emptyNode
     if result.kind != nkStmtList:
@@ -607,7 +608,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         if def.kind == nkPar: v.ast = def[j]
         setVarType(v, tup.sons[j])
         b.sons[j] = newSymNode(v)
-      addDefer(c, result, v)
+      if not newDestructors: addDefer(c, result, v)
       checkNilable(v)
       if sfCompileTime in v.flags: hasCompileTime = true
   if hasCompileTime: vm.setupCompileTimeVar(c.module, c.cache, result)
@@ -1276,9 +1277,30 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
 proc semOverride(c: PContext, s: PSym, n: PNode) =
   case s.name.s.normalize
   of "destroy", "=destroy":
-    doDestructorStuff(c, s, n)
-    if not experimentalMode(c):
-      localError n.info, "use the {.experimental.} pragma to enable destructors"
+    if newDestructors:
+      let t = s.typ
+      var noError = false
+      if t.len == 2 and t.sons[0] == nil and t.sons[1].kind == tyVar:
+        var obj = t.sons[1].sons[0]
+        while true:
+          incl(obj.flags, tfHasAsgn)
+          if obj.kind == tyGenericBody: obj = obj.lastSon
+          elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
+          else: break
+        if obj.kind in {tyObject, tyDistinct}:
+          if obj.destructor.isNil:
+            obj.destructor = s
+          else:
+            localError(n.info, errGenerated,
+              "cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
+          noError = true
+      if not noError:
+        localError(n.info, errGenerated,
+          "signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
+    else:
+      doDestructorStuff(c, s, n)
+      if not experimentalMode(c):
+        localError n.info, "use the {.experimental.} pragma to enable destructors"
     incl(s.flags, sfUsed)
   of "deepcopy", "=deepcopy":
     if s.typ.len == 2 and
@@ -1303,7 +1325,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
       localError(n.info, errGenerated,
                  "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
     incl(s.flags, sfUsed)
-  of "=":
+  of "=", "=sink":
     if s.magic == mAsgn: return
     incl(s.flags, sfUsed)
     let t = s.typ
@@ -1321,14 +1343,15 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
           objB = objB.sons[0]
         else: break
       if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB):
-        if obj.assignment.isNil:
-          obj.assignment = s
+        let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink)
+        if opr[].isNil:
+          opr[] = s
         else:
           localError(n.info, errGenerated,
-                     "cannot bind another '=' to: " & typeToString(obj))
+                     "cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
         return
     localError(n.info, errGenerated,
-               "signature for '=' must be proc[T: object](x: var T; y: T)")
+               "signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)")
   else:
     if sfOverriden in s.flags:
       localError(n.info, errGenerated,
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index a3953d87e..172a557b3 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -357,11 +357,17 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
         assert newbody.kind in {tyRef, tyPtr}
         assert newbody.lastSon.typeInst == nil
         newbody.lastSon.typeInst = result
-    let asgn = newbody.assignment
-    if asgn != nil and sfFromGeneric notin asgn.flags:
-      # '=' needs to be instantiated for generics when the type is constructed:
-      newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info,
-                                                attachedAsgn, 1)
+    template typeBound(field) =
+      let opr = newbody.field
+      if opr != nil and sfFromGeneric notin opr.flags:
+        # '=' needs to be instantiated for generics when the type is constructed:
+        newbody.field = cl.c.instTypeBoundOp(cl.c, opr, result, cl.info,
+                                             attachedAsgn, 1)
+    # we need to produce the destructor first here because generated '='
+    # and '=sink' operators can rely on it:
+    if newDestructors: typeBound(destructor)
+    typeBound(assignment)
+    typeBound(sink)
     let methods = skipTypes(bbody, abstractPtrs).methods
     for col, meth in items(methods):
       # we instantiate the known methods belonging to that type, this causes
diff --git a/compiler/transf.nim b/compiler/transf.nim
index f1ee49a54..c3d12dafe 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -21,9 +21,7 @@
 import
   intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
   idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
-  lambdalifting, sempass2, lowerings, lookups
-
-# implementation
+  lambdalifting, sempass2, lowerings, lookups, destroyer
 
 type
   PTransNode* = distinct PNode
@@ -45,7 +43,7 @@ type
     inlining: int            # > 0 if we are in inlining context (copy vars)
     nestedProcs: int         # > 0 if we are in a nested proc
     contSyms, breakSyms: seq[PSym]  # to transform 'continue' and 'break'
-    deferDetected, tooEarly: bool
+    deferDetected, tooEarly, needsDestroyPass: bool
   PTransf = ref TTransfContext
 
 proc newTransNode(a: PNode): PTransNode {.inline.} =
@@ -782,7 +780,8 @@ proc transform(c: PTransf, n: PNode): PTransNode =
                   nkBlockStmt, nkBlockExpr}:
       oldDeferAnchor = c.deferAnchor
       c.deferAnchor = n
-
+  if n.typ != nil and tfHasAsgn in n.typ.flags:
+    c.needsDestroyPass = true
   case n.kind
   of nkSym:
     result = transformSym(c, n)
@@ -972,9 +971,11 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
     result = processTransf(c, result, prc)
     liftDefer(c, result)
     #result = liftLambdas(prc, result)
-    incl(result.flags, nfTransf)
     when useEffectSystem: trackProc(prc, result)
-    #if prc.name.s == "testbody":
+    if c.needsDestroyPass and newDestructors:
+      result = injectDestructorCalls(prc, result)
+    incl(result.flags, nfTransf)
+      #if prc.name.s == "testbody":
     #  echo renderTree(result)
 
 proc transformStmt*(module: PSym, n: PNode): PNode =
@@ -985,10 +986,12 @@ proc transformStmt*(module: PSym, n: PNode): PNode =
     result = processTransf(c, n, module)
     liftDefer(c, result)
     #result = liftLambdasForTopLevel(module, result)
-    incl(result.flags, nfTransf)
     when useEffectSystem: trackTopLevelStmt(module, result)
     #if n.info ?? "temp.nim":
     #  echo renderTree(result, {renderIds})
+    if c.needsDestroyPass and newDestructors:
+      result = injectDestructorCalls(module, result)
+    incl(result.flags, nfTransf)
 
 proc transformExpr*(module: PSym, n: PNode): PNode =
   if nfTransf in n.flags:
@@ -997,4 +1000,6 @@ proc transformExpr*(module: PSym, n: PNode): PNode =
     var c = openTransf(module, "")
     result = processTransf(c, n, module)
     liftDefer(c, result)
+    if c.needsDestroyPass and newDestructors:
+      result = injectDestructorCalls(module, result)
     incl(result.flags, nfTransf)
diff --git a/doc/manual/types.txt b/doc/manual/types.txt
index 2c4b019ad..070296271 100644
--- a/doc/manual/types.txt
+++ b/doc/manual/types.txt
@@ -570,7 +570,7 @@ order. The *names* of the fields also have to be identical.
 
 The assignment operator for tuples copies each component.
 The default assignment operator for objects copies each component. Overloading
-of the assignment operator is described in `type-bound-operations-operator`_. 
+of the assignment operator is described in `type-bound-operations-operator`_.
 
 .. code-block:: nim
 
@@ -905,8 +905,8 @@ not compatible to ``pointer`` to prevent the following from compiling:
 Future directions:
 
 * Memory regions might become available for  ``string`` and ``seq`` too.
-* Builtin regions like ``private``, ``global`` and ``local`` will
-  prove very useful for the upcoming OpenCL target.
+* Builtin regions like ``private``, ``global`` and ``local`` might be
+  useful for an OpenCL target.
 * Builtin "regions" can model ``lent`` and ``unique`` pointers.
 * An assignment operator can be attached to a region so that proper write
   barriers can be generated. This would imply that the GC can be implemented
diff --git a/lib/nimbase.h b/lib/nimbase.h
index 34a2e43e7..c06c45691 100644
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -396,13 +396,6 @@ typedef struct TStringDesc* string;
 #define GenericSeqSize sizeof(TGenericSeq)
 #define paramCount() cmdCount
 
-#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__i386__)
-#  ifndef NAN
-static unsigned long nimNaN[2]={0xffffffff, 0x7fffffff};
-#    define NAN (*(double*) nimNaN)
-#  endif
-#endif
-
 #ifndef NAN
 #  define NAN (0.0 / 0.0)
 #endif
diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim
index 69f6acfb8..7321889a8 100644
--- a/lib/posix/posix_other.nim
+++ b/lib/posix/posix_other.nim
@@ -146,7 +146,7 @@ type
   Mode* {.importc: "mode_t", header: "<sys/types.h>".} = cint
   Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = int
   Off* {.importc: "off_t", header: "<sys/types.h>".} = int64
-  Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int
+  Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int32
   Pthread_attr* {.importc: "pthread_attr_t", header: "<sys/types.h>".} = int
   Pthread_barrier* {.importc: "pthread_barrier_t",
                       header: "<sys/types.h>".} = int
diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim
index f847ddd58..560273dfa 100644
--- a/lib/pure/collections/lists.nim
+++ b/lib/pure/collections/lists.nim
@@ -37,6 +37,14 @@ type
   DoublyLinkedRing*[T] = object ## a doubly linked ring
     head*: DoublyLinkedNode[T]
 
+  SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T]
+
+  SomeLinkedRing*[T] = SinglyLinkedRing[T] | DoublyLinkedRing[T]
+
+  SomeLinkedCollection*[T] = SomeLinkedList[T] | SomeLinkedRing[T]
+
+  SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[T]
+
 {.deprecated: [TDoublyLinkedNode: DoublyLinkedNodeObj,
     PDoublyLinkedNode: DoublyLinkedNode,
     TSinglyLinkedNode: SinglyLinkedNodeObj,
@@ -86,137 +94,57 @@ template itemsRingImpl() {.dirty.} =
       it = it.next
       if it == L.head: break
 
-template nodesListImpl() {.dirty.} =
-  var it = L.head
-  while it != nil:
-    var nxt = it.next
-    yield it
-    it = nxt
-
-template nodesRingImpl() {.dirty.} =
-  var it = L.head
-  if it != nil:
-    while true:
-      var nxt = it.next
-      yield it
-      it = nxt
-      if it == L.head: break
-
-template findImpl() {.dirty.} =
-  for x in nodes(L):
-    if x.value == value: return x
-
-iterator items*[T](L: DoublyLinkedList[T]): T =
+iterator items*[T](L: SomeLinkedList[T]): T =
   ## yields every value of `L`.
   itemsListImpl()
 
-iterator items*[T](L: SinglyLinkedList[T]): T =
-  ## yields every value of `L`.
-  itemsListImpl()
-
-iterator items*[T](L: SinglyLinkedRing[T]): T =
-  ## yields every value of `L`.
-  itemsRingImpl()
-
-iterator items*[T](L: DoublyLinkedRing[T]): T =
+iterator items*[T](L: SomeLinkedRing[T]): T =
   ## yields every value of `L`.
   itemsRingImpl()
 
-iterator mitems*[T](L: var DoublyLinkedList[T]): var T =
+iterator mitems*[T](L: var SomeLinkedList[T]): var T =
   ## yields every value of `L` so that you can modify it.
   itemsListImpl()
 
-iterator mitems*[T](L: var SinglyLinkedList[T]): var T =
-  ## yields every value of `L` so that you can modify it.
-  itemsListImpl()
-
-iterator mitems*[T](L: var SinglyLinkedRing[T]): var T =
+iterator mitems*[T](L: var SomeLinkedRing[T]): var T =
   ## yields every value of `L` so that you can modify it.
   itemsRingImpl()
 
-iterator mitems*[T](L: var DoublyLinkedRing[T]): var T =
-  ## yields every value of `L` so that you can modify it.
-  itemsRingImpl()
-
-iterator nodes*[T](L: SinglyLinkedList[T]): SinglyLinkedNode[T] =
-  ## iterates over every node of `x`. Removing the current node from the
-  ## list during traversal is supported.
-  nodesListImpl()
-
-iterator nodes*[T](L: DoublyLinkedList[T]): DoublyLinkedNode[T] =
-  ## iterates over every node of `x`. Removing the current node from the
-  ## list during traversal is supported.
-  nodesListImpl()
-
-iterator nodes*[T](L: SinglyLinkedRing[T]): SinglyLinkedNode[T] =
+iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
   ## iterates over every node of `x`. Removing the current node from the
   ## list during traversal is supported.
-  nodesRingImpl()
+  var it = L.head
+  while it != nil:
+    var nxt = it.next
+    yield it
+    it = nxt
 
-iterator nodes*[T](L: DoublyLinkedRing[T]): DoublyLinkedNode[T] =
+iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
   ## iterates over every node of `x`. Removing the current node from the
   ## list during traversal is supported.
-  nodesRingImpl()
+  var it = L.head
+  if it != nil:
+    while true:
+      var nxt = it.next
+      yield it
+      it = nxt
+      if it == L.head: break
 
-template dollarImpl() {.dirty.} =
+proc `$`*[T](L: SomeLinkedCollection[T]): string =
+  ## turns a list into its string representation.
   result = "["
   for x in nodes(L):
     if result.len > 1: result.add(", ")
     result.add($x.value)
   result.add("]")
 
-proc `$`*[T](L: SinglyLinkedList[T]): string =
-  ## turns a list into its string representation.
-  dollarImpl()
-
-proc `$`*[T](L: DoublyLinkedList[T]): string =
-  ## turns a list into its string representation.
-  dollarImpl()
-
-proc `$`*[T](L: SinglyLinkedRing[T]): string =
-  ## turns a list into its string representation.
-  dollarImpl()
-
-proc `$`*[T](L: DoublyLinkedRing[T]): string =
-  ## turns a list into its string representation.
-  dollarImpl()
-
-proc find*[T](L: SinglyLinkedList[T], value: T): SinglyLinkedNode[T] =
-  ## searches in the list for a value. Returns nil if the value does not
-  ## exist.
-  findImpl()
-
-proc find*[T](L: DoublyLinkedList[T], value: T): DoublyLinkedNode[T] =
+proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] =
   ## searches in the list for a value. Returns nil if the value does not
   ## exist.
-  findImpl()
-
-proc find*[T](L: SinglyLinkedRing[T], value: T): SinglyLinkedNode[T] =
-  ## searches in the list for a value. Returns nil if the value does not
-  ## exist.
-  findImpl()
-
-proc find*[T](L: DoublyLinkedRing[T], value: T): DoublyLinkedNode[T] =
-  ## searches in the list for a value. Returns nil if the value does not
-  ## exist.
-  findImpl()
-
-proc contains*[T](L: SinglyLinkedList[T], value: T): bool {.inline.} =
-  ## searches in the list for a value. Returns false if the value does not
-  ## exist, true otherwise.
-  result = find(L, value) != nil
-
-proc contains*[T](L: DoublyLinkedList[T], value: T): bool {.inline.} =
-  ## searches in the list for a value. Returns false if the value does not
-  ## exist, true otherwise.
-  result = find(L, value) != nil
-
-proc contains*[T](L: SinglyLinkedRing[T], value: T): bool {.inline.} =
-  ## searches in the list for a value. Returns false if the value does not
-  ## exist, true otherwise.
-  result = find(L, value) != nil
+  for x in nodes(L):
+    if x.value == value: return x
 
-proc contains*[T](L: DoublyLinkedRing[T], value: T): bool {.inline.} =
+proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} =
   ## searches in the list for a value. Returns false if the value does not
   ## exist, true otherwise.
   result = find(L, value) != nil
@@ -266,7 +194,6 @@ proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
   if n.next != nil: n.next.prev = n.prev
   if n.prev != nil: n.prev.next = n.next
 
-
 proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
   ## appends a node `n` to `L`. Efficiency: O(1).
   if L.head != nil:
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index 2a0dbd2ca..0f23b7e85 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -448,12 +448,22 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
     if selectWorker(readyWorker, fn, data): return
     for i in 0.. <currentPoolSize:
       if selectWorker(addr(workersData[i]), fn, data): return
+
     # determine what to do, but keep in mind this is expensive too:
     # state.calls < maxPoolSize: warmup phase
     # (state.calls and 127) == 0: periodic check
     if state.calls < maxPoolSize or (state.calls and 127) == 0:
       # ensure the call to 'advice' is atomic:
       if tryAcquire(stateLock):
+        if currentPoolSize < minPoolSize:
+          if not workersData[currentPoolSize].initialized:
+            activateWorkerThread(currentPoolSize)
+          let w = addr(workersData[currentPoolSize])
+          atomicInc currentPoolSize
+          if selectWorker(w, fn, data):
+            release(stateLock)
+            return
+
         case advice(state)
         of doNothing: discard
         of doCreateThread:
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index cb4f4f664..de1d332a3 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -883,7 +883,9 @@ proc recvFull(client: HttpClient | AsyncHttpClient, size: int, timeout: int,
       let data = client.socket.recv(sizeToRecv, timeout)
     else:
       let data = await client.socket.recv(sizeToRecv)
-    if data == "": break # We've been disconnected.
+    if data == "":
+      client.close()
+      break # We've been disconnected.
 
     readLen.inc(data.len)
     if keep:
@@ -950,6 +952,7 @@ proc parseBody(client: HttpClient | AsyncHttpClient,
       if length > 0:
         let recvLen = await client.recvFull(length, client.timeout, true)
         if recvLen == 0:
+          client.close()
           httpError("Got disconnected while trying to read body.")
         if recvLen != length:
           httpError("Received length doesn't match expected length. Wanted " &
@@ -962,13 +965,20 @@ proc parseBody(client: HttpClient | AsyncHttpClient,
       if headers.getOrDefault"Connection" == "close" or httpVersion == "1.0":
         while true:
           let recvLen = await client.recvFull(4000, client.timeout, true)
-          if recvLen == 0: break
+          if recvLen == 0:
+            client.close()
+            break
 
   when client is AsyncHttpClient:
     client.bodyStream.complete()
   else:
     client.bodyStream.setPosition(0)
 
+  # If the server will close our connection, then no matter the method of
+  # reading the body, we need to close our socket.
+  if headers.getOrDefault"Connection" == "close":
+    client.close()
+
 proc parseResponse(client: HttpClient | AsyncHttpClient,
                    getBody: bool): Future[Response | AsyncResponse]
                    {.multisync.} =
@@ -984,7 +994,10 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
       line = await client.socket.recvLine(client.timeout)
     else:
       line = await client.socket.recvLine()
-    if line == "": break # We've been disconnected.
+    if line == "":
+      # We've been disconnected.
+      client.close()
+      break
     if line == "\c\L":
       fullyRead = true
       break
@@ -1033,7 +1046,8 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
                    url: Uri) {.multisync.} =
   if client.currentURL.hostname != url.hostname or
       client.currentURL.scheme != url.scheme or
-      client.currentURL.port != url.port:
+      client.currentURL.port != url.port or
+      (not client.connected):
     let isSsl = url.scheme.toLowerAscii() == "https"
 
     if isSsl and not defined(ssl):
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index 656114fb1..fd7a3af03 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -37,15 +37,15 @@
 ## Retrieving the value of a JSON node can then be achieved using one of the
 ## helper procedures, which include:
 ##
-## * ``getNum``
-## * ``getFNum``
+## * ``getInt``
+## * ``getFloat``
 ## * ``getStr``
-## * ``getBVal``
+## * ``getBool``
 ##
 ## To retrieve the value of ``"key"`` you can do the following:
 ##
 ## .. code-block:: Nim
-##   doAssert jsonNode["key"].getFNum() == 3.14
+##   doAssert jsonNode["key"].getFloat() == 3.14
 ##
 ## The ``[]`` operator will raise an exception when the specified field does
 ## not exist. If you wish to avoid this behaviour you can use the ``{}``
@@ -681,14 +681,25 @@ proc getStr*(n: JsonNode, default: string = ""): string =
   if n.isNil or n.kind != JString: return default
   else: return n.str
 
-proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt =
+proc getInt*(n: JsonNode, default: int = 0): int =
   ## Retrieves the int value of a `JInt JsonNode`.
   ##
   ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil.
   if n.isNil or n.kind != JInt: return default
+  else: return int(n.num)
+
+proc getBiggestInt*(n: JsonNode, default: BiggestInt = 0): BiggestInt =
+  ## Retrieves the BiggestInt value of a `JInt JsonNode`.
+  ##
+  ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil.
+  if n.isNil or n.kind != JInt: return default
   else: return n.num
 
-proc getFNum*(n: JsonNode, default: float = 0.0): float =
+proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt {.deprecated.} =
+  ## Deprecated - use getInt or getBiggestInt instead
+  getBiggestInt(n, default)
+
+proc getFloat*(n: JsonNode, default: float = 0.0): float =
   ## Retrieves the float value of a `JFloat JsonNode`.
   ##
   ## Returns ``default`` if ``n`` is not a ``JFloat`` or ``JInt``, or if ``n`` is nil.
@@ -698,13 +709,21 @@ proc getFNum*(n: JsonNode, default: float = 0.0): float =
   of JInt: return float(n.num)
   else: return default
 
-proc getBVal*(n: JsonNode, default: bool = false): bool =
+proc getFNum*(n: JsonNode, default: float = 0.0): float {.deprecated.} =
+  ## Deprecated - use getFloat instead
+  getFloat(n, default)
+
+proc getBool*(n: JsonNode, default: bool = false): bool =
   ## Retrieves the bool value of a `JBool JsonNode`.
   ##
   ## Returns ``default`` if ``n`` is not a ``JBool``, or if ``n`` is nil.
   if n.isNil or n.kind != JBool: return default
   else: return n.bval
 
+proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated.} =
+  ## Deprecated - use getBVal instead
+  getBool(n, default)
+
 proc getFields*(n: JsonNode,
     default = initOrderedTable[string, JsonNode](4)):
         OrderedTable[string, JsonNode] =
@@ -1342,7 +1361,7 @@ proc getEnum(node: JsonNode, ast: string, T: typedesc): T =
     # TODO: I shouldn't need this proc.
     proc convert[T](x: BiggestInt): T = T(x)
     verifyJsonKind(node, {JInt}, ast)
-    return convert[T](node.getNum())
+    return convert[T](node.getBiggestInt())
   else:
     verifyJsonKind(node, {JString}, ast)
     return parseEnum[T](node.getStr())
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index 8037b31b0..7fd8bbcef 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -184,6 +184,8 @@ when not defined(JS):
   proc pow*(x, y: float32): float32 {.importc: "powf", header: "<math.h>".}
   proc pow*(x, y: float64): float64 {.importc: "pow", header: "<math.h>".}
     ## computes x to power raised of y.
+    ##
+    ## To compute power between integers, use `^` e.g. 2 ^ 6
 
   proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".}
   proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".}
diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim
index bf26d2e59..a54556915 100644
--- a/lib/pure/strscans.nim
+++ b/lib/pure/strscans.nim
@@ -253,7 +253,7 @@ is performed.
   for r in collectLinks(body):
     echo r
 
-In this example both macros are combined seamlessly in order to maximise 
+In this example both macros are combined seamlessly in order to maximise
 efficiency and perform different checks.
 
 .. code-block:: nim
@@ -308,7 +308,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
   ## See top level documentation of his module of how ``scanf`` works.
   template matchBind(parser) {.dirty.} =
     var resLen = genSym(nskLet, "resLen")
-    conds.add newLetStmt(resLen, newCall(bindSym(parser), input, results[i], idx))
+    conds.add newLetStmt(resLen, newCall(bindSym(parser), inp, results[i], idx))
     conds.add resLen.notZero
     conds.add resLen
 
@@ -316,7 +316,8 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
   var p = 0
   var idx = genSym(nskVar, "idx")
   var res = genSym(nskVar, "res")
-  result = newTree(nnkStmtListExpr, newVarStmt(idx, newLit 0), newVarStmt(res, newLit false))
+  let inp = genSym(nskLet, "inp")
+  result = newTree(nnkStmtListExpr, newLetStmt(inp, input), newVarStmt(idx, newLit 0), newVarStmt(res, newLit false))
   var conds = newTree(nnkStmtList)
   var fullMatch = false
   while p < pattern.len:
@@ -325,7 +326,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
       case pattern[p]
       of '$':
         var resLen = genSym(nskLet, "resLen")
-        conds.add newLetStmt(resLen, newCall(bindSym"skip", input, newLit($pattern[p]), idx))
+        conds.add newLetStmt(resLen, newCall(bindSym"skip", inp, newLit($pattern[p]), idx))
         conds.add resLen.notZero
         conds.add resLen
       of 'w':
@@ -347,7 +348,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
           error("no float var given for $f")
         inc i
       of 's':
-        conds.add newCall(bindSym"inc", idx, newCall(bindSym"skipWhitespace", input, idx))
+        conds.add newCall(bindSym"inc", idx, newCall(bindSym"skipWhitespace", inp, idx))
         conds.add newEmptyNode()
         conds.add newEmptyNode()
       of '.':
@@ -364,7 +365,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
             token.add pattern[q]
             inc q
           var resLen = genSym(nskLet, "resLen")
-          conds.add newLetStmt(resLen, newCall(bindSym"parseUntil", input, results[i], newLit(token), idx))
+          conds.add newLetStmt(resLen, newCall(bindSym"parseUntil", inp, results[i], newLit(token), idx))
           conds.add newCall(bindSym"!=", resLen, newLit min)
           conds.add resLen
         else:
@@ -386,7 +387,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
         let expr = pattern.substr(start, p-1)
         if i < results.len:
           var resLen = genSym(nskLet, "resLen")
-          conds.add newLetStmt(resLen, buildUserCall(expr, input, results[i], idx))
+          conds.add newLetStmt(resLen, buildUserCall(expr, inp, results[i], idx))
           conds.add newCall(bindSym"!=", resLen, newLit 0)
           conds.add resLen
         else:
@@ -406,7 +407,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
           else: discard
           inc p
         let expr = pattern.substr(start, p-1)
-        conds.add newCall(bindSym"inc", idx, buildUserCall(expr, input, idx))
+        conds.add newCall(bindSym"inc", idx, buildUserCall(expr, inp, idx))
         conds.add newEmptyNode()
         conds.add newEmptyNode()
       else: error("invalid format string")
@@ -417,13 +418,13 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
         token.add pattern[p]
         inc p
       var resLen = genSym(nskLet, "resLen")
-      conds.add newLetStmt(resLen, newCall(bindSym"skip", input, newLit(token), idx))
+      conds.add newLetStmt(resLen, newCall(bindSym"skip", inp, newLit(token), idx))
       conds.add resLen.notZero
       conds.add resLen
   result.add conditionsToIfChain(conds, idx, res, 0)
   if fullMatch:
     result.add newCall(bindSym"and", res,
-      newCall(bindSym">=", idx, newCall(bindSym"len", input)))
+      newCall(bindSym">=", idx, newCall(bindSym"len", inp)))
   else:
     result.add res
 
@@ -684,3 +685,14 @@ when isMainModule:
           "NimMain c:/users/anwender/projects/nim/lib/system.nim:2613",
           "main c:/users/anwender/projects/nim/lib/system.nim:2620"]
   doAssert parseGDB(gdbOut) == result
+
+  # bug #6487
+  var count = 0
+
+  proc test(): string =
+    inc count
+    result = ",123123"
+
+  var a: int
+  discard scanf(test(), ",$i", a)
+  doAssert count == 1
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index 0c4f15c91..7d9c3108b 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -285,7 +285,7 @@ proc runeReverseOffset*(s: string, rev:Positive): (int, int) =
 
 proc runeSubStr*(s: string, pos:int, len:int = int.high): string =
   ## Returns the UTF-8 substring starting at codepoint pos
-  ## with len codepoints. If pos or len is negativ they count from
+  ## with len codepoints. If pos or len is negative they count from
   ## the end of the string. If len is not given it means the longest
   ## possible string.
   ##
diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim
index d8e4ed52f..4b2e4e052 100644
--- a/lib/pure/uri.nim
+++ b/lib/pure/uri.nim
@@ -278,7 +278,9 @@ proc `/`*(x: Uri, path: string): Uri =
   result = x
 
   if result.path.len == 0:
-    result.path = path
+    if path[0] != '/':
+      result.path = "/"
+    result.path.add(path)
     return
 
   if result.path[result.path.len-1] == '/':
@@ -476,6 +478,11 @@ when isMainModule:
     let foo = parseUri("http://example.com") / "/baz"
     doAssert foo.path == "/baz"
 
+  # bug found on stream 13/10/17
+  block:
+    let foo = parseUri("http://localhost:9515") / "status"
+    doAssert $foo == "http://localhost:9515/status"
+
   # isAbsolute tests
   block:
     doAssert "www.google.com".parseUri().isAbsolute() == false
@@ -515,4 +522,6 @@ when isMainModule:
     doAssert "https://example.com/about".parseUri().isAbsolute == true
     doAssert "https://example.com/about/staff.html".parseUri().isAbsolute == true
     doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true
-    doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true
\ No newline at end of file
+    doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true
+
+  echo("All good!")
\ No newline at end of file
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index 78db96e77..19d27e7d2 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -301,13 +301,14 @@ proc pageAddr(p: pointer): PChunk {.inline.} =
   result = cast[PChunk](cast[ByteAddress](p) and not PageMask)
   #sysAssert(Contains(allocator.chunkStarts, pageIndex(result)))
 
-proc writeFreeList(a: MemRegion) =
-  var it = a.freeChunksList
-  c_fprintf(stdout, "freeChunksList: %p\n", it)
-  while it != nil:
-    c_fprintf(stdout, "it: %p, next: %p, prev: %p, size: %ld\n",
-              it, it.next, it.prev, it.size)
-    it = it.next
+when false:
+  proc writeFreeList(a: MemRegion) =
+    var it = a.freeChunksList
+    c_fprintf(stdout, "freeChunksList: %p\n", it)
+    while it != nil:
+      c_fprintf(stdout, "it: %p, next: %p, prev: %p, size: %ld\n",
+                it, it.next, it.prev, it.size)
+      it = it.next
 
 const nimMaxHeap {.intdefine.} = 0
 
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index a2ff72a30..21757cf78 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -155,14 +155,15 @@ template setColor(c, col) =
   else:
     c.refcount = c.refcount and not colorMask or col
 
-proc writeCell(msg: cstring, c: PCell) =
-  var kind = -1
-  var typName: cstring = "nil"
-  if c.typ != nil:
-    kind = ord(c.typ.kind)
-    when defined(nimTypeNames):
-      if not c.typ.name.isNil:
-        typName = c.typ.name
+when defined(logGC):
+  proc writeCell(msg: cstring, c: PCell) =
+    var kind = -1
+    var typName: cstring = "nil"
+    if c.typ != nil:
+      kind = ord(c.typ.kind)
+      when defined(nimTypeNames):
+        if not c.typ.name.isNil:
+          typName = c.typ.name
 
   when leakDetector:
     c_fprintf(stdout, "[GC] %s: %p %d %s rc=%ld from %s(%ld)\n",
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index 96c045e6b..016bf5822 100644
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -398,7 +398,7 @@ template afterThreadRuns() =
     threadDestructionHandlers[i]()
 
 when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions):
-  proc deallocOsPages()
+  proc deallocOsPages() {.rtl.}
 
 when defined(boehmgc):
   type GCStackBaseProc = proc(sb: pointer, t: pointer) {.noconv.}
diff --git a/tests/closure/t1641.nim b/tests/closure/t1641.nim
new file mode 100644
index 000000000..a3e4da367
--- /dev/null
+++ b/tests/closure/t1641.nim
@@ -0,0 +1,20 @@
+discard """
+  output: '''foo 0
+bar 0
+baz'''
+"""
+
+# bug #1641
+proc baz() =
+  echo "baz"
+
+proc bar(x: int, p: proc()) =
+  echo "bar ", x
+  p()
+
+proc foo(x: int, p: proc(x: int)) =
+  echo "foo ", x
+  p(x)
+
+let x = 0
+x.foo do(x: int): x.bar do(): baz()
diff --git a/tests/cpp/tget_subsystem.nim b/tests/cpp/tget_subsystem.nim
index 461914739..81009dd39 100644
--- a/tests/cpp/tget_subsystem.nim
+++ b/tests/cpp/tget_subsystem.nim
@@ -21,3 +21,11 @@ proc getSubsystem*[T](): ptr T {.
 
 let input: ptr Input = getSubsystem[Input]()
 
+
+# bug #4910
+
+proc foo() =
+  var ts: array[10, int]
+  for t in mitems(ts):
+     t = 123
+
diff --git a/tests/destructor/tcustomstrings.nim b/tests/destructor/tcustomstrings.nim
new file mode 100644
index 000000000..2250d4772
--- /dev/null
+++ b/tests/destructor/tcustomstrings.nim
@@ -0,0 +1,96 @@
+discard """
+  output: '''foo bar to appendmore here
+foo bar to appendmore here
+foo bar to appendmore here
+foo bar to appendmore here
+foo bar to appendmore here
+after 16 16'''
+  cmd: '''nim c --newruntime $file'''
+"""
+
+type
+  mystring = object
+    len, cap: int
+    data: ptr UncheckedArray[char]
+
+{.this: self.}
+
+var
+  allocCount, deallocCount: int
+
+proc `=destroy`*(self: var mystring) =
+  if data != nil:
+    dealloc(data)
+    inc deallocCount
+    data = nil
+    len = 0
+    cap = 0
+
+proc `=sink`*(a: var mystring, b: mystring) =
+  # we hope this is optimized away for not yet alive objects:
+  if a.data != nil and a.data != b.data:
+    dealloc(a.data)
+    inc deallocCount
+  a.len = b.len
+  a.cap = b.cap
+  a.data = b.data
+
+proc `=`*(a: var mystring; b: mystring) =
+  if a.data != nil and a.data != b.data:
+    dealloc(a.data)
+    inc deallocCount
+    a.data = nil
+  a.len = b.len
+  a.cap = b.cap
+  if b.data != nil:
+    a.data = cast[type(a.data)](alloc(a.cap + 1))
+    inc allocCount
+    copyMem(a.data, b.data, a.cap+1)
+
+proc resize(self: var mystring) =
+  if cap == 0: cap = 8
+  else: cap = (cap * 3) shr 1
+  if data == nil: inc allocCount
+  data = cast[type(data)](realloc(data, cap + 1))
+
+proc add*(self: var mystring; c: char) =
+  if self.len >= self.cap: resize(self)
+  self.data[self.len] = c
+  self.data[self.len+1] = '\0'
+  inc self.len
+
+proc ensure(self: var mystring; newLen: int) =
+  if newLen >= cap:
+    cap = max((cap * 3) shr 1, newLen)
+    if cap > 0:
+      if data == nil: inc allocCount
+      data = cast[type(data)](realloc(data, cap + 1))
+
+proc add*(self: var mystring; y: mystring) =
+  let newLen = len + y.len
+  ensure(self, newLen)
+  copyMem(addr data[len], y.data, y.data.len + 1)
+  len = newLen
+
+proc create*(lit: string): mystring =
+  let newLen = lit.len
+  ensure(result, newLen)
+  copyMem(addr result.data[result.len], unsafeAddr lit[0], newLen + 1)
+  result.len = newLen
+
+proc `&`*(a, b: mystring): mystring =
+  result = a
+  result.add b
+
+proc main(n: int) =
+  var a: mystring
+  let b = create" to append"
+  for i in 0..<n:
+    if i > 4: break
+    a = create"foo bar"
+    let c = b & create"more here"
+    a.add c
+    echo cstring(a.data)
+
+main(1000)
+echo "after ", allocCount, " ", deallocCount
diff --git a/tests/effects/tgcsafe2.nim b/tests/effects/tgcsafe2.nim
new file mode 100644
index 000000000..0b2c090a7
--- /dev/null
+++ b/tests/effects/tgcsafe2.nim
@@ -0,0 +1,12 @@
+discard """
+  errormsg: '''type mismatch: got (proc (s: string){.locks: 0.})'''
+  line: 11
+"""
+#5620
+var res = ""
+
+proc takeCallback(foo: (proc(s: string) {.gcsafe.})) =
+  foo "string"
+
+takeCallback(proc (s: string) =
+  res &= s & "abc")
diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim
index 32d848e06..153cf8556 100644
--- a/tests/stdlib/tjsonmacro.nim
+++ b/tests/stdlib/tjsonmacro.nim
@@ -87,8 +87,8 @@ when isMainModule:
 
     result = to(node, TestVariant)
     doAssert result.name == node["name"].getStr()
-    doAssert result.age == node["age"].getNum().uint8
-    doAssert result.other == node["other"].getNum()
+    doAssert result.age == node["age"].getInt().uint8
+    doAssert result.other == node["other"].getBiggestInt()
 
   # TODO: Test object variant with set in of branch.
   # TODO: Should we support heterogenous arrays?
diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim
index a93c79f5c..d620a4587 100644
--- a/tests/testament/categories.nim
+++ b/tests/testament/categories.nim
@@ -83,9 +83,9 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
       ""
 
   testSpec c, makeTest("lib/nimrtl.nim",
-    options & " --app:lib -d:createNimRtl", cat)
+    options & " --app:lib -d:createNimRtl --threads:on", cat)
   testSpec c, makeTest("tests/dll/server.nim",
-    options & " --app:lib -d:useNimRtl" & rpath, cat)
+    options & " --app:lib -d:useNimRtl --threads:on" & rpath, cat)
 
 
   when defined(Windows):
@@ -101,7 +101,7 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
     var nimrtlDll = DynlibFormat % "nimrtl"
     safeCopyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll)
 
-  testSpec r, makeTest("tests/dll/client.nim", options & " -d:useNimRtl" & rpath,
+  testSpec r, makeTest("tests/dll/client.nim", options & " -d:useNimRtl --threads:on" & rpath,
                        cat, actionRun)
 
 proc dllTests(r: var TResults, cat: Category, options: string) =
diff --git a/tools/nimpretty.nim b/tools/nimpretty.nim
index 2c967b1e8..36d1382cf 100644
--- a/tools/nimpretty.nim
+++ b/tools/nimpretty.nim
@@ -42,7 +42,8 @@ proc writeVersion() =
 proc prettyPrint(infile: string) =
   let fileIdx = fileInfoIdx(infile)
   let tree = parseFile(fileIdx, newIdentCache())
-  renderModule(tree, infile, {})
+  let outfile = changeFileExt(infile, ".pretty.nim")
+  renderModule(tree, infile, outfile, {})
 
 proc main =
   var infile: string
@@ -50,7 +51,7 @@ proc main =
   for kind, key, val in getopt():
     case kind
     of cmdArgument:
-      infile = key
+      infile = key.addFileExt(".nim")
     of cmdLongoption, cmdShortOption:
       case normalize(key)
       of "help", "h": writeHelp()
diff --git a/web/bountysource.nim b/web/bountysource.nim
deleted file mode 100644
index 5dfdb4497..000000000
--- a/web/bountysource.nim
+++ /dev/null
@@ -1,140 +0,0 @@
-# Based on bountysource.cr located at https://github.com/crystal-lang/crystal-website/blob/master/scripts/bountysource.cr
-import httpclient, asyncdispatch, json, strutils, os, strtabs, sequtils, future,
-  algorithm, times
-
-type
-  BountySource = ref object
-    client: AsyncHttpClient
-    team: string
-
-  Sponsor = object
-    name, url, logo: string
-    amount, allTime: float
-    since: TimeInfo
-
-const
-  team = "nim"
-  apiUrl = "https://api.bountysource.com"
-  githubApiUrl = "https://api.github.com"
-
-proc newBountySource(team, token: string): BountySource =
-  result = BountySource(
-    client: newAsyncHttpClient(userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36"),
-    team: team
-  )
-
-  # Set up headers
-  result.client.headers["Accept"] = "application/vnd.bountysource+json; version=2"
-  result.client.headers["Authorization"] = "token " & token
-  result.client.headers["Referer"] = "https://salt.bountysource.com/teams/nim/admin/supporters"
-  result.client.headers["Origin"] = "https://salt.bountysource.com/"
-
-proc getSupporters(self: BountySource): Future[JsonNode] {.async.} =
-  let response = await self.client.get(apiUrl &
-    "/supporters?order=monthly&per_page=200&team_slug=" & self.team)
-  doAssert response.status.startsWith($Http200)
-  return parseJson(await response.body)
-
-proc getGithubUser(username: string): Future[JsonNode] {.async.} =
-  let client = newAsyncHttpClient()
-  let response = await client.get(githubApiUrl & "/users/" & username)
-  if response.status.startsWith($Http200):
-    return parseJson(await response.body)
-  else:
-    echo("Could not get Github user: ", username, ". ", response.status)
-    return nil
-
-proc processSupporters(supporters: JsonNode) =
-  var before = supporters.elems.len
-  supporters.elems.keepIf(
-    item => item["display_name"].getStr != "Anonymous"
-  )
-  echo("Discarded ", before - supporters.elems.len, " anonymous sponsors.")
-  echo("Found ", supporters.elems.len, " named sponsors.")
-
-  supporters.elems.sort(
-    (x, y) => cmp(y["alltime_amount"].getFNum, x["alltime_amount"].getFNum)
-  )
-
-
-proc quote(text: string): string =
-  if {' ', ','} in text:
-    return "\"" & text & "\""
-  else:
-    return text
-
-proc getLevel(amount: float): int =
-  result = 0
-  const levels = [250, 150, 75, 25, 10, 5, 1]
-  for i in levels:
-    if amount.int <= i:
-      result = i
-
-proc writeCsv(sponsors: seq[Sponsor], filename="sponsors.new.csv") =
-  var csv = ""
-  csv.add "logo, name, url, this_month, all_time, since, level\n"
-  for sponsor in sponsors:
-    csv.add "$#,$#,$#,$#,$#,$#,$#\n" % [
-      sponsor.logo.quote, sponsor.name.quote,
-      sponsor.url.quote, $sponsor.amount.int,
-      $sponsor.allTime.int, sponsor.since.format("MMM d, yyyy").quote,
-      $sponsor.amount.getLevel
-    ]
-  writeFile(filename, csv)
-  echo("Written csv file to ", filename)
-
-when isMainModule:
-  if paramCount() == 0:
-    quit("You need to specify the BountySource access token on the command\n" &
-      "line, you can find it by going onto https://www.bountysource.com/people/25278-dom96\n" &
-      "and looking at your browser's network inspector tab to see the token being\n" &
-      "sent to api.bountysource.com")
-
-  let token = paramStr(1)
-  let bountysource = newBountySource(team, token)
-
-  echo("Getting sponsors...")
-  let supporters = waitFor bountysource.getSupporters()
-  processSupporters(supporters)
-
-  echo("Generating sponsors list... (please be patient)")
-  var activeSponsors: seq[Sponsor] = @[]
-  var inactiveSponsors: seq[Sponsor] = @[]
-  for supporter in supporters:
-    let name = supporter["display_name"].getStr
-    var url = ""
-    let ghUser = waitFor getGithubUser(name)
-    if not ghUser.isNil:
-      if ghUser["blog"].kind != JNull:
-        url = ghUser["blog"].getStr
-      else:
-        url = ghUser["html_url"].getStr
-
-    if url.len > 0 and not url.startsWith("http"):
-      url = "http://" & url
-
-    let amount = supporter["monthly_amount"].getFNum()
-    # Only show URL when user donated at least $5.
-    if amount < 5:
-      url = ""
-
-    #let supporter = getSupporter(supporters,
-    #                             supportLevel["owner"]["display_name"].getStr)
-    #if supporter.isNil: continue
-    var logo = ""
-    if amount >= 75:
-      discard # TODO
-
-    let sponsor = Sponsor(name: name, url: url, logo: logo, amount: amount,
-        allTime: supporter["alltime_amount"].getFNum(),
-        since: parse(supporter["created_at"].getStr, "yyyy-MM-dd'T'hh:mm:ss")
-      )
-    if supporter["monthly_amount"].getFNum > 0.0:
-      activeSponsors.add(sponsor)
-    else:
-      inactiveSponsors.add(sponsor)
-
-  echo("Generated ", activeSponsors.len, " active sponsors")
-  echo("Generated ", inactiveSponsors.len, " inactive sponsors")
-  writeCsv(activeSponsors)
-  writeCsv(inactiveSponsors, "inactive_sponsors.new.csv")
diff --git a/web/bountysource.nim.cfg b/web/bountysource.nim.cfg
deleted file mode 100644
index 521e21de4..000000000
--- a/web/bountysource.nim.cfg
+++ /dev/null
@@ -1 +0,0 @@
--d:ssl
diff --git a/web/website.ini b/web/website.ini
index 13e9b97e8..b91e4003a 100644
--- a/web/website.ini
+++ b/web/website.ini
@@ -35,7 +35,7 @@ doc: "nimfix.rst;nimsuggest.rst;nep1.rst;nims.rst;contributing.rst"
 pdf: "manual.rst;lib.rst;tut1.rst;tut2.rst;nimc.rst;niminst.rst;gc.rst"
 srcdoc2: "system.nim;system/nimscript;pure/ospaths"
 srcdoc2: "core/macros;pure/marshal;core/typeinfo"
-srcdoc2: "impure/re;impure/nre;pure/typetraits"
+srcdoc2: "impure/re;impure/nre;pure/typetraits;../nimsuggest/sexp.nim"
 srcdoc2: "pure/concurrency/threadpool.nim;pure/concurrency/cpuinfo.nim"
 srcdoc: "system/threads.nim;system/channels.nim;js/dom"
 srcdoc2: "pure/os;pure/strutils;pure/math;pure/matchers;pure/algorithm"