summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md1
-rw-r--r--.gitignore4
-rw-r--r--changelog.md21
-rw-r--r--compiler/ast.nim26
-rw-r--r--compiler/ccgcalls.nim36
-rw-r--r--compiler/ccgexprs.nim297
-rw-r--r--compiler/ccgstmts.nim17
-rw-r--r--compiler/ccgtypes.nim35
-rw-r--r--compiler/cgen.nim21
-rw-r--r--compiler/cgendata.nim1
-rw-r--r--compiler/dfa.nim69
-rw-r--r--compiler/docgen.nim2
-rw-r--r--compiler/docgen2.nim4
-rw-r--r--compiler/extccomp.nim7
-rw-r--r--compiler/injectdestructors.nim106
-rw-r--r--compiler/jsgen.nim314
-rw-r--r--compiler/lambdalifting.nim46
-rw-r--r--compiler/liftdestructors.nim78
-rw-r--r--compiler/modules.nim34
-rw-r--r--compiler/packagehandling.nim14
-rw-r--r--compiler/ropes.nim7
-rw-r--r--compiler/semdata.nim6
-rw-r--r--compiler/semexprs.nim53
-rw-r--r--compiler/semmagic.nim35
-rw-r--r--compiler/sempass2.nim20
-rw-r--r--compiler/semstmts.nim52
-rw-r--r--compiler/semtempl.nim8
-rw-r--r--compiler/semtypes.nim4
-rw-r--r--compiler/semtypinst.nim25
-rw-r--r--compiler/sighashes.nim4
-rw-r--r--compiler/sigmatch.nim17
-rw-r--r--compiler/transf.nim2
-rw-r--r--compiler/trees.nim2
-rw-r--r--compiler/vmgen.nim47
-rw-r--r--doc/docgen.rst55
-rw-r--r--doc/manual.rst45
-rw-r--r--doc/manual_experimental.rst6
-rw-r--r--lib/core/macros.nim10
-rw-r--r--lib/core/runtime_v2.nim5
-rw-r--r--lib/core/seqs.nim41
-rw-r--r--lib/impure/db_mysql.nim12
-rw-r--r--lib/impure/db_odbc.nim12
-rw-r--r--lib/impure/db_postgres.nim10
-rw-r--r--lib/impure/db_sqlite.nim12
-rw-r--r--lib/impure/nre.nim21
-rw-r--r--lib/js/asyncjs.nim2
-rw-r--r--lib/pure/asyncdispatch.nim43
-rw-r--r--lib/pure/asyncftpclient.nim6
-rw-r--r--lib/pure/asynchttpserver.nim3
-rw-r--r--lib/pure/asyncmacro.nim2
-rw-r--r--lib/pure/asyncnet.nim8
-rw-r--r--lib/pure/base64.nim1
-rw-r--r--lib/pure/collections/hashcommon.nim68
-rw-r--r--lib/pure/collections/lists.nim18
-rw-r--r--lib/pure/collections/sequtils.nim16
-rw-r--r--lib/pure/collections/setimpl.nim153
-rw-r--r--lib/pure/collections/sets.nim473
-rw-r--r--lib/pure/collections/sharedtables.nim10
-rw-r--r--lib/pure/collections/tableimpl.nim111
-rw-r--r--lib/pure/collections/tables.nim271
-rw-r--r--lib/pure/dynlib.nim4
-rw-r--r--lib/pure/htmlgen.nim3
-rw-r--r--lib/pure/includes/osenv.nim2
-rw-r--r--lib/pure/includes/oserr.nim2
-rw-r--r--lib/pure/parsecfg.nim94
-rw-r--r--lib/pure/parseutils.nim183
-rw-r--r--lib/pure/random.nim22
-rw-r--r--lib/pure/streams.nim913
-rw-r--r--lib/pure/strformat.nim23
-rw-r--r--lib/pure/times.nim15
-rw-r--r--lib/pure/unittest.nim10
-rw-r--r--lib/system.nim46
-rw-r--r--lib/system/ansi_c.nim2
-rw-r--r--lib/system/fatal.nim2
-rw-r--r--lib/system/strmantle.nim40
-rw-r--r--lib/system/threads.nim5
-rw-r--r--nimdoc/testproject/expected/testproject.html4
-rw-r--r--nimdoc/testproject/testproject.nim2
-rw-r--r--testament/categories.nim44
-rw-r--r--testament/tester.nim60
-rw-r--r--tests/ccgbugs/t5701.nim10
-rw-r--r--tests/destructor/tdestructor.nim4
-rw-r--r--tests/destructor/tdestructor3.nim4
-rw-r--r--tests/destructor/tdont_return_unowned_from_owned.nim43
-rw-r--r--tests/destructor/tgcdestructors.nim39
-rw-r--r--tests/destructor/tobjfield_analysis.nim34
-rw-r--r--tests/destructor/towned_binary_tree.nim92
-rw-r--r--tests/destructor/twidgets.nim78
-rw-r--r--tests/destructor/twidgets_unown.nim68
-rw-r--r--tests/discard/tdiscardable.nim20
-rw-r--r--tests/macros/tnewproc.nim51
-rw-r--r--tests/modules/mnotuniquename.nim1
-rw-r--r--tests/modules/tnotuniquename.nim9
-rw-r--r--tests/modules/tnotuniquename/mnotuniquename.nim0
-rw-r--r--tests/modules/tnotuniquename2.nim1
-rw-r--r--tests/modules/tnotuniquename_dir/mnotuniquename.nim2
-rw-r--r--tests/types/tillegaltuplerecursiongeneric.nim2
-rw-r--r--tests/types/tillegaltuplerecursiongeneric2.nim9
-rw-r--r--tests/types/tillegaltyperecursion2.nim2
99 files changed, 3371 insertions, 1403 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 5fe7971a3..8a4f43123 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -38,6 +38,7 @@ Hello World!
 * Your Nim version (output of `nim -v`).
 * Was it working in the previous Nim releases?
 * A link to a related issue or discussion.
+* A project reference where (and how) the issue causes problems.
 -->
 ```
 $ nim -v
diff --git a/.gitignore b/.gitignore
index 9de4569bf..1da7348fd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,6 +52,8 @@ xcuserdata/
 /compiler/nim.dot
 /reject.json
 /run.json
+*.json
+/pkgstemp/**/*
 # for `nim doc foo.nim`
 /*.html
 lib/**/*.html
@@ -59,6 +61,8 @@ lib/**/*.html
 
 /testresults.json
 testament.db
+/tests/**/*.json
+/tests/**/*.js
 /csources
 dist/
 
diff --git a/changelog.md b/changelog.md
index f31d57541..adc35a079 100644
--- a/changelog.md
+++ b/changelog.md
@@ -47,11 +47,17 @@
 
 - `system.ValueError` now inherits from `system.CatchableError` instead of `system.Defect`.
 
-- The procs `parseutils.parseBiggsetInt`, `parseutils.parseInt`,
+- The procs `parseutils.parseBiggestInt`, `parseutils.parseInt`,
   `parseutils.parseBiggestUInt` and `parseutils.parseUInt` now raise a
   `ValueError` when the parsed integer is outside of the valid range.
   Previously they sometimes raised a `OverflowError` and sometimes returned `0`.
 
+- The procs `parseutils.parseBin`, `parseutils.parseOct` and `parseutils.parseHex`
+  were not clearing their `var` parameter `number` and used to push its value to
+  the left when storing the parsed string into it. Now they always set the value
+  of the parameter to `0` before storing the result of the parsing, unless the
+  string to parse is not valid and then the value of `number` is not changed.
+
 - `streams.StreamObject` now restricts its fields to only raise `system.Defect`,
   `system.IOError` and `system.OSError`.
   This change only affects custom stream implementations.
@@ -91,6 +97,7 @@
 - Custom types that should be supported by `strformat` (&) now need an
   explicit overload of `formatValue`.
 
+
 #### Breaking changes in the compiler
 
 - The compiler now implements the "generic symbol prepass" for `when` statements
@@ -107,6 +114,7 @@ proc enumToString*(enums: openArray[enum]): string =
 ```
 
 - ``discard x`` is now illegal when `x` is a function symbol.
+
 - Implicit imports via ``--import: module`` in a config file are now restricted
   to the main package.
 
@@ -152,6 +160,9 @@ proc enumToString*(enums: openArray[enum]): string =
 
 - Added `system.default`.
 
+- Added `sequtils.items` for closure iterators, allows closure iterators 
+  to be used by the the mapIt, filterIt, allIt, anyIt, etc.
+
 
 ### Library changes
 
@@ -179,6 +190,7 @@ proc enumToString*(enums: openArray[enum]): string =
   differently.
 
 - `securehash` is moved to `lib/deprecated`
+
 - The switch ``-d:useWinAnsi`` is not supported anymore.
 
 
@@ -202,14 +214,19 @@ proc enumToString*(enums: openArray[enum]): string =
   it's more recognizable and allows tools like github to recognize it as Nim,
   see [#9647](https://github.com/nim-lang/Nim/issues/9647).
   The previous extension will continue to work.
+
 - Pragma syntax is now consistent. Previous syntax where type pragmas did not
   follow the type name is now deprecated. Also pragma before generic parameter
   list is deprecated to be consistent with how pragmas are used with a proc. See
   [#8514](https://github.com/nim-lang/Nim/issues/8514) and
   [#1872](https://github.com/nim-lang/Nim/issues/1872) for further details.
 
+- Hash sets and tables are initialized by default. The explicit `initHashSet`,
+  `initTable`, etc. are not needed anymore.
+
 
 ### Tool changes
+
 - `jsondoc` now include a `moduleDescription` field with the module
   description. `jsondoc0` shows comments as it's own objects as shown in the
   documentation.
@@ -218,6 +235,7 @@ proc enumToString*(enums: openArray[enum]): string =
 
 
 ### Compiler changes
+
 - The deprecated `fmod` proc is now unavailable on the VM'.
 - A new `--outdir` option was added.
 - The compiled JavaScript file for the project produced by executing `nim js`
@@ -226,4 +244,5 @@ proc enumToString*(enums: openArray[enum]): string =
   The compiler also provides a new more flexible API for handling the
   hot code reloading events in the code.
 
+
 ### Bugfixes
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 75e67ac67..aeb19ae1a 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -644,7 +644,7 @@ type
     mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
     mNewString, mNewStringOfCap, mParseBiggestFloat,
     mMove, mWasMoved, mDestroy,
-    mDefault, mAccessEnv, mReset,
+    mDefault, mUnown, mAccessEnv, mReset,
     mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs,
     mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
     mOrdinal,
@@ -877,6 +877,13 @@ type
 
   TTypeSeq* = seq[PType]
   TLockLevel* = distinct int16
+
+  TTypeAttachedOp* = enum ## as usual, order is important here
+    attachedDestructor,
+    attachedAsgn,
+    attachedSink,
+    attachedDeepCopy
+
   TType* {.acyclic.} = object of TIdObj # \
                               # types are identical iff they have the
                               # same id; there may be multiple copies of a type
@@ -897,12 +904,7 @@ type
     owner*: PSym              # the 'owner' of the type
     sym*: PSym                # types have the sym associated with them
                               # it is used for converting types to strings
-    destructor*: PSym         # destructor. warning: nil here may not necessary
-                              # mean that there is no destructor.
-                              # see instantiateDestructor in semdestruct.nim
-    deepCopy*: PSym           # overriden 'deepCopy' operation
-    assignment*: PSym         # overriden '=' operation
-    sink*: PSym               # overriden '=sink' operation
+    attachedOps*: array[TTypeAttachedOp, PSym] # destructors, etc.
     methods*: seq[(int,PSym)] # attached methods
     size*: BiggestInt         # the size of the type in bytes
                               # -1 means that the size is unkwown
@@ -1275,6 +1277,7 @@ const
   UnspecifiedLockLevel* = TLockLevel(-1'i16)
   MaxLockLevel* = 1000'i16
   UnknownLockLevel* = TLockLevel(1001'i16)
+  AttachedOpToStr*: array[TTypeAttachedOp, string] = ["=destroy", "=", "=sink", "=deepcopy"]
 
 proc `$`*(x: TLockLevel): string =
   if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>"
@@ -1341,10 +1344,7 @@ proc assignType*(dest, src: PType) =
   dest.n = src.n
   dest.size = src.size
   dest.align = src.align
-  dest.destructor = src.destructor
-  dest.deepCopy = src.deepCopy
-  dest.sink = src.sink
-  dest.assignment = src.assignment
+  dest.attachedOps = src.attachedOps
   dest.lockLevel = src.lockLevel
   # this fixes 'type TLock = TSysLock':
   if src.sym != nil:
@@ -1831,3 +1831,7 @@ proc addParam*(procType: PType; param: PSym) =
   param.position = procType.len-1
   addSon(procType.n, newSymNode(param))
   rawAddSon(procType, param.typ)
+
+template destructor*(t: PType): PSym = t.attachedOps[attachedDestructor]
+template assignment*(t: PType): PSym = t.attachedOps[attachedAsgn]
+template asink*(t: PType): PSym = t.attachedOps[attachedSink]
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index e46e2c47d..1e856ad73 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -46,13 +46,23 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
         genAssignment(p, d, tmp, {}) # no need for deep copying
     else:
       add(pl, ~")")
-      if p.module.compileToCpp and lfSingleUse in d.flags:
-        # do not generate spurious temporaries for C++! For C we're better off
-        # with them to prevent undefined behaviour and because the codegen
-        # is free to emit expressions multiple times!
-        d.k = locCall
-        d.r = pl
-        excl d.flags, lfSingleUse
+      if p.module.compileToCpp:
+        if lfSingleUse in d.flags:
+          # do not generate spurious temporaries for C++! For C we're better off
+          # with them to prevent undefined behaviour and because the codegen
+          # is free to emit expressions multiple times!
+          d.k = locCall
+          d.r = pl
+          excl d.flags, lfSingleUse
+        else:
+          if d.k == locNone and p.splitDecls == 0:
+            getTempCpp(p, typ.sons[0], d, pl)
+          else:
+            if d.k == locNone: getTemp(p, typ.sons[0], d)
+            var list: TLoc
+            initLoc(list, locCall, d.lode, OnUnknown)
+            list.r = pl
+            genAssignment(p, d, list, {}) # no need for deep copying
       else:
         if d.k == locNone: getTemp(p, typ.sons[0], d)
         assert(d.t != nil)        # generate an assignment to d:
@@ -218,10 +228,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
     genParamLoop(pl)
 
   template genCallPattern {.dirty.} =
-    lineF(p, cpsStmts, callPattern & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
+    if tfIterator in typ.flags:
+      lineF(p, cpsStmts, PatIter & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
+    else:
+      lineF(p, cpsStmts, PatProc & ";$n", [rdLoc(op), pl, pl.addComma, rawProc])
 
   let rawProc = getRawProcType(p, typ)
-  let callPattern = if tfIterator in typ.flags: PatIter else: PatProc
   if typ.sons[0] != nil:
     if isInvalidReturnType(p.config, typ.sons[0]):
       if sonsLen(ri) > 1: add(pl, ~", ")
@@ -246,7 +258,11 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
       assert(d.t != nil)        # generate an assignment to d:
       var list: TLoc
       initLoc(list, locCall, d.lode, OnUnknown)
-      list.r = callPattern % [rdLoc(op), pl, pl.addComma, rawProc]
+      if tfIterator in typ.flags:
+        list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
+      else:
+        list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
+
       genAssignment(p, d, list, {}) # no need for deep copying
   else:
     genCallPattern()
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 9f4d57359..e03c4b1c1 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -99,17 +99,17 @@ proc bitSetToWord(s: TBitSet, size: int): BiggestInt =
     if j < len(s): result = result or (ze64(s[j]) shl (j * 8))
 
 proc genRawSetData(cs: TBitSet, size: int): Rope =
-  var frmt: FormatStr
   if size > 8:
     result = "{$n" % []
     for i in countup(0, size - 1):
       if i < size - 1:
         # not last iteration?
-        if (i + 1) mod 8 == 0: frmt = "0x$1,$n"
-        else: frmt = "0x$1, "
+        if (i + 1) mod 8 == 0:
+          addf(result, "0x$1,$n", [rope(toHex(ze64(cs[i]), 2))])
+        else:
+          addf(result, "0x$1, ", [rope(toHex(ze64(cs[i]), 2))])
       else:
-        frmt = "0x$1}$n"
-      addf(result, frmt, [rope(toHex(ze64(cs[i]), 2))])
+        addf(result, "0x$1}$n", [rope(toHex(ze64(cs[i]), 2))])
   else:
     result = intLiteral(bitSetToWord(cs, size))
     #  result := rope('0x' + ToHex(bitSetToWord(cs, size), size * 2))
@@ -510,10 +510,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
       "mulInt64", "divInt64", "modInt64",
       "addInt64", "subInt64"
     ]
-    opr: array[mAddI..mPred, string] = [
-      "($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)",
-      "($#)($# / $#)", "($#)($# % $#)",
-      "($#)($# + $#)", "($#)($# - $#)"]
+    opr: array[mAddI..mPred, string] = ["+", "-", "*", "/", "%", "+", "-"]
   var a, b: TLoc
   assert(e.sons[1].typ != nil)
   assert(e.sons[2].typ != nil)
@@ -523,7 +520,7 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   # later via 'chckRange'
   let t = e.typ.skipTypes(abstractRange)
   if optOverflowCheck notin p.options:
-    let res = opr[m] % [getTypeDesc(p.module, e.typ), rdLoc(a), rdLoc(b)]
+    let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
     putIntoDest(p, d, e, res)
   else:
     let res = binaryArithOverflowRaw(p, t, a, b,
@@ -531,11 +528,6 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
     putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])
 
 proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
-  const
-    opr: array[mUnaryMinusI..mAbsI, string] = [
-      mUnaryMinusI: "((NI$2)-($1))",
-      mUnaryMinusI64: "-($1)",
-      mAbsI: "($1 > 0? ($1) : -($1))"]
   var
     a: TLoc
     t: PType
@@ -545,54 +537,17 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   if optOverflowCheck in p.options:
     linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n",
             [rdLoc(a), intLiteral(firstOrd(p.config, t))])
-  putIntoDest(p, d, e, opr[m] % [rdLoc(a), rope(getSize(p.config, t) * 8)])
+  case m
+  of mUnaryMinusI:
+    putIntoDest(p, d, e, "((NI$2)-($1))" % [rdLoc(a), rope(getSize(p.config, t) * 8)])
+  of mUnaryMinusI64:
+    putIntoDest(p, d, e, "-($1)" % [rdLoc(a)])
+  of mAbsI:
+    putIntoDest(p, d, e, "($1 > 0? ($1) : -($1))" % [rdLoc(a)])
+  else:
+    assert(false, $m)
 
 proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
-  const
-    binArithTab: array[mAddF64..mXor, string] = [
-      "(($4)($1) + ($4)($2))", # AddF64
-      "(($4)($1) - ($4)($2))", # SubF64
-      "(($4)($1) * ($4)($2))", # MulF64
-      "(($4)($1) / ($4)($2))", # DivF64
-      "($4)((NU$5)($1) >> (NU$3)($2))", # ShrI
-      "($4)((NU$3)($1) << (NU$3)($2))", # ShlI
-      "($4)((NI$3)($1) >> (NU$3)($2))", # AshrI
-      "($4)($1 & $2)",      # BitandI
-      "($4)($1 | $2)",      # BitorI
-      "($4)($1 ^ $2)",      # BitxorI
-      "(($1 <= $2) ? $1 : $2)", # MinI
-      "(($1 >= $2) ? $1 : $2)", # MaxI
-      "(($1 <= $2) ? $1 : $2)", # MinF64
-      "(($1 >= $2) ? $1 : $2)", # MaxF64
-      "($4)((NU$3)($1) + (NU$3)($2))", # AddU
-      "($4)((NU$3)($1) - (NU$3)($2))", # SubU
-      "($4)((NU$3)($1) * (NU$3)($2))", # MulU
-      "($4)((NU$3)($1) / (NU$3)($2))", # DivU
-      "($4)((NU$3)($1) % (NU$3)($2))", # ModU
-      "($1 == $2)",           # EqI
-      "($1 <= $2)",           # LeI
-      "($1 < $2)",            # LtI
-      "($1 == $2)",           # EqF64
-      "($1 <= $2)",           # LeF64
-      "($1 < $2)",            # LtF64
-      "((NU$3)($1) <= (NU$3)($2))", # LeU
-      "((NU$3)($1) < (NU$3)($2))", # LtU
-      "((NU64)($1) <= (NU64)($2))", # LeU64
-      "((NU64)($1) < (NU64)($2))", # LtU64
-      "($1 == $2)",           # EqEnum
-      "($1 <= $2)",           # LeEnum
-      "($1 < $2)",            # LtEnum
-      "((NU8)($1) == (NU8)($2))", # EqCh
-      "((NU8)($1) <= (NU8)($2))", # LeCh
-      "((NU8)($1) < (NU8)($2))", # LtCh
-      "($1 == $2)",           # EqB
-      "($1 <= $2)",           # LeB
-      "($1 < $2)",            # LtB
-      "($1 == $2)",           # EqRef
-      "($1 == $2)",           # EqPtr
-      "($1 <= $2)",           # LePtr
-      "($1 < $2)",            # LtPtr
-      "($1 != $2)"]           # Xor
   var
     a, b: TLoc
     s, k: BiggestInt
@@ -603,9 +558,59 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   # BUGFIX: cannot use result-type here, as it may be a boolean
   s = max(getSize(p.config, a.t), getSize(p.config, b.t)) * 8
   k = getSize(p.config, a.t) * 8
-  putIntoDest(p, d, e,
-              binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s),
-                                      getSimpleTypeDesc(p.module, e.typ), rope(k)])
+
+  template applyFormat(frmt: untyped) =
+    putIntoDest(p, d, e, frmt % [
+      rdLoc(a), rdLoc(b), rope(s),
+      getSimpleTypeDesc(p.module, e.typ), rope(k)]
+    )
+
+  case op
+  of mAddF64: applyFormat("(($4)($1) + ($4)($2))")
+  of mSubF64: applyFormat("(($4)($1) - ($4)($2))")
+  of mMulF64: applyFormat("(($4)($1) * ($4)($2))")
+  of mDivF64: applyFormat("(($4)($1) / ($4)($2))")
+  of mShrI: applyFormat("($4)((NU$5)($1) >> (NU$3)($2))")
+  of mShlI: applyFormat("($4)((NU$3)($1) << (NU$3)($2))")
+  of mAshrI: applyFormat("($4)((NI$3)($1) >> (NU$3)($2))")
+  of mBitandI: applyFormat("($4)($1 & $2)")
+  of mBitorI: applyFormat("($4)($1 | $2)")
+  of mBitxorI: applyFormat("($4)($1 ^ $2)")
+  of mMinI: applyFormat("(($1 <= $2) ? $1 : $2)")
+  of mMaxI: applyFormat("(($1 >= $2) ? $1 : $2)")
+  of mMinF64: applyFormat("(($1 <= $2) ? $1 : $2)")
+  of mMaxF64: applyFormat("(($1 >= $2) ? $1 : $2)")
+  of mAddU: applyFormat("($4)((NU$3)($1) + (NU$3)($2))")
+  of mSubU: applyFormat("($4)((NU$3)($1) - (NU$3)($2))")
+  of mMulU: applyFormat("($4)((NU$3)($1) * (NU$3)($2))")
+  of mDivU: applyFormat("($4)((NU$3)($1) / (NU$3)($2))")
+  of mModU: applyFormat("($4)((NU$3)($1) % (NU$3)($2))")
+  of mEqI: applyFormat("($1 == $2)")
+  of mLeI: applyFormat("($1 <= $2)")
+  of mLtI: applyFormat("($1 < $2)")
+  of mEqF64: applyFormat("($1 == $2)")
+  of mLeF64: applyFormat("($1 <= $2)")
+  of mLtF64: applyFormat("($1 < $2)")
+  of mLeU: applyFormat("((NU$3)($1) <= (NU$3)($2))")
+  of mLtU: applyFormat("((NU$3)($1) < (NU$3)($2))")
+  of mLeU64: applyFormat("((NU64)($1) <= (NU64)($2))")
+  of mLtU64: applyFormat("((NU64)($1) < (NU64)($2))")
+  of mEqEnum: applyFormat("($1 == $2)")
+  of mLeEnum: applyFormat("($1 <= $2)")
+  of mLtEnum: applyFormat("($1 < $2)")
+  of mEqCh: applyFormat("((NU8)($1) == (NU8)($2))")
+  of mLeCh: applyFormat("((NU8)($1) <= (NU8)($2))")
+  of mLtCh: applyFormat("((NU8)($1) < (NU8)($2))")
+  of mEqB: applyFormat("($1 == $2)")
+  of mLeB: applyFormat("($1 <= $2)")
+  of mLtB: applyFormat("($1 < $2)")
+  of mEqRef: applyFormat("($1 == $2)")
+  of mEqUntracedRef: applyFormat("($1 == $2)")
+  of mLePtr: applyFormat("($1 <= $2)")
+  of mLtPtr: applyFormat("($1 < $2)")
+  of mXor: applyFormat("($1 != $2)")
+  else:
+    assert(false, $op)
 
 proc genEqProc(p: BProc, e: PNode, d: var TLoc) =
   var a, b: TLoc
@@ -628,7 +633,8 @@ proc genIsNil(p: BProc, e: PNode, d: var TLoc) =
 
 proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   const
-    unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not
+    unArithTab: array[mNot..mToBiggestInt, string] = [
+      "!($1)", # Not
       "$1",                   # UnaryPlusI
       "($3)((NU$2) ~($1))",   # BitnotI
       "$1",                   # UnaryPlusF64
@@ -654,10 +660,58 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   assert(e.sons[1].typ != nil)
   initLocExpr(p, e.sons[1], a)
   t = skipTypes(e.typ, abstractRange)
-  putIntoDest(p, d, e,
-              unArithTab[op] % [rdLoc(a), rope(getSize(p.config, t) * 8),
+
+
+  template applyFormat(frmt: untyped) =
+    putIntoDest(p, d, e, frmt % [rdLoc(a), rope(getSize(p.config, t) * 8),
                 getSimpleTypeDesc(p.module, e.typ)])
 
+
+  case op
+  of mNot:
+    applyFormat("!($1)")
+  of mUnaryPlusI:
+    applyFormat("$1")
+  of mBitnotI:
+    applyFormat("($3)((NU$2) ~($1))")
+  of mUnaryPlusF64:
+    applyFormat("$1")
+  of mUnaryMinusF64:
+    applyFormat("-($1)")
+  of mAbsF64:
+    applyFormat("($1 < 0? -($1) : ($1))")
+    # BUGFIX: fabs() makes problems for Tiny C
+  of mZe8ToI:
+    applyFormat("(($3)(NU)(NU8)($1))")
+  of mZe8ToI64:
+    applyFormat("(($3)(NU64)(NU8)($1))")
+  of mZe16ToI:
+    applyFormat("(($3)(NU)(NU16)($1))")
+  of mZe16ToI64:
+    applyFormat("(($3)(NU64)(NU16)($1))")
+  of mZe32ToI64:
+    applyFormat("(($3)(NU64)(NU32)($1))")
+  of mZeIToI64:
+    applyFormat("(($3)(NU64)(NU)($1))")
+  of mToU8:
+    applyFormat("(($3)(NU8)(NU)($1))")
+  of mToU16:
+    applyFormat("(($3)(NU16)(NU)($1))")
+  of mToU32:
+    applyFormat("(($3)(NU32)(NU64)($1))")
+  of mToFloat:
+    applyFormat("((double) ($1))")
+  of mToBiggestFloat:
+    applyFormat("((double) ($1))")
+  of mToInt:
+    applyFormat("float64ToInt32($1)")
+  of mToBiggestInt:
+    applyFormat("float64ToInt64($1)")
+  else:
+    assert false, $op
+
+
+
 proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
   result = p.module.compileToCpp and
       skipTypes(typ, abstractInstOwned).kind == tyVar and
@@ -942,6 +996,23 @@ proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
   of tyTuple: genTupleElem(p, n, d)
   else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
 
+proc isSimpleExpr(n: PNode): bool =
+  # calls all the way down --> can stay expression based
+  case n.kind
+  of nkCallKinds, nkDotExpr, nkPar, nkTupleConstr,
+      nkObjConstr, nkBracket, nkCurly, nkHiddenDeref, nkDerefExpr, nkHiddenAddr,
+      nkHiddenStdConv, nkHiddenSubConv, nkConv, nkAddr:
+    for c in n:
+      if not isSimpleExpr(c): return false
+    result = true
+  of nkStmtListExpr:
+    for i in 0..n.len-2:
+      if n[i].kind notin {nkCommentStmt, nkEmpty}: return false
+    result = isSimpleExpr(n.lastSon)
+  else:
+    if n.isAtom:
+      result = true
+
 proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   # how to generate code?
   #  'expr1 and expr2' becomes:
@@ -963,24 +1034,41 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   # tmp = a
   # end:
   # a = tmp
-  var
-    L: TLabel
-    tmp: TLoc
-  getTemp(p, e.typ, tmp)      # force it into a temp!
-  inc p.splitDecls
-  expr(p, e.sons[1], tmp)
-  L = getLabel(p)
-  if m == mOr:
-    lineF(p, cpsStmts, "if ($1) goto $2;$n", [rdLoc(tmp), L])
-  else:
-    lineF(p, cpsStmts, "if (!($1)) goto $2;$n", [rdLoc(tmp), L])
-  expr(p, e.sons[2], tmp)
-  fixLabel(p, L)
-  if d.k == locNone:
-    d = tmp
+  when false:
+    #if isSimpleExpr(e) and p.module.compileToCpp:
+    var tmpA, tmpB: TLoc
+    #getTemp(p, e.typ, tmpA)
+    #getTemp(p, e.typ, tmpB)
+    initLocExprSingleUse(p, e.sons[1], tmpA)
+    initLocExprSingleUse(p, e.sons[2], tmpB)
+    tmpB.k = locExpr
+    if m == mOr:
+      tmpB.r = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))"
+    else:
+      tmpB.r = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))"
+    if d.k == locNone:
+      d = tmpB
+    else:
+      genAssignment(p, d, tmpB, {})
   else:
-    genAssignment(p, d, tmp, {}) # no need for deep copying
-  dec p.splitDecls
+    var
+      L: TLabel
+      tmp: TLoc
+    getTemp(p, e.typ, tmp)      # force it into a temp!
+    inc p.splitDecls
+    expr(p, e.sons[1], tmp)
+    L = getLabel(p)
+    if m == mOr:
+      lineF(p, cpsStmts, "if ($1) goto $2;$n", [rdLoc(tmp), L])
+    else:
+      lineF(p, cpsStmts, "if (!($1)) goto $2;$n", [rdLoc(tmp), L])
+    expr(p, e.sons[2], tmp)
+    fixLabel(p, L)
+    if d.k == locNone:
+      d = tmp
+    else:
+      genAssignment(p, d, tmp, {}) # no need for deep copying
+    dec p.splitDecls
 
 proc genEcho(p: BProc, n: PNode) =
   # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)``
@@ -1675,7 +1763,7 @@ proc fewCmps(conf: ConfigRef; s: PNode): bool =
   else:
     result = sonsLen(s) <= 8  # 8 seems to be a good value
 
-proc binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) =
+template binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) =
   putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(p.config, b, a.t)])
 
 proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
@@ -1686,7 +1774,7 @@ proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
   of 8: binaryExprIn(p, e, a, b, d, "(($1 &((NU64)1<<((NU)($2)&63U)))!=0)")
   else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)")
 
-proc binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) =
+template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   var a, b: TLoc
   assert(d.k == locNone)
   initLocExpr(p, e.sons[1], a)
@@ -1753,13 +1841,19 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of 1, 2, 4, 8:
     case op
     of mIncl:
-      var ts = "NU" & $(size * 8)
-      binaryStmtInExcl(p, e, d,
-          "$1 |= ((" & ts & ")1)<<(($2)%(sizeof(" & ts & ")*8));$n")
+      case size
+      of 1: binaryStmtInExcl(p, e, d, "$1 |= ((NU8)1)<<(($2) & 7);$n")
+      of 2: binaryStmtInExcl(p, e, d, "$1 |= ((NU16)1)<<(($2) & 15);$n")
+      of 4: binaryStmtInExcl(p, e, d, "$1 |= ((NU32)1)<<(($2) & 31);$n")
+      of 8: binaryStmtInExcl(p, e, d, "$1 |= ((NU64)1)<<(($2) & 63);$n")
+      else: assert(false, $size)
     of mExcl:
-      var ts = "NU" & $(size * 8)
-      binaryStmtInExcl(p, e, d, "$1 &= ~(((" & ts & ")1) << (($2) % (sizeof(" &
-          ts & ")*8)));$n")
+      case size
+      of 1: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU8)1) << (($2) & 7));$n")
+      of 2: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU16)1) << (($2) & 15));$n")
+      of 4: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU32)1) << (($2) & 31));$n")
+      of 8: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU64)1) << (($2) & 63));$n")
+      else: assert(false, $size)
     of mCard:
       if size <= 4: unaryExprChar(p, e, d, "#countBits32($1)")
       else: unaryExprChar(p, e, d, "#countBits64($1)")
@@ -1824,14 +1918,14 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
   var a: TLoc
   initLocExpr(p, e.sons[1], a)
   let etyp = skipTypes(e.typ, abstractRange+{tyOwned})
+  let srcTyp = skipTypes(e.sons[1].typ, abstractRange)
   if etyp.kind in ValueTypes and lfIndirect notin a.flags:
     putIntoDest(p, d, e, "(*($1*) ($2))" %
         [getTypeDesc(p.module, e.typ), addrLoc(p.config, a)], a.storage)
-  elif etyp.kind == tyProc and etyp.callConv == ccClosure:
+  elif etyp.kind == tyProc and etyp.callConv == ccClosure and srcTyp.callConv != ccClosure:
     putIntoDest(p, d, e, "(($1) ($2))" %
         [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
   else:
-    let srcTyp = skipTypes(e.sons[1].typ, abstractRange)
     # C++ does not like direct casts from pointer to shorter integral types
     if srcTyp.kind in {tyPtr, tyPointer} and etyp.kind in IntegralTypes:
       putIntoDest(p, d, e, "(($1) (ptrdiff_t) ($2))" %
@@ -2085,7 +2179,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)")
   of mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($1)")
   of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
-  of mStrToStr: expr(p, e.sons[1], d)
+  of mStrToStr, mUnown: expr(p, e.sons[1], d)
   of mEnumToStr:
     if optNimV2 in p.config.globalOptions:
       genEnumToStr(p, e, d)
@@ -2228,14 +2322,14 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
           initLocExpr(p, it.sons[0], a)
           initLocExpr(p, it.sons[1], b)
           lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
-              "$2 |=((" & ts & ")(1)<<(($1)%(sizeof(" & ts & ")*8)));$n", [
+              "$2 |=(($5)(1)<<(($1)%(sizeof($5)*8)));$n", [
               rdLoc(idx), rdLoc(d), rdSetElemLoc(p.config, a, e.typ),
-              rdSetElemLoc(p.config, b, e.typ)])
+              rdSetElemLoc(p.config, b, e.typ), rope(ts)])
         else:
           initLocExpr(p, it, a)
           lineF(p, cpsStmts,
-               "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n",
-               [rdLoc(d), rdSetElemLoc(p.config, a, e.typ)])
+               "$1 |=(($3)(1)<<(($2)%(sizeof($3)*8)));$n",
+               [rdLoc(d), rdSetElemLoc(p.config, a, e.typ), rope(ts)])
 
 proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
   var rec: TLoc
@@ -2609,7 +2703,10 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
             genProc(p.module, prc)
   of nkParForStmt: genParForStmt(p, n)
   of nkState: genState(p, n)
-  of nkGotoState: genGotoState(p, n)
+  of nkGotoState:
+    # simply never set it back to 0 here from here on...
+    inc p.splitDecls
+    genGotoState(p, n)
   of nkBreakState: genBreakState(p, n, d)
   else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
 
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 386bedae9..abddb7c6c 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -207,7 +207,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
     let tryStmt = p.nestedTryStmts.pop
     if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
       # Pop safe points generated by try
-      if not tryStmt.inExcept and not isDefined(p.config, "nimQuirky"):
+      if not tryStmt.inExcept:
         linefmt(p, cpsStmts, "#popSafePoint();$n", [])
 
     # Pop this try-stmt of the list of nested trys
@@ -227,8 +227,9 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
   if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
     # Pop exceptions that was handled by the
     # except-blocks we are in
-    for i in countdown(howManyExcepts-1, 0):
-      linefmt(p, cpsStmts, "#popCurrentException();$n", [])
+    if not p.noSafePoints:
+      for i in countdown(howManyExcepts-1, 0):
+        linefmt(p, cpsStmts, "#popCurrentException();$n", [])
 
 proc genGotoState(p: BProc, n: PNode) =
   # we resist the temptation to translate it into duff's device as it later
@@ -449,7 +450,7 @@ proc genReturnStmt(p: BProc, t: PNode) =
   blockLeaveActions(p,
     howManyTrys    = p.nestedTryStmts.len,
     howManyExcepts = p.inExceptBlockLen)
-  if (p.finallySafePoints.len > 0) and not isDefined(p.config, "nimQuirky"):
+  if (p.finallySafePoints.len > 0) and not p.noSafePoints:
     # If we're in a finally block, and we came here by exception
     # consume it before we return.
     var safePoint = p.finallySafePoints[p.finallySafePoints.len-1]
@@ -1004,6 +1005,8 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
       (t.kind == nkHiddenTryStmt and sfSystemModule in p.module.module.flags)
   if not quirkyExceptions:
     p.module.includeHeader("<setjmp.h>")
+  else:
+    p.noSafePoints = true
   genLineDir(p, t)
   discard cgsym(p.module, "Exception")
   var safePoint: Rope
@@ -1021,7 +1024,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
       linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
     startBlock(p, "if ($1.status == 0) {$n", [safePoint])
   var length = sonsLen(t)
-  add(p.nestedTryStmts, (t, false))
+  add(p.nestedTryStmts, (t, quirkyExceptions))
   expr(p, t.sons[0], d)
   if not quirkyExceptions:
     linefmt(p, cpsStmts, "#popSafePoint();$n", [])
@@ -1135,9 +1138,9 @@ proc genAsmStmt(p: BProc, t: PNode) =
   # work:
   if p.prc == nil:
     # top level asm statement?
-    addf(p.module.s[cfsProcHeaders], CC[p.config.cCompiler].asmStmtFrmt, [s])
+    add(p.module.s[cfsProcHeaders], runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s]))
   else:
-    lineF(p, cpsStmts, CC[p.config.cCompiler].asmStmtFrmt, [s])
+    add(p.s(cpsStmts), indentLine(p, runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])))
 
 proc determineSection(n: PNode): TCFileSection =
   result = cfsProcHeaders
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index fb0f7dbf4..facd8b3d4 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -338,12 +338,17 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope =
     if result == nil: result = cacheGetType(m.typeCache, sig)
 
 proc structOrUnion(t: PType): Rope =
+  let cachedUnion {.global.} = rope("union")
+  let cachedStruct {.global.} = rope("struct")
   let t = t.skipTypes({tyAlias, tySink})
-  (if tfUnion in t.flags: rope("union") else: rope("struct"))
+  if tfUnion in t.flags: cachedUnion
+  else: cachedStruct
 
-proc getForwardStructFormat(m: BModule): string =
-  if m.compileToCpp: result = "$1 $2;$n"
-  else: result = "typedef $1 $2 $2;$n"
+proc addForwardStructFormat(m: BModule, structOrUnion: Rope, typename: Rope) =
+  if m.compileToCpp:
+    m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename]
+  else:
+    m.s[cfsForwardTypes].addf "typedef $1 $2 $2;$n", [structOrUnion, typename]
 
 proc seqStar(m: BModule): string =
   if m.config.selectedGC == gcDestructors: result = ""
@@ -360,8 +365,7 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
     result = getTypeName(m, typ, sig)
     m.forwTypeCache[sig] = result
     if not isImportedType(concrete):
-      addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
-          [structOrUnion(typ), result])
+      addForwardStructFormat(m, structOrUnion(typ), result)
     else:
       pushType(m, concrete)
     doAssert m.forwTypeCache[sig] == result
@@ -733,8 +737,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
     if result == nil:
       result = getTypeName(m, origTyp, sig)
       if not isImportedType(t):
-        addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
-            [structOrUnion(t), result])
+        addForwardStructFormat(m, structOrUnion(t), result)
       m.forwTypeCache[sig] = result
     assert(cacheGetType(m.typeCache, sig) == nil)
     m.typeCache[sig] = result & seqStar(m)
@@ -845,8 +848,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
         result = getTypeName(m, origTyp, sig)
         m.forwTypeCache[sig] = result
         if not isImportedType(t):
-          addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
-             [structOrUnion(t), result])
+          addForwardStructFormat(m, structOrUnion(t), result)
         assert m.forwTypeCache[sig] == result
       m.typeCache[sig] = result # always call for sideeffects:
       if not incompleteType(t):
@@ -905,7 +907,8 @@ proc finishTypeDescriptions(m: BModule) =
     discard getTypeDesc(m, m.typeStack[i])
     inc(i)
 
-template cgDeclFrmt*(s: PSym): string = s.constraint.strVal
+template cgDeclFrmt*(s: PSym): string =
+  s.constraint.strVal
 
 proc isReloadable(m: BModule, prc: PSym): bool =
   return m.hcrOn and sfNonReloadable notin prc.flags
@@ -943,7 +946,7 @@ proc genProcHeader(m: BModule, prc: PSym, asPtr: bool = false): Rope =
          params])
   else:
     let asPtrStr = if asPtr: (rope("(*") & name & ")") else: name
-    result = prc.cgDeclFrmt % [rettype, asPtrStr, params]
+    result = runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params])
 
 # ------------------ type info generation -------------------------------------
 
@@ -1346,10 +1349,10 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
     # results are not deterministic!
     genTupleInfo(m, t, origType, result, info)
   else: internalError(m.config, "genTypeInfo(" & $t.kind & ')')
-  if t.deepCopy != nil:
-    genDeepCopyProc(m, t.deepCopy, result)
-  elif origType.deepCopy != nil:
-    genDeepCopyProc(m, origType.deepCopy, result)
+  if t.attachedOps[attachedDeepCopy] != nil:
+    genDeepCopyProc(m, t.attachedOps[attachedDeepCopy], result)
+  elif origType.attachedOps[attachedDeepCopy] != nil:
+    genDeepCopyProc(m, origType.attachedOps[attachedDeepCopy], result)
   result = prefixTI.rope & result & ")".rope
 
 proc genTypeSection(m: BModule, n: PNode) =
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 92cc3819b..9bc3353dc 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -206,15 +206,15 @@ proc indentLine(p: BProc, r: Rope): Rope =
     prepend(result, "\t".rope)
 
 template appcg(m: BModule, c: var Rope, frmt: FormatStr,
-           args: varargs[untyped]) =
+           args: untyped) =
   add(c, ropecg(m, frmt, args))
 
 template appcg(m: BModule, sec: TCFileSection, frmt: FormatStr,
-           args: varargs[untyped]) =
+           args: untyped) =
   add(m.s[sec], ropecg(m, frmt, args))
 
 template appcg(p: BProc, sec: TCProcSection, frmt: FormatStr,
-           args: varargs[untyped]) =
+           args: untyped) =
   add(p.s(sec), ropecg(p.module, frmt, args))
 
 template line(p: BProc, sec: TCProcSection, r: Rope) =
@@ -224,7 +224,7 @@ template line(p: BProc, sec: TCProcSection, r: string) =
   add(p.s(sec), indentLine(p, r.rope))
 
 template lineF(p: BProc, sec: TCProcSection, frmt: FormatStr,
-              args: openarray[Rope]) =
+              args: untyped) =
   add(p.s(sec), indentLine(p, frmt % args))
 
 template lineCg(p: BProc, sec: TCProcSection, frmt: FormatStr,
@@ -440,6 +440,15 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
   result.flags = {}
   constructLoc(p, result, not needsInit)
 
+proc getTempCpp(p: BProc, t: PType, result: var TLoc; value: Rope) =
+  inc(p.labels)
+  result.r = "T" & rope(p.labels) & "_"
+  linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t), result.r, value])
+  result.k = locTemp
+  result.lode = lodeTyp t
+  result.storage = OnStack
+  result.flags = {}
+
 proc getIntTemp(p: BProc, result: var TLoc) =
   inc(p.labels)
   result.r = "T" & rope(p.labels) & "_"
@@ -484,7 +493,7 @@ proc localVarDecl(p: BProc; n: PNode): Rope =
     add(result, " ")
     add(result, s.loc.r)
   else:
-    result = s.cgDeclFrmt % [result, s.loc.r]
+    result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.r])
 
 proc assignLocalVar(p: BProc, n: PNode) =
   #assert(s.loc.k == locNone) # not yet assigned
@@ -535,7 +544,7 @@ proc assignGlobalVar(p: BProc, n: PNode) =
         if sfVolatile in s.flags: add(decl, " volatile")
         addf(decl, " $1;$n", [s.loc.r])
       else:
-        decl = (s.cgDeclFrmt & ";$n") % [td, s.loc.r]
+        decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r])
       add(p.module.s[cfsVars], decl)
   if p.withinLoop > 0:
     # fixes tests/run/tzeroarray:
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index 4cd66b333..d9c3b7b86 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -70,6 +70,7 @@ type
     threadVarAccessed*: bool  # true if the proc already accessed some threadvar
     hasCurFramePointer*: bool # true if _nimCurFrame var needed to recover after
                               # exception is generated
+    noSafePoints*: bool       # the proc doesn't use safe points in exception handling
     lastLineInfo*: TLineInfo  # to avoid generating excessive 'nimln' statements
     currLineInfo*: TLineInfo  # AST codegen will make this superfluous
     nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]]
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index 436fd699f..d36427098 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -567,19 +567,36 @@ proc genReturn(c: var Con; n: PNode) =
 
 const
   InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
-  PathKinds* = {nkDotExpr, nkCheckedFieldExpr,
+  PathKinds0 = {nkDotExpr, nkCheckedFieldExpr,
                 nkBracketExpr, nkDerefExpr, nkHiddenDeref,
                 nkAddr, nkHiddenAddr,
-                nkHiddenStdConv, nkHiddenSubConv, nkObjDownConv, nkObjUpConv}
+                nkObjDownConv, nkObjUpConv}
+  PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv}
+
+proc getRoot(n: PNode): PNode =
+  result = n
+  while true:
+    case result.kind
+    of PathKinds0:
+      result = result[0]
+    of PathKinds1:
+      result = result[1]
+    else: break
+
+proc skipConvDfa*(n: PNode): PNode =
+  result = n
+  while true:
+    case result.kind
+    of nkObjDownConv, nkObjUpConv:
+      result = result[0]
+    of PathKinds1:
+      result = result[1]
+    else: break
 
 proc genUse(c: var Con; orig: PNode) =
-  var n = orig
-  var iters = 0
-  while n.kind in PathKinds:
-    n = n[0]
-    inc iters
+  let n = dfa.getRoot(orig)
   if n.kind == nkSym and n.sym.kind in InterestingSyms:
-    c.code.add Instr(n: orig, kind: use, sym: if iters > 0: nil else: n.sym)
+    c.code.add Instr(n: orig, kind: use, sym: if orig != n: nil else: n.sym)
 
 proc aliases(obj, field: PNode): bool =
   var n = field
@@ -590,7 +607,7 @@ proc aliases(obj, field: PNode): bool =
     if sameTrees(obj, n): return true
     case n.kind
     of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv,
-       nkObjDownConv, nkObjUpConv, nkHiddenDeref:
+       nkObjDownConv, nkObjUpConv, nkHiddenDeref, nkDerefExpr:
       n = n[0]
     of nkBracketExpr:
       let x = n[0]
@@ -616,13 +633,19 @@ proc instrTargets*(ins: Instr; loc: PNode): bool =
     # use x; question does it affect 'x.f'? Yes.
     result = aliases(ins.n, loc) or aliases(loc, ins.n)
 
-proc isAnalysableFieldAccess*(n: PNode; owner: PSym): bool =
-  var n = n
+proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
+  var n = orig
   while true:
     case n.kind
     of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv,
-       nkObjDownConv, nkObjUpConv, nkHiddenDeref:
+       nkObjDownConv, nkObjUpConv:
+      n = n[0]
+    of nkHiddenDeref, nkDerefExpr:
+      # We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
+      # pointer indirection.
       n = n[0]
+      return n.kind == nkSym and n.sym.owner == owner and (isSinkParam(n.sym) or
+          n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned, tyVar})
     of nkBracketExpr:
       let x = n[0]
       if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple:
@@ -633,11 +656,25 @@ proc isAnalysableFieldAccess*(n: PNode; owner: PSym): bool =
       break
   # XXX Allow closure deref operations here if we know
   # the owner controlled the closure allocation?
-  result = n.kind == nkSym and n.sym.owner == owner and owner.kind != skModule
+  result = n.kind == nkSym and n.sym.owner == owner and
+    owner.kind != skModule and
+    (n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar)
+  # Note: There is a different move analyzer possible that checks for
+  # consume(param.key); param.key = newValue  for all paths. Then code like
+  #
+  #   let splited = split(move self.root, x)
+  #   self.root = merge(splited.lower, splited.greater)
+  #
+  # could be written without the ``move self.root``. However, this would be
+  # wrong! Then the write barrier for the ``self.root`` assignment would
+  # free the old data and all is lost! Lesson: Don't be too smart, trust the
+  # lower level C++ optimizer to specialize this code.
 
 proc genDef(c: var Con; n: PNode) =
   if n.kind == nkSym and n.sym.kind in InterestingSyms:
     c.code.add Instr(n: n, kind: def, sym: n.sym)
+  elif isAnalysableFieldAccess(n, c.owner):
+    c.code.add Instr(n: n, kind: def, sym: nil)
 
 proc canRaise(fn: PNode): bool =
   const magicsThatCanRaise = {
@@ -715,7 +752,7 @@ proc gen(c: var Con; n: PNode) =
     # "uses" 'i'. But we are only talking about builtin array indexing so
     # it doesn't matter and 'x = 34' is NOT a usage of 'x'.
     genDef(c, n[0])
-  of PathKinds:
+  of PathKinds0 - {nkHiddenStdConv, nkHiddenSubConv, nkObjDownConv, nkObjUpConv}:
     genUse(c, n)
   of nkIfStmt, nkIfExpr: genIf(c, n)
   of nkWhenStmt:
@@ -732,8 +769,8 @@ proc gen(c: var Con; n: PNode) =
      nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr:
     for x in n: gen(c, x)
   of nkPragmaBlock: gen(c, n.lastSon)
-  of nkDiscardStmt: gen(c, n.sons[0])
-  of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast:
+  of nkDiscardStmt, nkObjDownConv, nkObjUpConv: gen(c, n.sons[0])
+  of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, nkHiddenSubConv, nkHiddenStdConv:
     gen(c, n.sons[1])
   of nkStringToCString, nkCStringToString: gen(c, n.sons[0])
   of nkVarSection, nkLetSection: genVarSection(c, n)
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 93efef526..014f757db 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -172,7 +172,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
                                 outExt, RelativeDir"htmldocs", false)
   result.thisDir = result.destFile.splitFile.dir
 
-proc dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) =
+template dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) =
   if conf.cmd != cmdRst2tex: addf(dest, xml, args)
   else: addf(dest, tex, args)
 
diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim
index 2232601f4..048860423 100644
--- a/compiler/docgen2.nim
+++ b/compiler/docgen2.nim
@@ -20,11 +20,12 @@ type
   TGen = object of PPassContext
     doc: PDoc
     module: PSym
+    config: ConfigRef
   PGen = ref TGen
 
 template shouldProcess(g): bool =
   (g.module.owner.id == g.doc.conf.mainPackageId and optWholeProject in g.doc.conf.globalOptions) or
-      sfMainModule in g.module.flags
+      sfMainModule in g.module.flags or g.config.projectMainIdx == g.module.info.fileIndex
 
 template closeImpl(body: untyped) {.dirty.} =
   var g = PGen(p)
@@ -60,6 +61,7 @@ template myOpenImpl(ext: untyped) {.dirty.} =
   var g: PGen
   new(g)
   g.module = module
+  g.config = graph.config
   var d = newDocumentor(AbsoluteFile toFullPath(graph.config, FileIndex module.position),
       graph.cache, graph.config, ext)
   d.hasToc = true
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index ab42f4f52..9880ecf4f 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -376,7 +376,7 @@ proc nameToCC*(name: string): TSystemCC =
   result = ccNone
 
 proc isVSCompatible*(conf: ConfigRef): bool =
-  return conf.cCompiler == ccVcc or 
+  return conf.cCompiler == ccVcc or
           conf.cCompiler == ccClangCl or
           (conf.cCompiler == ccIcl and conf.target.hostOS in osDos..osWindows)
 
@@ -738,7 +738,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
     # way of being able to debug and rebuild the program at the same time. This
     # is accomplished using the /PDB:<filename> flag (there also exists the
     # /PDBALTPATH:<filename> flag). The only downside is that the .pdb files are
-    # atleast 300kb big (when linking statically to the runtime - or else 5mb+) 
+    # atleast 300kb big (when linking statically to the runtime - or else 5mb+)
     # and will quickly accumulate. There is a hacky solution: we could try to
     # delete all .pdb files with a pattern and swallow exceptions.
     #
@@ -910,7 +910,8 @@ proc callCCompiler*(conf: ConfigRef) =
                        else: AbsoluteFile(conf.projectName)
       linkCmd = getLinkCmd(conf, mainOutput, objfiles)
       if optCompileOnly notin conf.globalOptions:
-        if defined(windows) and linkCmd.len > 8_000:
+        const MaxCmdLen = when defined(windows): 8_000 else: 32_000
+        if linkCmd.len > MaxCmdLen:
           # Windows's command line limit is about 8K (don't laugh...) so C compilers on
           # Windows support a feature where the command line can be passed via ``@linkcmd``
           # to them.
diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim
index 7901ed4dc..0e414c975 100644
--- a/compiler/injectdestructors.nim
+++ b/compiler/injectdestructors.nim
@@ -136,7 +136,7 @@ to do it.
 import
   intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
   strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
-  lineinfos, parampatterns
+  lineinfos, parampatterns, sighashes
 
 const
   InterestingSyms = {skVar, skResult, skLet, skForVar, skTemp}
@@ -195,15 +195,17 @@ proc isLastRead(n: PNode; c: var Con): bool =
   # first we need to search for the instruction that belongs to 'n':
   c.otherRead = nil
   var instr = -1
+  let m = dfa.skipConvDfa(n)
+
   for i in 0..<c.g.len:
     # This comparison is correct and MUST not be ``instrTargets``:
-    if c.g[i].kind == use and c.g[i].n == n:
+    if c.g[i].kind == use and c.g[i].n == m:
       if instr < 0:
         instr = i
         break
 
   dbg:
-    echo "starting point for ", n, " is ", instr
+    echo "starting point for ", n, " is ", instr, " ", n.kind
 
   if instr < 0: return false
   # we go through all paths beginning from 'instr+1' and need to
@@ -314,24 +316,37 @@ proc makePtrType(c: Con, baseType: PType): PType =
   result = newType(tyPtr, c.owner)
   addSonSkipIntLit(result, baseType)
 
-template genOp(opr, opname, ri) =
-  let op = opr
+proc addDestroy(c: var Con; n: PNode) =
+  # append to front:
+  c.destroys = newTree(nkStmtList, n, c.destroys)
+
+proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
+  var op = t.attachedOps[kind]
+
+  if op == nil:
+    # give up and find the canonical type instead:
+    let h = sighashes.hashType(t, {CoType, CoConsiderOwned})
+    let canon = c.graph.canonTypes.getOrDefault(h)
+    if canon != nil:
+      op = canon.attachedOps[kind]
+
   if op == nil:
-    globalError(c.graph.config, dest.info, "internal error: '" & opname &
+    globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
       "' operator not found for type " & typeToString(t))
   elif op.ast[genericParamsPos].kind != nkEmpty:
-    globalError(c.graph.config, dest.info, "internal error: '" & opname &
+    globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
       "' operator is generic")
-  if sfError in op.flags: checkForErrorPragma(c, t, ri, opname)
+  if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind])
   let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
   addrExp.add(dest)
   result = newTree(nkCall, newSymNode(op), addrExp)
 
 proc genSink(c: Con; t: PType; dest, ri: PNode): PNode =
   let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
-  let op = if t.sink != nil: t.sink else: t.assignment
-  if op != nil:
-    genOp(op, "=sink", ri)
+  let k = if t.attachedOps[attachedSink] != nil: attachedSink
+           else: attachedAsgn
+  if t.attachedOps[k] != nil:
+    result = genOp(c, t, k, dest, ri)
   else:
     # in rare cases only =destroy exists but no sink or assignment
     # (see Pony object in tmove_objconstr.nim)
@@ -342,15 +357,15 @@ proc genCopy(c: Con; t: PType; dest, ri: PNode): PNode =
   if tfHasOwned in t.flags:
     checkForErrorPragma(c, t, ri, "=")
   let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
-  genOp(t.assignment, "=", ri)
+  result = genOp(c, t, attachedAsgn, dest, ri)
 
 proc genCopyNoCheck(c: Con; t: PType; dest, ri: PNode): PNode =
   let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
-  genOp(t.assignment, "=", ri)
+  result = genOp(c, t, attachedAsgn, dest, ri)
 
 proc genDestroy(c: Con; t: PType; dest: PNode): PNode =
   let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
-  genOp(t.destructor, "=destroy", nil)
+  result = genOp(c, t, attachedDestructor, dest, nil)
 
 proc addTopVar(c: var Con; v: PNode) =
   c.topLevelVars.add newTree(nkIdentDefs, v, c.emptyNode, c.emptyNode)
@@ -408,6 +423,17 @@ proc sinkParamIsLastReadCheck(c: var Con, s: PNode) =
      localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s &
          "` is already consumed at " & toFileLineCol(c. graph.config, s.info))
 
+proc isSinkTypeForParam(t: PType): bool =
+  # a parameter like 'seq[owned T]' must not be used only once, but its
+  # elements must, so we detect this case here:
+  result = t.skipTypes({tyGenericInst, tyAlias}).kind in {tySink, tyOwned}
+  when false:
+    if isSinkType(t):
+      if t.skipTypes({tyGenericInst, tyAlias}).kind in {tyArray, tyVarargs, tyOpenArray, tySequence}:
+        result = false
+      else:
+        result = true
+
 proc passCopyToSink(n: PNode; c: var Con): PNode =
   result = newNodeIT(nkStmtListExpr, n.info, n.typ)
   let tmp = getTemp(c, n.typ, n.info)
@@ -441,7 +467,7 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
       let L = if parameters != nil: parameters.len else: 0
       result.add arg[0]
       for i in 1..<arg.len:
-        result.add pArg(arg[i], c, i < L and isSinkType(parameters[i]))
+        result.add pArg(arg[i], c, i < L and isSinkTypeForParam(parameters[i]))
     elif arg.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkBracket, nkCharLit..nkTripleStrLit}:
       discard "object construction to sink parameter: nothing to do"
       result = arg
@@ -518,16 +544,22 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
     let L = if parameters != nil: parameters.len else: 0
     ri2.add ri[0]
     for i in 1..<ri.len:
-      ri2.add pArg(ri[i], c, i < L and isSinkType(parameters[i]))
+      ri2.add pArg(ri[i], c, i < L and isSinkTypeForParam(parameters[i]))
     #recurse(ri, ri2)
     result.add ri2
   of nkBracketExpr:
     if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym):
       # unpacking of tuple: move out the elements
       result = genSink(c, dest.typ, dest, ri)
+      result.add p(ri, c)
+    elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c):
+      # Rule 3: `=sink`(x, z); wasMoved(z)
+      var snk = genSink(c, dest.typ, dest, ri)
+      snk.add ri
+      result = newTree(nkStmtList, snk, genWasMoved(ri, c))
     else:
       result = genCopy(c, dest.typ, dest, ri)
-    result.add p(ri, c)
+      result.add p(ri, c)
   of nkStmtListExpr:
     result = newNodeI(nkStmtList, ri.info)
     for i in 0..ri.len-2:
@@ -610,7 +642,9 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
       result = genCopy(c, dest.typ, dest, ri)
       result.add p(ri, c)
   of nkHiddenSubConv, nkHiddenStdConv:
-    if ri[1].kind in movableNodeKinds:
+    if sameType(ri.typ, ri[1].typ):
+      result = moveOrCopy(dest, ri[1], c)
+    elif ri[1].kind in movableNodeKinds:
       result = moveOrCopy(dest, ri[1], c)
       var b = newNodeIT(ri.kind, ri.info, ri.typ)
       b.add ri[0] # add empty node
@@ -673,6 +707,15 @@ proc injectDefaultCalls(n: PNode, c: var Con) =
 proc isCursor(n: PNode): bool {.inline.} =
   result = n.kind == nkSym and sfCursor in n.sym.flags
 
+proc keepVar(n, it: PNode, c: var Con): PNode =
+  # keep the var but transform 'ri':
+  result = copyNode(n)
+  var itCopy = copyNode(it)
+  for j in 0..it.len-2:
+    itCopy.add it[j]
+  itCopy.add p(it[it.len-1], c)
+  result.add itCopy
+
 proc p(n: PNode; c: var Con): PNode =
   case n.kind
   of nkVarSection, nkLetSection:
@@ -694,24 +737,17 @@ proc p(n: PNode; c: var Con): PNode =
             c.addTopVar v
             # make sure it's destroyed at the end of the proc:
             if not isUnpackedTuple(it[0].sym):
-              c.destroys.add genDestroy(c, v.typ, v)
-            if ri.kind != nkEmpty:
-              let r = moveOrCopy(v, ri, c)
-              result.add r
+              c.addDestroy genDestroy(c, 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-2:
-          itCopy.add it[j]
-        itCopy.add p(ri, c)
-        varSection.add itCopy
-        result.add varSection
+        result.add keepVar(n, it, c)
   of nkCallKinds:
     let parameters = n[0].typ
     let L = if parameters != nil: parameters.len else: 0
     for i in 1 ..< n.len:
-      n.sons[i] = pArg(n[i], c, i < L and isSinkType(parameters[i]))
+      n.sons[i] = pArg(n[i], c, i < L and isSinkTypeForParam(parameters[i]))
     if n.typ != nil and hasDestructor(n.typ):
       discard "produce temp creation"
       result = newNodeIT(nkStmtListExpr, n.info, n.typ)
@@ -720,11 +756,11 @@ proc p(n: PNode; c: var Con): PNode =
       sinkExpr.add n
       result.add sinkExpr
       result.add tmp
-      c.destroys.add genDestroy(c, n.typ, tmp)
+      c.addDestroy genDestroy(c, n.typ, tmp)
     else:
       result = n
   of nkAsgn, nkFastAsgn:
-    if hasDestructor(n[0].typ):
+    if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda, nkClosure}:
       result = moveOrCopy(n[0], n[1], c)
     else:
       result = copyNode(n)
@@ -783,8 +819,8 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
     let params = owner.typ.n
     for i in 1 ..< params.len:
       let param = params[i].sym
-      if isSinkParam(param) and hasDestructor(param.typ.skipTypes({tySink})):
-        c.destroys.add genDestroy(c, param.typ.skipTypes({tyGenericInst, tyAlias, tySink}), params[i])
+      if isSinkTypeForParam(param.typ) and hasDestructor(param.typ.skipTypes({tySink})):
+        c.addDestroy genDestroy(c, param.typ.skipTypes({tyGenericInst, tyAlias, tySink}), params[i])
 
   #if optNimV2 in c.graph.config.globalOptions:
   #  injectDefaultCalls(n, c)
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 5f93cbbbd..cd13aab78 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -32,9 +32,9 @@ import
   ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
   nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables,
   times, ropes, math, passes, ccgutils, wordrecg, renderer,
-  intsets, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils, 
+  intsets, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils,
   pathutils, transf
-  
+
 
 from modulegraphs import ModuleGraph, PPassContext
 
@@ -365,92 +365,92 @@ proc genOr(p: PProc, a, b: PNode, r: var TCompRes) =
     line(p, "}")
 
 type
-  TMagicFrmt = array[0..3, string]
+  TMagicFrmt = array[0..1, string]
   TMagicOps = array[mAddI..mStrToStr, TMagicFrmt]
 
-const # magic checked op; magic unchecked op; checked op; unchecked op
-  jsOps: TMagicOps = [
-    ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # AddI
-    ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # SubI
-    ["mulInt", "", "mulInt($1, $2)", "($1 * $2)"], # MulI
-    ["divInt", "", "divInt($1, $2)", "Math.trunc($1 / $2)"], # DivI
-    ["modInt", "", "modInt($1, $2)", "Math.trunc($1 % $2)"], # ModI
-    ["addInt", "", "addInt($1, $2)", "($1 + $2)"], # Succ
-    ["subInt", "", "subInt($1, $2)", "($1 - $2)"], # Pred
-    ["", "", "($1 + $2)", "($1 + $2)"], # AddF64
-    ["", "", "($1 - $2)", "($1 - $2)"], # SubF64
-    ["", "", "($1 * $2)", "($1 * $2)"], # MulF64
-    ["", "", "($1 / $2)", "($1 / $2)"], # DivF64
-    ["", "", "", ""], # ShrI
-    ["", "", "($1 << $2)", "($1 << $2)"], # ShlI
-    ["", "", "($1 >> $2)", "($1 >> $2)"], # AshrI
-    ["", "", "($1 & $2)", "($1 & $2)"], # BitandI
-    ["", "", "($1 | $2)", "($1 | $2)"], # BitorI
-    ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
-    ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinI
-    ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI
-    ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
-    ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
-    ["", "", "", ""], # addU
-    ["", "", "", ""], # subU
-    ["", "", "", ""], # mulU
-    ["", "", "", ""], # divU
-    ["", "", "($1 % $2)", "($1 % $2)"], # modU
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqI
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
-    ["", "", "($1 < $2)", "($1 < $2)"], # LtI
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqF64
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
-    ["", "", "($1 < $2)", "($1 < $2)"], # LtF64
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # leU
-    ["", "", "($1 < $2)", "($1 < $2)"], # ltU
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # leU64
-    ["", "", "($1 < $2)", "($1 < $2)"], # ltU64
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqEnum
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum
-    ["", "", "($1 < $2)", "($1 < $2)"], # LtEnum
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqCh
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeCh
-    ["", "", "($1 < $2)", "($1 < $2)"], # LtCh
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqB
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # LeB
-    ["", "", "($1 < $2)", "($1 < $2)"], # LtB
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqRef
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqUntracedRef
-    ["", "", "($1 <= $2)", "($1 <= $2)"], # LePtr
-    ["", "", "($1 < $2)", "($1 < $2)"], # LtPtr
-    ["", "", "($1 != $2)", "($1 != $2)"], # Xor
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqCString
-    ["", "", "($1 == $2)", "($1 == $2)"], # EqProc
-    ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
-    ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
-    ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
-    ["", "", "!($1)", "!($1)"], # Not
-    ["", "", "+($1)", "+($1)"], # UnaryPlusI
-    ["", "", "~($1)", "~($1)"], # BitnotI
-    ["", "", "+($1)", "+($1)"], # UnaryPlusF64
-    ["", "", "-($1)", "-($1)"], # UnaryMinusF64
-    ["", "", "Math.abs($1)", "Math.abs($1)"], # AbsF64
-    ["Ze8ToI", "Ze8ToI", "Ze8ToI($1)", "Ze8ToI($1)"], # mZe8ToI
-    ["Ze8ToI64", "Ze8ToI64", "Ze8ToI64($1)", "Ze8ToI64($1)"], # mZe8ToI64
-    ["Ze16ToI", "Ze16ToI", "Ze16ToI($1)", "Ze16ToI($1)"], # mZe16ToI
-    ["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64
-    ["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64
-    ["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64
-    ["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8
-    ["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16
-    ["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32
-    ["", "", "$1", "$1"],     # ToFloat
-    ["", "", "$1", "$1"],     # ToBiggestFloat
-    ["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToInt
-    ["", "", "Math.trunc($1)", "Math.trunc($1)"], # ToBiggestInt
-    ["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"],
-    ["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"],
-    ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
-    ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
-    ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
-    ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"],
-    ["", "", "$1", "$1"]]
+const # magic checked op; magic unchecked op;
+  jsMagics: TMagicOps = [
+    ["addInt", ""], # AddI
+    ["subInt", ""], # SubI
+    ["mulInt", ""], # MulI
+    ["divInt", ""], # DivI
+    ["modInt", ""], # ModI
+    ["addInt", ""], # Succ
+    ["subInt", ""], # Pred
+    ["", ""], # AddF64
+    ["", ""], # SubF64
+    ["", ""], # MulF64
+    ["", ""], # DivF64
+    ["", ""], # ShrI
+    ["", ""], # ShlI
+    ["", ""], # AshrI
+    ["", ""], # BitandI
+    ["", ""], # BitorI
+    ["", ""], # BitxorI
+    ["nimMin", "nimMin"], # MinI
+    ["nimMax", "nimMax"], # MaxI
+    ["nimMin", "nimMin"], # MinF64
+    ["nimMax", "nimMax"], # MaxF64
+    ["", ""], # addU
+    ["", ""], # subU
+    ["", ""], # mulU
+    ["", ""], # divU
+    ["", ""], # modU
+    ["", ""], # EqI
+    ["", ""], # LeI
+    ["", ""], # LtI
+    ["", ""], # EqF64
+    ["", ""], # LeF64
+    ["", ""], # LtF64
+    ["", ""], # leU
+    ["", ""], # ltU
+    ["", ""], # leU64
+    ["", ""], # ltU64
+    ["", ""], # EqEnum
+    ["", ""], # LeEnum
+    ["", ""], # LtEnum
+    ["", ""], # EqCh
+    ["", ""], # LeCh
+    ["", ""], # LtCh
+    ["", ""], # EqB
+    ["", ""], # LeB
+    ["", ""], # LtB
+    ["", ""], # EqRef
+    ["", ""], # EqUntracedRef
+    ["", ""], # LePtr
+    ["", ""], # LtPtr
+    ["", ""], # Xor
+    ["", ""], # EqCString
+    ["", ""], # EqProc
+    ["negInt", ""], # UnaryMinusI
+    ["negInt64", ""], # UnaryMinusI64
+    ["absInt", ""], # AbsI
+    ["", ""], # Not
+    ["", ""], # UnaryPlusI
+    ["", ""], # BitnotI
+    ["", ""], # UnaryPlusF64
+    ["", ""], # UnaryMinusF64
+    ["", ""], # AbsF64
+    ["Ze8ToI", "Ze8ToI"], # mZe8ToI
+    ["Ze8ToI64", "Ze8ToI64"], # mZe8ToI64
+    ["Ze16ToI", "Ze16ToI"], # mZe16ToI
+    ["Ze16ToI64", "Ze16ToI64"], # mZe16ToI64
+    ["Ze32ToI64", "Ze32ToI64"], # mZe32ToI64
+    ["ZeIToI64", "ZeIToI64"], # mZeIToI64
+    ["toU8", "toU8"], # toU8
+    ["toU16", "toU16"], # toU16
+    ["toU32", "toU32"], # toU32
+    ["", ""],     # ToFloat
+    ["", ""],     # ToBiggestFloat
+    ["", ""], # ToInt
+    ["", ""], # ToBiggestInt
+    ["nimCharToStr", "nimCharToStr"],
+    ["nimBoolToStr", "nimBoolToStr"],
+    ["cstrToNimstr", "cstrToNimstr"],
+    ["cstrToNimstr", "cstrToNimstr"],
+    ["cstrToNimstr", "cstrToNimstr"],
+    ["cstrToNimstr", "cstrToNimstr"],
+    ["", ""]]
 
 proc needsTemp(p: PProc; n: PNode): bool =
   # check if n contains a call to determine
@@ -478,7 +478,7 @@ proc maybeMakeTemp(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rope] =
   else:
     (a: a, tmp: b)
 
-proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
+template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
   # $1 and $2 in the `frmt` string bind to lhs and rhs of the expr,
   # if $3 or $4 are present they will be substituted with temps for
   # lhs and rhs respectively
@@ -490,8 +490,8 @@ proc binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
   var
     a, tmp = x.rdLoc
     b, tmp2 = y.rdLoc
-  if "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
-  if "$4" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
+  when "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
+  when "$4" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
 
   r.res = frmt % [a, b, tmp, tmp2]
   r.kind = resExpr
@@ -520,7 +520,7 @@ proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string,
     r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
   r.kind = resExpr
 
-proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
+template ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
   var x, y, z: TCompRes
   useMagic(p, magic)
   gen(p, n.sons[1], x)
@@ -529,7 +529,7 @@ proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
   r.res = frmt % [x.rdLoc, y.rdLoc, z.rdLoc]
   r.kind = resExpr
 
-proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
+template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
   # $1 binds to n[1], if $2 is present it will be substituted to a tmp of $1
   useMagic(p, magic)
   gen(p, n.sons[1], r)
@@ -541,15 +541,108 @@ proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
 proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
   var
     x, y: TCompRes
+    xLoc,yLoc: Rope
   let i = ord(optOverflowCheck notin p.options)
-  useMagic(p, jsOps[op][i])
+  useMagic(p, jsMagics[op][i])
   if sonsLen(n) > 2:
     gen(p, n.sons[1], x)
     gen(p, n.sons[2], y)
-    r.res = jsOps[op][i + 2] % [x.rdLoc, y.rdLoc]
+    xLoc = x.rdLoc
+    yLoc = y.rdLoc
   else:
     gen(p, n.sons[1], r)
-    r.res = jsOps[op][i + 2] % [r.rdLoc]
+    xLoc = r.rdLoc
+
+  template applyFormat(frmtA, frmtB: string) =
+    if i == 0:
+      r.res = frmtA % [xLoc, yLoc]
+    else:
+      r.res = frmtB % [xLoc, yLoc]
+
+  case op:
+  of mAddI: applyFormat("addInt($1, $2)", "($1 + $2)")
+  of mSubI: applyFormat("subInt($1, $2)", "($1 - $2)")
+  of mMulI: applyFormat("mulInt($1, $2)", "($1 * $2)")
+  of mDivI: applyFormat("divInt($1, $2)", "Math.trunc($1 / $2)")
+  of mModI: applyFormat("modInt($1, $2)", "Math.trunc($1 % $2)")
+  of mSucc: applyFormat("addInt($1, $2)", "($1 + $2)")
+  of mPred: applyFormat("subInt($1, $2)", "($1 - $2)")
+  of mAddF64: applyFormat("($1 + $2)", "($1 + $2)")
+  of mSubF64: applyFormat("($1 - $2)", "($1 - $2)")
+  of mMulF64: applyFormat("($1 * $2)", "($1 * $2)")
+  of mDivF64: applyFormat("($1 / $2)", "($1 / $2)")
+  of mShrI: applyFormat("", "")
+  of mShlI: applyFormat("($1 << $2)", "($1 << $2)")
+  of mAshrI: applyFormat("($1 >> $2)", "($1 >> $2)")
+  of mBitandI: applyFormat("($1 & $2)", "($1 & $2)")
+  of mBitorI: applyFormat("($1 | $2)", "($1 | $2)")
+  of mBitxorI: applyFormat("($1 ^ $2)", "($1 ^ $2)")
+  of mMinI: applyFormat("nimMin($1, $2)", "nimMin($1, $2)")
+  of mMaxI: applyFormat("nimMax($1, $2)", "nimMax($1, $2)")
+  of mMinF64: applyFormat("nimMin($1, $2)", "nimMin($1, $2)")
+  of mMaxF64: applyFormat("nimMax($1, $2)", "nimMax($1, $2)")
+  of mAddU: applyFormat("", "")
+  of msubU: applyFormat("", "")
+  of mmulU: applyFormat("", "")
+  of mdivU: applyFormat("", "")
+  of mmodU: applyFormat("($1 % $2)", "($1 % $2)")
+  of mEqI: applyFormat("($1 == $2)", "($1 == $2)")
+  of mLeI: applyFormat("($1 <= $2)", "($1 <= $2)")
+  of mLtI: applyFormat("($1 < $2)", "($1 < $2)")
+  of mEqF64: applyFormat("($1 == $2)", "($1 == $2)")
+  of mLeF64: applyFormat("($1 <= $2)", "($1 <= $2)")
+  of mLtF64: applyFormat("($1 < $2)", "($1 < $2)")
+  of mleU: applyFormat("($1 <= $2)", "($1 <= $2)")
+  of mltU: applyFormat("($1 < $2)", "($1 < $2)")
+  of mleU64: applyFormat("($1 <= $2)", "($1 <= $2)")
+  of mltU64: applyFormat("($1 < $2)", "($1 < $2)")
+  of mEqEnum: applyFormat("($1 == $2)", "($1 == $2)")
+  of mLeEnum: applyFormat("($1 <= $2)", "($1 <= $2)")
+  of mLtEnum: applyFormat("($1 < $2)", "($1 < $2)")
+  of mEqCh: applyFormat("($1 == $2)", "($1 == $2)")
+  of mLeCh: applyFormat("($1 <= $2)", "($1 <= $2)")
+  of mLtCh: applyFormat("($1 < $2)", "($1 < $2)")
+  of mEqB: applyFormat("($1 == $2)", "($1 == $2)")
+  of mLeB: applyFormat("($1 <= $2)", "($1 <= $2)")
+  of mLtB: applyFormat("($1 < $2)", "($1 < $2)")
+  of mEqRef: applyFormat("($1 == $2)", "($1 == $2)")
+  of mEqUntracedRef: applyFormat("($1 == $2)", "($1 == $2)")
+  of mLePtr: applyFormat("($1 <= $2)", "($1 <= $2)")
+  of mLtPtr: applyFormat("($1 < $2)", "($1 < $2)")
+  of mXor: applyFormat("($1 != $2)", "($1 != $2)")
+  of mEqCString: applyFormat("($1 == $2)", "($1 == $2)")
+  of mEqProc: applyFormat("($1 == $2)", "($1 == $2)")
+  of mUnaryMinusI: applyFormat("negInt($1)", "-($1)")
+  of mUnaryMinusI64: applyFormat("negInt64($1)", "-($1)")
+  of mAbsI: applyFormat("absInt($1)", "Math.abs($1)")
+  of mNot: applyFormat("!($1)", "!($1)")
+  of mUnaryPlusI: applyFormat("+($1)", "+($1)")
+  of mBitnotI: applyFormat("~($1)", "~($1)")
+  of mUnaryPlusF64: applyFormat("+($1)", "+($1)")
+  of mUnaryMinusF64: applyFormat("-($1)", "-($1)")
+  of mAbsF64: applyFormat("Math.abs($1)", "Math.abs($1)")
+  of mZe8ToI: applyFormat("Ze8ToI($1)", "Ze8ToI($1)")
+  of mZe8ToI64: applyFormat("Ze8ToI64($1)", "Ze8ToI64($1)")
+  of mZe16ToI: applyFormat("Ze16ToI($1)", "Ze16ToI($1)")
+  of mZe16ToI64: applyFormat("Ze16ToI64($1)", "Ze16ToI64($1)")
+  of mZe32ToI64: applyFormat("Ze32ToI64($1)", "Ze32ToI64($1)")
+  of mZeIToI64: applyFormat("ZeIToI64($1)", "ZeIToI64($1)")
+  of mtoU8: applyFormat("toU8($1)", "toU8($1)")
+  of mtoU16: applyFormat("toU16($1)", "toU16($1)")
+  of mtoU32: applyFormat("toU32($1)", "toU32($1)")
+  of mToFloat: applyFormat("$1", "$1")
+  of mToBiggestFloat: applyFormat("$1", "$1")
+  of mToInt: applyFormat("Math.trunc($1)", "Math.trunc($1)")
+  of mToBiggestInt: applyFormat("Math.trunc($1)", "Math.trunc($1)")
+  of mCharToStr: applyFormat("nimCharToStr($1)", "nimCharToStr($1)")
+  of mBoolToStr: applyFormat("nimBoolToStr($1)", "nimBoolToStr($1)")
+  of mIntToStr: applyFormat("cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")")
+  of mInt64ToStr: applyFormat("cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")")
+  of mFloatToStr: applyFormat("cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")")
+  of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)")
+  of mStrToStr, mUnown: applyFormat("$1", "$1")
+  else:
+    assert false, $op
 
 proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
   case op
@@ -1268,6 +1361,9 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
       internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
     r.res = s.loc.r
   of skProc, skFunc, skConverter, skMethod:
+    if sfCompileTime in s.flags:
+      localError(p.config, n.info, "request to generate code for .compileTime proc: " &
+          s.name.s)
     discard mangleName(p.module, s)
     r.res = s.loc.r
     if lfNoDecl in s.loc.flags or s.magic != mNone or
@@ -1603,6 +1699,9 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
     else:
       varCode = "var $2"
   else:
+    # Is this really a thought through feature?  this basically unused
+    # feature makes it impossible for almost all format strings in
+    # this function to be checked at compile time.
     varCode = v.constraint.strVal
 
   if n.kind == nkEmpty:
@@ -1611,8 +1710,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
       lineF(p, "var $1 = null;$n", [varName])
       lineF(p, "var $1_Idx = 0;$n", [varName])
     else:
-      lineF(p, varCode & " = $3;$n",
-                [returnType, varName, createVar(p, v.typ, isIndirect(v))])
+      line(p, runtimeFormat(varCode & " = $3;$n", [returnType, varName, createVar(p, v.typ, isIndirect(v))]))
   else:
     gen(p, n, a)
     case mapType(p, v.typ)
@@ -1626,29 +1724,29 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
       let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {}
       if a.typ == etyBaseIndex:
         if targetBaseIndex:
-          lineF(p, varCode & " = $3, $2_Idx = $4;$n",
-                   [returnType, v.loc.r, a.address, a.res])
+          line(p, runtimeFormat(varCode & " = $3, $2_Idx = $4;$n",
+                   [returnType, v.loc.r, a.address, a.res]))
         else:
           if isIndirect(v):
-            lineF(p, varCode & " = [[$3, $4]];$n",
-                     [returnType, v.loc.r, a.address, a.res])
+            line(p, runtimeFormat(varCode & " = [[$3, $4]];$n",
+                     [returnType, v.loc.r, a.address, a.res]))
           else:
-            lineF(p, varCode & " = [$3, $4];$n",
-                     [returnType, v.loc.r, a.address, a.res])
+            line(p, runtimeFormat(varCode & " = [$3, $4];$n",
+                     [returnType, v.loc.r, a.address, a.res]))
       else:
         if targetBaseIndex:
           let tmp = p.getTemp
           lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
                    [tmp, a.res, v.loc.r])
         else:
-          lineF(p, varCode & " = $3;$n", [returnType, v.loc.r, a.res])
+          line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, a.res]))
       return
     else:
       s = a.res
     if isIndirect(v):
-      lineF(p, varCode & " = [$3];$n", [returnType, v.loc.r, s])
+      line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.r, s]))
     else:
-      lineF(p, varCode & " = $3;$n", [returnType, v.loc.r, s])
+      line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, s]))
 
   if useReloadingGuard:
     dec p.extraIndent
@@ -2117,7 +2215,7 @@ proc genReturnStmt(p: PProc, n: PNode) =
   lineF(p, "break BeforeRet;$n", [])
 
 proc frameCreate(p: PProc; procname, filename: Rope): Rope =
-  let frameFmt =
+  const frameFmt =
     "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n"
 
   result = p.indentLine(frameFmt % [procname, filename])
@@ -2185,7 +2283,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
 
   var def: Rope
   if not prc.constraint.isNil:
-    def = (prc.constraint.strVal & " {$n$#$#$#$#$#") %
+    def = runtimeFormat(prc.constraint.strVal & " {$n$#$#$#$#$#",
             [ returnType,
               name,
               header,
@@ -2193,7 +2291,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
               optionalLine(p.locals),
               optionalLine(resultAsgn),
               optionalLine(genProcBody(p, prc)),
-              optionalLine(p.indentLine(returnStmt))]
+              optionalLine(p.indentLine(returnStmt))])
   else:
     result = ~"\L"
 
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 180c5531b..6f0f8e0a5 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -261,7 +261,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
 
 proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
   let envParam = getHiddenParam(g, owner)
-  let obj = envParam.typ.lastSon
+  let obj = envParam.typ.skipTypes({tyOwned, tyRef})
   addField(obj, s, g.cache)
 
   var access = newSymNode(envParam)
@@ -320,15 +320,23 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
     rawAddSon(result, obj)
     c.ownerToType[owner.id] = result
 
+proc asOwnedRef(c: DetectionPass; t: PType): PType =
+  if optNimV2 in c.graph.config.globalOptions:
+    assert t.kind == tyRef
+    result = newType(tyOwned, t.owner)
+    result.rawAddSon t
+  else:
+    result = t
+
 proc getEnvTypeForOwnerUp(c: var DetectionPass; owner: PSym;
                           info: TLineInfo): PType =
   var r = c.getEnvTypeForOwner(owner, info)
   result = newType(tyPtr, owner)
-  rawAddSon(result, r.base)
+  rawAddSon(result, r.skipTypes({tyOwned, tyRef}))
 
 proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
   let refObj = c.getEnvTypeForOwner(dest, info) # getHiddenParam(dest).typ
-  let obj = refObj.lastSon
+  let obj = refObj.skipTypes({tyOwned, tyRef})
   # The assumption here is that gcDestructors means we cannot deal
   # with cycles properly, so it's better to produce a weak ref (=ptr) here.
   # This seems to be generally correct but since it's a bit risky it's only
@@ -343,7 +351,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
   let upIdent = getIdent(c.graph.cache, upName)
   let upField = lookupInRecord(obj.n, upIdent)
   if upField != nil:
-    if upField.typ.base != fieldType.base:
+    if upField.typ.skipTypes({tyOwned, tyRef}) != fieldType.skipTypes({tyOwned, tyRef}):
       localError(c.graph.config, dep.info, "internal error: up references do not agree")
   else:
     let result = newSym(skField, upIdent, obj.owner, obj.owner.info)
@@ -414,8 +422,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
         addClosureParam(c, owner, n.info)
         if interestingIterVar(s):
           if not c.capturedVars.containsOrIncl(s.id):
-            let obj = getHiddenParam(c.graph, owner).typ.lastSon
-            #let obj = c.getEnvTypeForOwner(s.owner).lastSon
+            let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef})
+            #let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef})
 
             if s.name.id == getIdent(c.graph.cache, ":state").id:
               obj.n[0].sym.id = -s.id
@@ -440,8 +448,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
       #echo "capturing ", n.info
       # variable 's' is actually captured:
       if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
-        let obj = c.getEnvTypeForOwner(ow, n.info).lastSon
-        #getHiddenParam(owner).typ.lastSon
+        let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef})
+        #getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef})
         addField(obj, s, c.graph.cache)
       # create required upFields:
       var w = owner.skipGenericOwner
@@ -530,14 +538,14 @@ proc setupEnvVar(owner: PSym; d: DetectionPass;
     let envVarType = d.ownerToType.getOrDefault(owner.id)
     if envVarType.isNil:
       localError d.graph.config, owner.info, "internal error: could not determine closure type"
-    result = newEnvVar(d.graph.cache, owner, envVarType)
+    result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType))
     c.envVars[owner.id] = result
 
 proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode =
   let p = getHiddenParam(g, owner)
   result = p.newSymNode
   if owner.isIterator:
-    let upField = lookupInRecord(p.typ.lastSon.n, getIdent(g.cache, upName))
+    let upField = lookupInRecord(p.typ.skipTypes({tyOwned, tyRef}).n, getIdent(g.cache, upName))
     if upField == nil:
       localError(g.config, owner.info, "could not find up reference for closure iter")
     else:
@@ -566,10 +574,10 @@ proc rawClosureCreation(owner: PSym;
         # add ``env.param = param``
         result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info))
 
-  let upField = lookupInRecord(env.typ.lastSon.n, getIdent(d.graph.cache, upName))
+  let upField = lookupInRecord(env.typ.skipTypes({tyOwned, tyRef}).n, getIdent(d.graph.cache, upName))
   if upField != nil:
     let up = getUpViaParam(d.graph, owner)
-    if up != nil and upField.typ.base == up.typ.base:
+    if up != nil and upField.typ.skipTypes({tyOwned, tyRef}) == up.typ.skipTypes({tyOwned, tyRef}):
       result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
                  up, env.info))
     #elif oldenv != nil and oldenv.typ == upField.typ:
@@ -584,11 +592,11 @@ proc closureCreationForIter(iter: PNode;
   let owner = iter.sym.skipGenericOwner
   var v = newSym(skVar, getIdent(d.graph.cache, envName), owner, iter.info)
   incl(v.flags, sfShadowed)
-  v.typ = getHiddenParam(d.graph, iter.sym).typ
+  v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
   var vnode: PNode
   if owner.isIterator:
     let it = getHiddenParam(d.graph, owner)
-    addUniqueField(it.typ.sons[0], v, d.graph.cache)
+    addUniqueField(it.typ.skipTypes({tyOwned, tyRef}), v, d.graph.cache)
     vnode = indirectAccess(newSymNode(it), v, v.info)
   else:
     vnode = v.newSymNode
@@ -597,10 +605,10 @@ proc closureCreationForIter(iter: PNode;
     result.add(vs)
   result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
 
-  let upField = lookupInRecord(v.typ.lastSon.n, getIdent(d.graph.cache, upName))
+  let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef}).n, getIdent(d.graph.cache, upName))
   if upField != nil:
     let u = setupEnvVar(owner, d, c)
-    if u.typ.base == upField.typ.base:
+    if u.typ.skipTypes({tyOwned, tyRef}) == upField.typ.skipTypes({tyOwned, tyRef}):
       result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
                  u, iter.info))
     else:
@@ -610,7 +618,7 @@ proc closureCreationForIter(iter: PNode;
 proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
                      c: var LiftingPass): PNode =
   let access = setupEnvVar(owner, d, c)
-  let obj = access.typ.sons[0]
+  let obj = access.typ.skipTypes({tyOwned, tyRef})
   let field = getFieldFromObj(obj, n.sym)
   if field != nil:
     result = rawIndirectAccess(access, field, n.info)
@@ -619,7 +627,7 @@ proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
     result = n
 
 proc getStateField*(g: ModuleGraph; owner: PSym): PSym =
-  getHiddenParam(g, owner).typ.sons[0].n.sons[0].sym
+  getHiddenParam(g, owner).typ.skipTypes({tyOwned, tyRef}).n.sons[0].sym
 
 proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
                       c: var LiftingPass): PNode
@@ -644,7 +652,7 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
     while true:
       if access.typ == wanted:
         return makeClosure(d.graph, s, access, n.info)
-      let obj = access.typ.sons[0]
+      let obj = access.typ.skipTypes({tyOwned, tyRef})
       let upField = lookupInRecord(obj.n, getIdent(d.graph.cache, upName))
       if upField == nil:
         localError(d.graph.config, n.info, "internal error: no environment found")
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index 310263875..2dcaa7984 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -126,7 +126,7 @@ proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
 
 proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} =
   result = optNimV2 in c.graph.config.globalOptions and
-    (tfHasGCedMem in t.flags or t.isGCedMem)
+    ({tfHasGCedMem, tfHasOwned} * t.flags != {} or t.isGCedMem)
 
 proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
                         field: var PSym): bool =
@@ -171,7 +171,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
     body.add newAsgnCall(c.graph, op, x, y)
     result = true
 
-proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool =
+proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode) =
   var op = t.destructor
   if op == nil and useNoGc(c, t):
     op = produceSym(c.c, t, attachedDestructor, c.info)
@@ -182,7 +182,6 @@ proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode): bool =
     markUsed(c.graph.config, c.info, op, c.graph.usageSym)
     onUse(c.info, op)
     body.add destructorCall(c.graph, op, x)
-    result = true
   elif useNoGc(c, t):
     internalError(c.graph.config, c.info,
       "type-bound operator could not be resolved")
@@ -197,7 +196,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
         assert t.typeInst != nil
         # patch generic destructor:
         op = c.c.instTypeBoundOp(c.c, op, t.typeInst, c.info, attachedAsgn, 1)
-        t.destructor = op
+        t.attachedOps[attachedDestructor] = op
 
       markUsed(c.graph.config, c.info, op, c.graph.usageSym)
       onUse(c.info, op)
@@ -207,9 +206,9 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
   of attachedAsgn:
     result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
   of attachedSink:
-    result = considerAsgnOrSink(c, t, body, x, y, t.sink)
+    result = considerAsgnOrSink(c, t, body, x, y, t.asink)
   of attachedDeepCopy:
-    let op = t.deepCopy
+    let op = t.attachedOps[attachedDeepCopy]
     if op != nil:
       markUsed(c.graph.config, c.info, op, c.graph.usageSym)
       onUse(c.info, op)
@@ -323,8 +322,8 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     body.add moveCall
     # alternatively we could do this:
     when false:
-      doAssert t.sink != nil
-      body.add newAsgnCall(c.graph, t.sink, x, y)
+      doAssert t.asink != nil
+      body.add newAsgnCall(c.graph, t.asink, x, y)
   of attachedDestructor:
     doAssert t.destructor != nil
     body.add destructorCall(c.graph, t.destructor, x)
@@ -365,10 +364,10 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   #var disposeCall = genBuiltin(c.graph, mDispose, "dispose", x)
 
   if isFinal(elemType):
-    discard addDestructorCall(c, elemType, actions, genDeref(x))
+    addDestructorCall(c, elemType, actions, genDeref(x))
     actions.add callCodegenProc(c.graph, "nimRawDispose", c.info, x)
   else:
-    discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x))
+    addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x))
     actions.add callCodegenProc(c.graph, "nimDestroyAndDispose", c.info, x)
 
   case c.kind
@@ -502,27 +501,10 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
 proc produceSymDistinctType(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
   assert typ.kind == tyDistinct
   let baseType = typ[0]
-  case kind
-  of attachedAsgn:
-    if baseType.assignment == nil:
-      discard produceSym(c, baseType, kind, info)
-    typ.assignment = baseType.assignment
-    result = typ.assignment
-  of attachedSink:
-    if baseType.sink == nil:
-      discard produceSym(c, baseType, kind, info)
-    typ.sink = baseType.sink
-    result = typ.sink
-  of attachedDeepCopy:
-    if baseType.deepCopy == nil:
-      discard produceSym(c, baseType, kind, info)
-    typ.deepCopy = baseType.deepCopy
-    result = typ.deepCopy
-  of attachedDestructor:
-    if baseType.destructor == nil:
-      discard produceSym(c, baseType, kind, info)
-    typ.destructor = baseType.destructor
-    result = typ.destructor
+  if baseType.attachedOps[kind] == nil:
+    discard produceSym(c, baseType, kind, info)
+  typ.attachedOps[kind] = baseType.attachedOps[kind]
+  result = typ.attachedOps[kind]
 
 proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
               info: TLineInfo): PSym =
@@ -536,11 +518,7 @@ proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
   a.c = c
   let g = c.graph
   let body = newNodeI(nkStmtList, info)
-  let procname = case kind
-                 of attachedAsgn: getIdent(g.cache, "=")
-                 of attachedSink: getIdent(g.cache, "=sink")
-                 of attachedDeepCopy: getIdent(g.cache, "=deepcopy")
-                 of attachedDestructor: getIdent(g.cache, "=destroy")
+  let procname = getIdent(g.cache, AttachedOpToStr[kind])
 
   result = newSym(skProc, procname, typ.owner, info)
   a.fn = result
@@ -557,11 +535,7 @@ proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
     result.typ.addParam src
 
   # register this operation already:
-  case kind
-  of attachedAsgn: typ.assignment = result
-  of attachedSink: typ.sink = result
-  of attachedDeepCopy: typ.deepCopy = result
-  of attachedDestructor: typ.destructor = result
+  typ.attachedOps[kind] = result
 
   var tk: TTypeKind
   if optNimV2 in c.graph.config.globalOptions:
@@ -636,23 +610,15 @@ proc createTypeBoundOps(c: PContext; orig: PType; info: TLineInfo) =
   # 5. We have a (custom) generic destructor.
   let typ = canon.skipTypes({tyGenericInst, tyAlias})
   # we generate the destructor first so that other operators can depend on it:
-  if typ.destructor == nil:
-    discard produceSym(c, typ, attachedDestructor, info)
-  else:
-    inst(typ.destructor, typ)
-  if typ.assignment == nil:
-    discard produceSym(c, typ, attachedAsgn, info)
-  else:
-    inst(typ.assignment, typ)
-  if typ.sink == nil:
-    discard produceSym(c, typ, attachedSink, info)
-  else:
-    inst(typ.sink, typ)
+  for k in attachedDestructor..attachedSink:
+    if typ.attachedOps[k] == nil:
+      discard produceSym(c, typ, k, info)
+    else:
+      inst(typ.attachedOps[k], typ)
 
   if overwrite:
-    orig.destructor = typ.destructor
-    orig.assignment = typ.assignment
-    orig.sink = typ.sink
+    for k in attachedDestructor..attachedSink:
+      orig.attachedOps[k] = typ.attachedOps[k]
 
   if not isTrival(orig.destructor):
     #or not isTrival(orig.assignment) or
diff --git a/compiler/modules.nim b/compiler/modules.nim
index 3d8ced35d..1b8c7b958 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -17,9 +17,9 @@ import
 proc resetSystemArtifacts*(g: ModuleGraph) =
   magicsys.resetSysTypes(g)
 
-proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: string) =
+proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
   let
-    pck = getPackageName(graph.config, filename)
+    pck = getPackageName(graph.config, filename.string)
     pck2 = if pck.len > 0: pck else: "unknown"
     pack = getIdent(graph.cache, pck2)
   var packSym = graph.packageSyms.strTableGet(pack)
@@ -27,6 +27,22 @@ proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; fil
     packSym = newSym(skPackage, getIdent(graph.cache, pck2), nil, result.info)
     initStrTable(packSym.tab)
     graph.packageSyms.strTableAdd(packSym)
+  else:
+    let existing = strTableGet(packSym.tab, result.name)
+    if existing != nil and existing.info.fileIndex != result.info.fileIndex:
+      when false:
+        # we used to produce an error:
+        localError(graph.config, result.info,
+          "module names need to be unique per Nimble package; module clashes with " &
+            toFullPath(graph.config, existing.info.fileIndex))
+      else:
+        # but starting with version 0.20 we now produce a fake Nimble package instead
+        # to resolve the conflicts:
+        let pck3 = fakePackageName(graph.config, filename)
+        packSym = newSym(skPackage, getIdent(graph.cache, pck3), nil, result.info)
+        initStrTable(packSym.tab)
+        graph.packageSyms.strTableAdd(packSym)
+
   result.owner = packSym
   result.position = int fileIdx
 
@@ -37,13 +53,7 @@ proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; fil
   incl(result.flags, sfUsed)
   initStrTable(result.tab)
   strTableAdd(result.tab, result) # a module knows itself
-  let existing = strTableGet(packSym.tab, result.name)
-  if existing != nil and existing.info.fileIndex != result.info.fileIndex:
-    localError(graph.config, result.info,
-      "module names need to be unique per Nimble package; module clashes with " &
-        toFullPath(graph.config, existing.info.fileIndex))
-  # strTableIncl() for error corrections:
-  discard strTableIncl(packSym.tab, result)
+  strTableAdd(packSym.tab, result)
 
 proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
   # We cannot call ``newSym`` here, because we have to circumvent the ID
@@ -51,7 +61,7 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
   new(result)
   result.id = -1             # for better error checking
   result.kind = skModule
-  let filename = toFullPath(graph.config, fileIdx)
+  let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
   result.name = getIdent(graph.cache, splitFile(filename).name)
   if not isNimIdentifier(result.name.s):
     rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s)
@@ -61,8 +71,8 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
 proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym =
   result = graph.getModule(fileIdx)
   if result == nil:
-    let filename = toFullPath(graph.config, fileIdx)
-    let (r, id) = loadModuleSym(graph, fileIdx, AbsoluteFile filename)
+    let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
+    let (r, id) = loadModuleSym(graph, fileIdx, filename)
     result = r
     if result == nil:
       result = newModule(graph, fileIdx)
diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim
index f94c3d72c..d7c6b25ae 100644
--- a/compiler/packagehandling.nim
+++ b/compiler/packagehandling.nim
@@ -29,9 +29,6 @@ proc getPackageName*(conf: ConfigRef; path: string): string =
       for file in walkFiles(d / "*.nimble"):
         result = file.splitFile.name
         break packageSearch
-      for file in walkFiles(d / "*.babel"):
-        result = file.splitFile.name
-        break packageSearch
   # we also store if we didn't find anything:
   when not defined(nimNoNilSeqs):
     if result.isNil: result = ""
@@ -41,10 +38,19 @@ proc getPackageName*(conf: ConfigRef; path: string): string =
     dec parents
     if parents <= 0: break
 
+proc fakePackageName*(conf: ConfigRef; path: AbsoluteFile): string =
+  # foo/../bar becomes foo7_7bar
+  result = relativeTo(path, conf.projectPath, '/').string.multiReplace(
+    {"/": "7", "..": "_", "7": "77", "_": "__"})
+
 proc withPackageName*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
   let x = getPackageName(conf, path.string)
   if x.len == 0:
     result = path
   else:
     let (p, file, ext) = path.splitFile
-    result = p / RelativeFile((x & '_' & file) & ext)
+    if x == "stdlib":
+      # Hot code reloading now relies on 'stdlib_system' names etc.
+      result = p / RelativeFile((x & '_' & file) & ext)
+    else:
+      result = p / RelativeFile(fakePackageName(conf, path))
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index 2071ab46a..87026025f 100644
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -211,7 +211,7 @@ proc ropeConcat*(a: varargs[Rope]): Rope =
 proc prepend*(a: var Rope, b: Rope) = a = b & a
 proc prepend*(a: var Rope, b: string) = a = b & a
 
-proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
+proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope =
   var i = 0
   var length = len(frmt)
   result = nil
@@ -269,7 +269,10 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
       add(result, substr(frmt, start, i - 1))
   assert(ropeInvariant(result))
 
-proc addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) =
+proc `%`*(frmt: static[FormatStr], args: openArray[Rope]): Rope =
+  runtimeFormat(frmt, args)
+
+template addf*(c: var Rope, frmt: FormatStr, args: openArray[Rope]) =
   ## shortcut for ``add(c, frmt % args)``.
   add(c, frmt % args)
 
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index f7a7f20dc..4b269dd4a 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -72,12 +72,6 @@ type
 
   TExprFlags* = set[TExprFlag]
 
-  TTypeAttachedOp* = enum
-    attachedAsgn,
-    attachedSink,
-    attachedDeepCopy,
-    attachedDestructor
-
   PContext* = ref TContext
   TContext* = object of TPassContext # a context represents a module
     enforceVoidContext*: PType
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index f39603c0e..ead9dab27 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -937,7 +937,7 @@ proc semExprNoType(c: PContext, n: PNode): PNode =
   let isPush = hintExtendedContext in c.config.notes
   if isPush: pushInfoContext(c.config, n.info)
   result = semExpr(c, n, {efWantStmt})
-  result = discardCheck(c, result, {})
+  discardCheck(c, result, {})
   if isPush: popInfoContext(c.config)
 
 proc isTypeExpr(n: PNode): bool =
@@ -1526,17 +1526,52 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
       x.typ.flags.incl tfVarIsPtr
       #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info
 
-proc asgnToResult(c: PContext, n, le, ri: PNode) =
+proc borrowCheck(c: PContext, n, le, ri: PNode) =
+  const
+    PathKinds0 = {nkDotExpr, nkCheckedFieldExpr,
+                  nkBracketExpr, nkAddr, nkHiddenAddr,
+                  nkObjDownConv, nkObjUpConv}
+    PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv}
+
+  proc getRoot(n: PNode; followDeref: bool): PNode =
+    result = n
+    while true:
+      case result.kind
+      of nkDerefExpr, nkHiddenDeref:
+        if followDeref: result = result[0]
+        else: break
+      of PathKinds0:
+        result = result[0]
+      of PathKinds1:
+        result = result[1]
+      else: break
+
+  proc scopedLifetime(c: PContext; ri: PNode): bool {.inline.} =
+    let n = getRoot(ri, followDeref = false)
+    result = (ri.kind in nkCallKinds+{nkObjConstr}) or
+      (n.kind == nkSym and n.sym.owner == c.p.owner)
+
+  proc escapes(c: PContext; le: PNode): bool {.inline.} =
+    # param[].foo[] = self  definitely escapes, we don't need to
+    # care about pointer derefs:
+    let n = getRoot(le, followDeref = true)
+    result = n.kind == nkSym and n.sym.kind == skParam
+
   # Special typing rule: do not allow to pass 'owned T' to 'T' in 'result = x':
-  if ri.typ != nil and ri.typ.skipTypes(abstractInst).kind == tyOwned and
-      le.typ != nil and le.typ.skipTypes(abstractInst).kind != tyOwned and ri.kind in nkCallKinds:
-    localError(c.config, n.info, "cannot return an owned pointer as an unowned pointer; " &
-      "use 'owned(" & typeToString(le.typ) & ")' as the return type")
+  const absInst = abstractInst - {tyOwned}
+  if ri.typ != nil and ri.typ.skipTypes(absInst).kind == tyOwned and
+      le.typ != nil and le.typ.skipTypes(absInst).kind != tyOwned and
+      scopedLifetime(c, ri):
+    if le.kind == nkSym and le.sym.kind == skResult:
+      localError(c.config, n.info, "cannot return an owned pointer as an unowned pointer; " &
+        "use 'owned(" & typeToString(le.typ) & ")' as the return type")
+    elif escapes(c, le):
+      localError(c.config, n.info,
+        "assignment produces a dangling ref: the unowned ref lives longer than the owned ref")
 
 template resultTypeIsInferrable(typ: PType): untyped =
   typ.isMetaType and typ.kind != tyTypeDesc
 
-
 proc goodLineInfo(arg: PNode): TLineinfo =
   if arg.kind == nkStmtListExpr and arg.len > 0:
     goodLineInfo(arg[^1])
@@ -1623,7 +1658,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
           c.p.owner.typ.sons[0] = rhsTyp
         else:
           typeMismatch(c.config, n.info, lhs.typ, rhsTyp)
-      asgnToResult(c, n, n.sons[0], rhs)
+    borrowCheck(c, n, lhs, rhs)
 
     n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1]))
     liftTypeBoundOps(c, lhs.typ, lhs.info)
@@ -1673,7 +1708,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
       a.sons[1] = result
       result = semAsgn(c, a)
   else:
-    result = discardCheck(c, result, {})
+    discardCheck(c, result, {})
 
   if c.p.owner.kind notin {skMacro, skTemplate} and
      c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index f290a08d5..7c75d8624 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -322,6 +322,39 @@ proc semOf(c: PContext, n: PNode): PNode =
   n.typ = getSysType(c.graph, n.info, tyBool)
   result = n
 
+proc semUnown(c: PContext; n: PNode): PNode =
+  proc unownedType(c: PContext; t: PType): PType =
+    case t.kind
+    of tyTuple:
+      var elems = newSeq[PType](t.len)
+      var someChange = false
+      for i in 0..<t.len:
+        elems[i] = unownedType(c, t[i])
+        if elems[i] != t[i]: someChange = true
+      if someChange:
+        result = newType(tyTuple, t.owner)
+        # we have to use 'rawAddSon' here so that type flags are
+        # properly computed:
+        for e in elems: result.rawAddSon(e)
+      else:
+        result = t
+    of tyOwned: result = t.sons[0]
+    of tySequence, tyOpenArray, tyArray, tyVarargs, tyVar, tyLent,
+       tyGenericInst, tyAlias:
+      let L = t.len-1
+      let b = unownedType(c, t[L])
+      if b != t[L]:
+        result = copyType(t, t.owner, keepId = false)
+        result[L] = b
+        result.flags.excl tfHasOwned
+      else:
+        result = t
+    else:
+      result = t
+
+  result = copyTree(n[1])
+  result.typ = unownedType(c, result.typ)
+
 proc magicsAfterOverloadResolution(c: PContext, n: PNode,
                                    flags: TExprFlags): PNode =
   ## This is the preferred code point to implement magics.
@@ -433,4 +466,6 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
     let t = n[1].typ.skipTypes(abstractVar)
     if t.destructor != nil:
       result.sons[0] = newSymNode(t.destructor)
+  of mUnown:
+    result = semUnown(c, n)
   else: result = n
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index f2a6a7891..c835ce8e3 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -527,7 +527,7 @@ proc isNoEffectList(n: PNode): bool {.inline.} =
   n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil)
 
 proc isTrival(caller: PNode): bool {.inline.} =
-  result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil}
+  result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved}
 
 proc trackOperand(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) =
   let a = skipConvAndClosure(n)
@@ -738,7 +738,7 @@ proc track(tracked: PEffects, n: PNode) =
         mergeTags(tracked, effectList.sons[tagEffects], n)
         gcsafeAndSideeffectCheck()
     if a.kind != nkSym or a.sym.magic != mNBindSym:
-      for i in 1 ..< len(n): trackOperand(tracked, n.sons[i], paramType(op, i), a)
+      for i in 1 ..< n.len: trackOperand(tracked, n.sons[i], paramType(op, i), a)
     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]
@@ -754,7 +754,7 @@ proc track(tracked: PEffects, n: PNode) =
       track(tracked, n.sons[i])
   of nkDotExpr:
     guardDotAccess(tracked, n)
-    for i in 0 ..< len(n): track(tracked, n.sons[i])
+    for i in 0 ..< n.len: track(tracked, n.sons[i])
   of nkCheckedFieldExpr:
     track(tracked, n.sons[0])
     if warnProveField in tracked.config.notes:
@@ -821,7 +821,7 @@ proc track(tracked: PEffects, n: PNode) =
   of nkForStmt, nkParForStmt:
     # we are very conservative here and assume the loop is never executed:
     let oldState = tracked.init.len
-    for i in 0 .. len(n)-3:
+    for i in 0 .. n.len-3:
       let it = n[i]
       track(tracked, it)
       if tracked.owner.kind != skMacro:
@@ -830,13 +830,19 @@ proc track(tracked: PEffects, n: PNode) =
             createTypeBoundOps(tracked.c, x.typ, x.info)
         else:
           createTypeBoundOps(tracked.c, it.typ, it.info)
-    for i in len(n)-2..len(n)-1:
-      track(tracked, n.sons[i])
+    let iterCall = n[n.len-2]
+    let loopBody = n[n.len-1]
+    if tracked.owner.kind != skMacro and iterCall.safelen > 1:
+      # XXX this is a bit hacky:
+      if iterCall[1].typ != nil and iterCall[1].typ.skipTypes(abstractVar).kind notin {tyVarargs, tyOpenArray}:
+        createTypeBoundOps(tracked.c, iterCall[1].typ, iterCall[1].info)
+    track(tracked, iterCall)
+    track(tracked, loopBody)
     setLen(tracked.init, oldState)
   of nkObjConstr:
     when false: track(tracked, n.sons[0])
     let oldFacts = tracked.guards.s.len
-    for i in 1 ..< len(n):
+    for i in 1 ..< n.len:
       let x = n.sons[i]
       track(tracked, x)
       if x.sons[0].kind == nkSym and sfDiscriminant in x.sons[0].sym.flags:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 067b3bc9c..b8f35408f 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -43,7 +43,7 @@ proc semDiscard(c: PContext, n: PNode): PNode =
     n.sons[0] = semExprWithType(c, n.sons[0])
     let sonType = n.sons[0].typ
     let sonKind = n.sons[0].kind
-    if isEmptyType(sonType) or sonType.kind == tyNone or n.sons[0].kind == nkTypeOfExpr:
+    if isEmptyType(sonType) or sonType.kind in {tyNone, tyTypeDesc} or sonKind == nkTypeOfExpr:
       localError(c.config, n.info, errInvalidDiscard)
     if sonType.kind == tyProc and sonKind notin nkCallKinds:
       # tyProc is disallowed to prevent ``discard foo`` to be valid, when ``discard foo()`` is meant.
@@ -130,13 +130,13 @@ proc fixNilType(c: PContext; n: PNode) =
     for it in n: fixNilType(c, it)
   n.typ = nil
 
-proc discardCheck(c: PContext, expr: PNode, flags: TExprFlags): PNode =
-  result = expr
+proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
   if c.matchedConcept != nil or efInTypeof in flags: return
 
   if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
     if implicitlyDiscardable(result):
-      result = newNode(nkDiscardStmt, result.info, @[result])
+      var n = newNodeI(nkDiscardStmt, result.info, 1)
+      n[0] = result
     elif result.typ.kind != tyError and c.config.cmd != cmdInteractive:
       var n = result
       while n.kind in skipForDiscardable: n = n.lastSon
@@ -168,8 +168,7 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode =
     else: illFormedAst(it, c.config)
   if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
       (not hasElse and efInTypeof notin flags):
-    for it in n:
-      it.sons[^1] = discardCheck(c, it.sons[^1], flags)
+    for it in n: discardCheck(c, it.lastSon, flags)
     result.kind = nkIfStmt
     # propagate any enforced VoidContext:
     if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext
@@ -267,14 +266,12 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
 
   dec c.p.inTryStmt
   if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}:
-    n.sons[0] = discardCheck(c, n.sons[0], flags)
-    for i in 1..n.len-1:
-      n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags)
+    discardCheck(c, n.sons[0], flags)
+    for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags)
     if typ == c.enforceVoidContext:
       result.typ = c.enforceVoidContext
   else:
-    if n.lastSon.kind == nkFinally:
-      n.sons[^1].sons[^1] = discardCheck(c, n.sons[^1].sons[^1], flags)
+    if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags)
     n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info)
     for i in 1..last:
       var it = n.sons[i]
@@ -474,10 +471,14 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
                      ($typ.kind).substr(2).toLowerAscii)
         elif typ.kind == tyProc and tfUnresolved in typ.flags:
           localError(c.config, def.info, errProcHasNoConcreteType % def.renderTree)
-        elif symkind == skVar and typ.kind == tyOwned and def.kind notin nkCallKinds:
-          # special type inference rule: 'var it = ownedPointer' is turned
-          # into an unowned pointer.
-          typ = typ.lastSon
+        when false:
+          # XXX This typing rule is neither documented nor complete enough to
+          # justify it. Instead use the newer 'unowned x' until we figured out
+          # a more general solution.
+          if symkind == skVar and typ.kind == tyOwned and def.kind notin nkCallKinds:
+            # special type inference rule: 'var it = ownedPointer' is turned
+            # into an unowned pointer.
+            typ = typ.lastSon
     else:
       if symkind == skLet: localError(c.config, a.info, errLetNeedsInit)
 
@@ -729,7 +730,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
   openScope(c)
   n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags)
   if efInTypeof notin flags:
-    n.sons[^1] = discardCheck(c, n.sons[^1], flags)
+    discardCheck(c, n.sons[length-1], flags)
   closeScope(c)
   dec(c.p.nestedLoopCounter)
 
@@ -919,8 +920,7 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
   closeScope(c)
   if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
       (not hasElse and efInTypeof notin flags):
-    for i in 1..n.len-1:
-      n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags)
+    for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags)
     # propagate any enforced VoidContext:
     if typ == c.enforceVoidContext:
       result.typ = c.enforceVoidContext
@@ -1568,7 +1568,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
       if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
         obj = canonType(c, obj)
         if obj.destructor.isNil:
-          obj.destructor = s
+          obj.attachedOps[attachedDestructor] = s
         else:
           prevDestructor(c, obj.destructor, obj, n.info)
         noError = true
@@ -1592,7 +1592,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
         elif t.kind == tyGenericInvocation: t = t.sons[0]
         else: break
       if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}:
-        if t.deepCopy.isNil: t.deepCopy = s
+        if t.attachedOps[attachedDeepCopy].isNil: t.attachedOps[attachedDeepCopy] = s
         else:
           localError(c.config, n.info, errGenerated,
                      "cannot bind another 'deepCopy' to: " & typeToString(t))
@@ -1631,11 +1631,11 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
         # attach these ops to the canonical tySequence
         obj = canonType(c, obj)
         #echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj)
-        let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink)
-        if opr[].isNil:
-          opr[] = s
+        let k = if name == "=": attachedAsgn else: attachedSink
+        if obj.attachedOps[k].isNil:
+          obj.attachedOps[k] = s
         else:
-          prevDestructor(c, opr[], obj, n.info)
+          prevDestructor(c, obj.attachedOps[k], obj, n.info)
         if obj.owner.getModule != s.getModule:
           localError(c.config, n.info, errGenerated,
             "type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
@@ -1828,7 +1828,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
 
   if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
   if s.name.s[0] in {'.', '('}:
-    if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}:
+    if s.name.s in [".", ".()", ".="] and {Feature.destructor, dotOperators} * c.features == {}:
       localError(c.config, n.info, "the overloaded " & s.name.s &
         " operator has to be enabled with {.experimental: \"dotOperators\".}")
     elif s.name.s == "()" and callOperator notin c.features:
@@ -2124,7 +2124,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
       n.typ = n.sons[i].typ
       if not isEmptyType(n.typ): n.kind = nkStmtListExpr
     elif i != last or voidContext:
-      n.sons[i] = discardCheck(c, n.sons[i], flags)
+      discardCheck(c, n.sons[i], flags)
     else:
       n.typ = n.sons[i].typ
       if not isEmptyType(n.typ): n.kind = nkStmtListExpr
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index cbb7a95c9..2854c90ae 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -559,9 +559,11 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
   else:
     s = semIdentVis(c, skTemplate, n.sons[0], {})
 
-  if s.owner != nil and sfSystemModule in s.owner.flags and
-      s.name.s in ["!=", ">=", ">", "incl", "excl", "in", "notin", "isnot"]:
-    incl(s.flags, sfCallsite)
+  if s.owner != nil:
+    const names = ["!=", ">=", ">", "incl", "excl", "in", "notin", "isnot"]
+    if sfSystemModule in s.owner.flags and s.name.s in names or
+       s.owner.name.s == "vm" and s.name.s == "stackTrace":
+      incl(s.flags, sfCallsite)
 
   styleCheckDef(c.config, s)
   onDef(n[0].info, s)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index e24597956..d0c8c8520 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1348,8 +1348,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
   # special check for generic object with
   # generic/partial specialized parent
   let tx = result.skipTypes(abstractPtrs, 50)
-  if tx.isNil:
-    localError(c.config, n.info, "invalid recursion in type '$1'" % typeToString(result[0]))
+  if tx.isNil or isTupleRecursive(tx):
+    localError(c.config, n.info, "illegal recursion in type '$1'" % typeToString(result[0]))
     return errorType(c)
   if tx != result and tx.kind == tyObject and tx.sons[0] != nil:
     semObjectTypeForInheritedGenericInst(c, n, tx)
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 931a54f96..5d2c4203c 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -380,13 +380,13 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
   if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
   rawAddSon(result, newbody)
   checkPartialConstructedType(cl.c.config, cl.info, newbody)
-  let dc = newbody.deepCopy
+  let dc = newbody.attachedOps[attachedDeepCopy]
   if not cl.allowMetaTypes:
-    if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
+    if dc != nil and sfFromGeneric notin newbody.attachedOps[attachedDeepCopy].flags:
       # 'deepCopy' needs to be instantiated for
       # generics *when the type is constructed*:
-      newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
-                                              attachedDeepCopy, 1)
+      newbody.attachedOps[attachedDeepCopy] = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
+                                                                   attachedDeepCopy, 1)
     if bodyIsNew and newbody.typeInst == nil:
       #doassert newbody.typeInst == nil
       newbody.typeInst = result
@@ -592,7 +592,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
         skipIntLiteralParams(result)
 
       of tySequence:
-        if cl.isReturnType and cl.c.config.selectedGc == gcDestructors and result.destructor.isNil and
+        if cl.isReturnType and cl.c.config.selectedGc == gcDestructors and
+            result.attachedOps[attachedDestructor].isNil and
             result[0].kind != tyEmpty and optNimV2 notin cl.c.config.globalOptions:
           let s = cl.c.graph.sysTypes[tySequence]
           var old = copyType(s, s.owner, keepId=false)
@@ -601,9 +602,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
           old.n = nil
           old.flags = {tfHasAsgn}
           old.addSonSkipIntLit result[0]
-          result.destructor = old.destructor
-          result.assignment = old.assignment
-          result.sink = old.sink
+          result.attachedOps = old.attachedOps
           cl.c.typesWithOps.add((result, old))
 
       else: discard
@@ -619,19 +618,19 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
         result.n = replaceObjBranches(cl, result.n)
 
 template typeBound(c, newty, oldty, field, info) =
-  let opr = newty.field
+  let opr = newty.attachedOps[field]
   if opr != nil and sfFromGeneric notin opr.flags:
     # '=' needs to be instantiated for generics when the type is constructed:
     #echo "DESTROY: instantiating ", astToStr(field), " for ", typeToString(oldty)
-    newty.field = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1)
+    newty.attachedOps[field] = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1)
 
 proc instAllTypeBoundOp*(c: PContext, info: TLineInfo) =
   var i = 0
   while i < c.typesWithOps.len:
     let (newty, oldty) = c.typesWithOps[i]
-    typeBound(c, newty, oldty, destructor, info)
-    typeBound(c, newty, oldty, sink, info)
-    typeBound(c, newty, oldty, assignment, info)
+    typeBound(c, newty, oldty, attachedDestructor, info)
+    typeBound(c, newty, oldty, attachedSink, info)
+    typeBound(c, newty, oldty, attachedAsgn, info)
     inc i
   setLen(c.typesWithOps, 0)
 
diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim
index 8e4458942..c69d58cd3 100644
--- a/compiler/sighashes.nim
+++ b/compiler/sighashes.nim
@@ -349,7 +349,7 @@ proc hashBodyTree(graph: ModuleGraph, c: var MD5Context, n: PNode) =
     c &= n.strVal
   else:
     for i in 0..<n.len:
-      hashTree(c, n.sons[i])
+      hashBodyTree(graph, c, n.sons[i])
 
 proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash =
   ## compute unique digest of the proc/func/method symbols
@@ -369,7 +369,7 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash =
   if sym.ast != nil:
     md5Init(c)
     c.md5Update(cast[cstring](result.addr), sizeof(result))
-    c.hashTree(sym.ast[bodyPos])
+    hashBodyTree(graph, c, sym.ast[bodyPos])
     c.md5Final(result.Md5Digest)
     graph.symBodyHashes[sym.id] = result
 
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 7083b052f..194055da9 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -2092,14 +2092,21 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
         result = localConvMatch(c, m, f, a, arg)
       else:
         r = typeRel(m, base(f), a)
-        if r >= isGeneric:
+        case r
+        of isGeneric:
           inc(m.convMatches)
           result = copyTree(arg)
-          if r == isGeneric:
-            result.typ = getInstantiatedType(c, arg, m, base(f))
+          result.typ = getInstantiatedType(c, arg, m, base(f))
           m.baseTypeMatch = true
-        # bug #4799, varargs accepting subtype relation object
-        elif r == isSubtype:
+        of isFromIntLit:
+          inc(m.intConvMatches, 256)
+          result = implicitConv(nkHiddenStdConv, f[0], arg, m, c)
+          m.baseTypeMatch = true
+        of isEqual:
+          inc(m.convMatches)
+          result = copyTree(arg)
+          m.baseTypeMatch = true
+        of isSubtype: # bug #4799, varargs accepting subtype relation object
           inc(m.subtypeMatches)
           if base(f).kind == tyTypeDesc:
             result = arg
diff --git a/compiler/transf.nim b/compiler/transf.nim
index c455e3319..25a8bc5dd 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -651,6 +651,8 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
       var t = formal.typ
       if formal.ast != nil and formal.ast.typ.destructor != nil and t.destructor == nil:
         t = formal.ast.typ # better use the type that actually has a destructor.
+      elif t.destructor == nil and arg.typ.destructor != nil:
+        t = arg.typ
       # generate a temporary and produce an assignment statement:
       var temp = newTemp(c, t, formal.info)
       #temp.sym.flags.incl sfCursor
diff --git a/compiler/trees.nim b/compiler/trees.nim
index 55a3c619e..a06e6152c 100644
--- a/compiler/trees.nim
+++ b/compiler/trees.nim
@@ -101,7 +101,7 @@ proc isDeepConstExpr*(n: PNode): bool =
       if not isDeepConstExpr(n.sons[i]): return false
     if n.typ.isNil: result = true
     else:
-      let t = n.typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink})
+      let t = n.typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned})
       if t.kind in {tyRef, tyPtr}: return false
       if t.kind != tyObject or not isCaseObj(t.n):
         result = true
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index a71ebc351..9c6f8a970 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -931,6 +931,21 @@ proc ldNullOpcode(t: PType): TOpcode =
   assert t != nil
   if fitsRegister(t): opcLdNullReg else: opcLdNull
 
+proc whichAsgnOpc(n: PNode): TOpcode =
+  case n.typ.skipTypes(abstractRange+{tyOwned}-{tyTypeDesc}).kind
+  of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
+    opcAsgnInt
+  of tyString, tyCString:
+    opcAsgnStr
+  of tyFloat..tyFloat128:
+    opcAsgnFloat
+  of tyRef, tyNil, tyVar, tyLent, tyPtr:
+    opcAsgnRef
+  else:
+    opcAsgnComplex
+
+proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc
+
 proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   case m
   of mAnd: c.genAndOr(n, opcFJmp, dest)
@@ -957,7 +972,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.genNarrow(n.sons[1], d)
     c.genAsgnPatch(n.sons[1], d)
     c.freeTemp(d)
-  of mOrd, mChr, mArrToSeq: c.gen(n.sons[1], dest)
+  of mOrd, mChr, mArrToSeq, mUnown: c.gen(n.sons[1], dest)
   of mNew, mNewFinalize:
     unused(c, n, dest)
     c.genNew(n)
@@ -1330,6 +1345,17 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mRunnableExamples:
     discard "just ignore any call to runnableExamples"
   of mDestroy: discard "ignore calls to the default destructor"
+  of mMove:
+    let arg = n[1]
+    let a = c.genx(arg)
+    assert dest >= 0
+    if dest < 0: dest = c.getTemp(arg.typ)
+    gABC(c, arg, whichAsgnOpc(arg), dest, a, 1)
+    # XXX use ldNullOpcode() here?
+    c.gABx(n, opcLdNull, a, c.genType(arg.typ))
+    c.gABx(n, opcNodeToReg, a, a)
+    c.genAsgnPatch(arg, a)
+    c.freeTemp(a)
   else:
     # mGCref, mGCunref,
     globalError(c.config, n.info, "cannot generate code for: " & $m)
@@ -1423,21 +1449,6 @@ proc genDeref(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) =
     if {gfNodeAddr, gfNode} * flags == {} and fitsRegister(n.typ):
       c.gABC(n, opcNodeToReg, dest, dest)
 
-proc whichAsgnOpc(n: PNode): TOpcode =
-  case n.typ.skipTypes(abstractRange+{tyOwned}-{tyTypeDesc}).kind
-  of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
-    opcAsgnInt
-  of tyString, tyCString:
-    opcAsgnStr
-  of tyFloat..tyFloat128:
-    opcAsgnFloat
-  of tyRef, tyNil, tyVar, tyLent, tyPtr:
-    opcAsgnRef
-  else:
-    opcAsgnComplex
-
-proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc
-
 proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
   let tmp = c.genx(ri)
   assert dest >= 0
@@ -1585,8 +1596,8 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
   #   var decls{.compileTime.}: seq[NimNode] = @[]
   let dest = c.getTemp(s.typ)
   c.gABx(n, opcLdGlobal, dest, s.position)
-  if s.ast != nil:
-    let tmp = c.genx(s.ast)
+  if s.astdef != nil:
+    let tmp = c.genx(s.astdef)
     c.genAdditionalCopy(n, opcWrDeref, dest, 0, tmp)
     c.freeTemp(dest)
     c.freeTemp(tmp)
diff --git a/doc/docgen.rst b/doc/docgen.rst
index 84969d8ee..bd7f9bf10 100644
--- a/doc/docgen.rst
+++ b/doc/docgen.rst
@@ -18,6 +18,21 @@ from input .nim files and projects, as well as HTML and LaTeX from input RST
 dependencies (``import``), any top-level documentation comments (##), and
 exported symbols (*), including procedures, types, and variables.
 
+Quick start
+-----------
+
+Generate HTML documentation for a file:
+
+::
+  nim doc <filename>.nim
+
+Generate HTML documentation for a whole project:
+
+::
+  # delete any htmldocs/*.idx file before starting
+  nim doc --project --index:on --git.url:<url> --git.commit:<tag> <main_filename>.nim
+  nim buildIndex -o:htmldocs/theindex.html htmldocs
+
 
 Documentation Comments
 ----------------------
@@ -186,21 +201,25 @@ file.
 See source switch
 -----------------
 
+The ``docSeeSrcUrl`` switch is deprecated. Use:
+
 ::
-  nim doc2 --docSeeSrcUrl:txt filename.nim
+  nim doc2 --git.url:<url> filename.nim
+
+With the ``git.url`` switch the *See source* hyperlink will appear below each
+documented item in your source code pointing to the implementation of that
+item on a GitHub repository.
+You can click the link to see the implementation of the item.
+
+The ``git.commit`` switch overrides the hardcoded `devel` branch in config/nimdoc.cfg.
+This is useful to link to a different branch e.g. `--git.commit:master`,
+or to a tag e.g. `--git.commit:1.2.3` or a commit.
 
-When you pass the ``docSeeSrcUrl`` switch to docgen, after each documented item
-in your source code the hyper link *See source* will appear pointing to the
-implementation of that item on a GitHub repository. You can click the link to
-see the implementation of the item.
+Source URLs are generated as `href="${url}/tree/${commit}/${path}#L${line}"` by default and this compatible with GitHub but not with GitLab.
 
-If you want to reuse this feature in your own documentation you will have to
-modify ``config/nimdoc.cfg`` to contain a ``doc.item.seesrc`` value with a
-hyper link to your own code repository. As you will see by the comments in that
-file, the value ``txt`` passed on the command line will be used in the HTML
-template along others like ``$path`` and ``$line``.
+You can edit ``config/nimdoc.cfg`` and modify the ``doc.item.seesrc`` value with a hyperlink to your own code repository.
 
-In the case of Nim's own documentation, the ``txt`` value is just a commit
+In the case of Nim's own documentation, the ``commit`` value is just a commit
 hash to append to a formatted URL to https://github.com/Araq/Nim. The
 ``tools/nimweb.nim`` helper queries the current git commit hash during doc
 generation, but since you might be working on an unpublished repository, it
@@ -231,7 +250,7 @@ HTML anchor generation
 ======================
 
 When you run the ``rst2html`` command, all sections in the RST document will
-get an anchor you can hyper link to. Usually you can guess the anchor lower
+get an anchor you can hyperlink to. Usually you can guess the anchor lower
 casing the section title and replacing spaces with dashes, and in any case you
 can get it from the table of contents. But when you run the ``doc`` or ``doc2``
 commands to generate API documentation, some symbol get one or two anchors at
@@ -249,7 +268,7 @@ will be their unquoted value after removing parameters, return types and
 pragmas. The plain name allows short and nice linking of symbols which works
 unless you have a module with collisions due to overloading.
 
-If you hyper link a plain name symbol and there are other matches on the same
+If you hyperlink a plain name symbol and there are other matches on the same
 HTML file, most browsers will go to the first one. To differentiate the rest,
 you will need to use the complex name. A complex name for a callable type is
 made up from several parts:
@@ -313,14 +332,14 @@ columns is:
 
 1. Mandatory term being indexed. Terms can include quoting according to
    Nim's rules (eg. \`^\`).
-2. Base filename plus anchor hyper link (eg.
+2. Base filename plus anchor hyperlink (eg.
    ``algorithm.html#*,int,SortOrder``).
-3. Optional human readable string to display as hyper link. If the value is not
-   present or is the empty string, the hyper link will be rendered
+3. Optional human readable string to display as hyperlink. If the value is not
+   present or is the empty string, the hyperlink will be rendered
    using the term. Prefix whitespace indicates that this entry is
    not for an API symbol but for a TOC entry.
-4. Optional title or description of the hyper link. Browsers usually display
-   this as a tooltip after hovering a moment over the hyper link.
+4. Optional title or description of the hyperlink. Browsers usually display
+   this as a tooltip after hovering a moment over the hyperlink.
 
 The index generation tools try to differentiate between documentation
 generated from ``.nim`` files and documentation generated from ``.txt`` or
diff --git a/doc/manual.rst b/doc/manual.rst
index 2824f186a..d4d2aae91 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -22,7 +22,10 @@ precise wording. This manual is constantly evolving into a proper specification.
 **Note**: The experimental features of Nim are
 covered `here <manual_experimental.html>`_.
 
-This document describes the lexis, the syntax, and the semantics of Nim.
+This document describes the lexis, the syntax, and the semantics of the Nim language.
+
+To learn how to compile Nim programs and generate documentation see
+`Compiler User Guide <nimc.html>`_ and `DocGen Tools Guide <docgen.html>`_.
 
 The language constructs are explained using an extended BNF, in which ``(a)*``
 means 0 or more ``a``'s, ``a+`` means 1 or more ``a``'s, and ``(a)?`` means an
@@ -2357,7 +2360,7 @@ argument's resolution:
   rem unresolvedExpression(undeclaredIdentifier)
 
 ``untyped`` and ``varargs[untyped]`` are the only metatype that are lazy in this sense, the other
-metatypes ``typed`` and ``type`` are not lazy.
+metatypes ``typed`` and ``typedesc`` are not lazy.
 
 
 Varargs matching
@@ -4434,7 +4437,7 @@ templates:
 | ``notin`` and ``isnot`` have the obvious meanings.
 
 The "types" of templates can be the symbols ``untyped``,
-``typed`` or ``type``. These are "meta types", they can only be used in certain
+``typed`` or ``typedesc``. These are "meta types", they can only be used in certain
 contexts. Regular types can be used too; this implies that ``typed`` expressions
 are expected.
 
@@ -4598,7 +4601,7 @@ In templates identifiers can be constructed with the backticks notation:
 .. code-block:: nim
     :test: "nim c $1"
 
-  template typedef(name: untyped, typ: type) =
+  template typedef(name: untyped, typ: typedesc) =
     type
       `T name`* {.inject.} = typ
       `P name`* {.inject.} = ref `T name`
@@ -4660,7 +4663,7 @@ template cannot be accessed in the instantiation context:
 .. code-block:: nim
     :test: "nim c $1"
 
-  template newException*(exceptn: type, message: string): untyped =
+  template newException*(exceptn: typedesc, message: string): untyped =
     var
       e: ref exceptn  # e is implicitly gensym'ed here
     new(e)
@@ -5095,27 +5098,26 @@ expression by coercing it to a corresponding ``static`` type:
 The complier will report any failure to evaluate the expression or a
 possible type mismatch error.
 
-type[T]
--------
+typedesc[T]
+-----------
 
 In many contexts, Nim allows you to treat the names of types as regular
 values. These values exists only during the compilation phase, but since
-all values must have a type, ``type`` is considered their special type.
+all values must have a type, ``typedesc`` is considered their special type.
 
-``type`` acts like a generic type. For instance, the type of the symbol
-``int`` is ``type[int]``. Just like with regular generic types, when the
-generic param is ommited, ``type`` denotes the type class of all types.
-As a syntactic convenience, you can also use ``type`` as a modifier.
-``type int`` is considered the same as ``type[int]``.
+``typedesc`` acts like a generic type. For instance, the type of the symbol
+``int`` is ``typedesc[int]``. Just like with regular generic types, when the
+generic param is ommited, ``typedesc`` denotes the type class of all types.
+As a syntactic convenience, you can also use ``typedesc`` as a modifier.
 
-Procs featuring ``type`` params are considered implicitly generic.
+Procs featuring ``typedesc`` params are considered implicitly generic.
 They will be instantiated for each unique combination of supplied types
 and within the body of the proc, the name of each param will refer to
 the bound concrete type:
 
 .. code-block:: nim
 
-  proc new(T: type): ref T =
+  proc new(T: typedesc): ref T =
     echo "allocating ", T.name
     new(result)
 
@@ -5126,14 +5128,14 @@ When multiple type params are present, they will bind freely to different
 types. To force a bind-once behavior one can use an explicit generic param:
 
 .. code-block:: nim
-  proc acceptOnlyTypePairs[T, U](A, B: type[T]; C, D: type[U])
+  proc acceptOnlyTypePairs[T, U](A, B: typedesc[T]; C, D: typedesc[U])
 
 Once bound, type params can appear in the rest of the proc signature:
 
 .. code-block:: nim
     :test: "nim c $1"
 
-  template declareVariableWithType(T: type, value: T) =
+  template declareVariableWithType(T: typedesc, value: T) =
     var x: T = value
 
   declareVariableWithType int, 42
@@ -5145,8 +5147,8 @@ types that will match the type param:
 .. code-block:: nim
     :test: "nim c $1"
 
-  template maxval(T: type int): int = high(int)
-  template maxval(T: type float): float = Inf
+  template maxval(T: typedesc[int]): int = high(int)
+  template maxval(T: typedesc[float]): float = Inf
 
   var i = int.maxval
   var f = float.maxval
@@ -5159,7 +5161,8 @@ The constraint can be a concrete type or a type class.
 typeof operator
 ---------------
 
-**Note**: ``typeof(x)`` can also be written as ``type(x)``.
+**Note**: ``typeof(x)`` can also be written as ``type(x)`` but ``type(x)``
+is discouraged.
 
 You can obtain the type of a given expression by constructing a ``typeof``
 value from it (in many other languages this is known as the `typeof`:idx:
@@ -6581,7 +6584,7 @@ Custom pragmas are defined using templates annotated with pragma ``pragma``:
 .. code-block:: nim
   template dbTable(name: string, table_space: string = "") {.pragma.}
   template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
-  template dbForeignKey(t: type) {.pragma.}
+  template dbForeignKey(t: typedesc) {.pragma.}
   template dbIgnore {.pragma.}
 
 
diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst
index 95eab0100..228c20339 100644
--- a/doc/manual_experimental.rst
+++ b/doc/manual_experimental.rst
@@ -486,9 +486,9 @@ The concept types can be parametric just like the regular generic types:
     M.data[m * M.N + n] = v
 
   # Adapt the Matrix type to the concept's requirements
-  template Rows*(M: type Matrix): int = M.M
-  template Cols*(M: type Matrix): int = M.N
-  template ValueType*(M: type Matrix): type = M.T
+  template Rows*(M: typedesc[Matrix]): int = M.M
+  template Cols*(M: typedesc[Matrix]): int = M.N
+  template ValueType*(M: typedesc[Matrix]): typedesc = M.T
 
   -------------
   ### usage.nim
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 7a8755299..6a4c094d0 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -1076,20 +1076,24 @@ proc expectKind*(n: NimNode; k: set[NimNodeKind]) {.compileTime.} =
   ## macros that check the AST that is passed to them.
   if n.kind notin k: error("Expected one of " & $k & ", got " & $n.kind, n)
 
-proc newProc*(name = newEmptyNode(); params: openArray[NimNode] = [newEmptyNode()];
-    body: NimNode = newStmtList(), procType = nnkProcDef): NimNode {.compileTime.} =
+proc newProc*(name = newEmptyNode();
+              params: openArray[NimNode] = [newEmptyNode()];
+              body: NimNode = newStmtList();
+              procType = nnkProcDef;
+              pragmas: NimNode = newEmptyNode()): NimNode {.compileTime.} =
   ## shortcut for creating a new proc
   ##
   ## The ``params`` array must start with the return type of the proc,
   ## followed by a list of IdentDefs which specify the params.
   if procType notin RoutineNodes:
     error("Expected one of " & $RoutineNodes & ", got " & $procType)
+  pragmas.expectKind({nnkEmpty, nnkPragma})
   result = newNimNode(procType).add(
     name,
     newEmptyNode(),
     newEmptyNode(),
     newNimNode(nnkFormalParams).add(params),
-    newEmptyNode(),  # pragmas
+    pragmas,
     newEmptyNode(),
     body)
 
diff --git a/lib/core/runtime_v2.nim b/lib/core/runtime_v2.nim
index f76fd2f3e..0165833b4 100644
--- a/lib/core/runtime_v2.nim
+++ b/lib/core/runtime_v2.nim
@@ -47,8 +47,9 @@ proc nimNewObj(size: int): pointer {.compilerRtl.} =
   when defined(nimscript):
     discard
   elif defined(useMalloc):
-    result = c_malloc(s) +! sizeof(RefHeader)
-    nimZeroMem(result, s)
+    var orig = c_malloc(s)
+    nimZeroMem(orig, s)
+    result = orig +! sizeof(RefHeader)
   else:
     result = alloc0(s) +! sizeof(RefHeader)
   inc allocs
diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim
index a79f0c2f5..9c88040ba 100644
--- a/lib/core/seqs.nim
+++ b/lib/core/seqs.nim
@@ -104,6 +104,10 @@ proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl, raises: [].} =
 proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.
     compilerRtl, noSideEffect, raises: [].} =
   {.noSideEffect.}:
+    template `+!`(p: pointer, s: int): pointer =
+      cast[pointer](cast[int](p) +% s)
+
+    const headerSize = sizeof(int) + sizeof(Allocator)
     if addlen <= 0:
       result = p
     elif p == nil:
@@ -112,14 +116,23 @@ proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.
       # Note: this means we cannot support things that have internal pointers as
       # they get reallocated here. This needs to be documented clearly.
       var p = cast[ptr PayloadBase](p)
-      let allocator = if p.allocator == nil: getLocalAllocator() else: p.allocator
       let cap = max(resize(p.cap), len+addlen)
-      var q = cast[ptr PayloadBase](allocator.realloc(allocator, p,
-        sizeof(int) + sizeof(Allocator) + elemSize * p.cap,
-        sizeof(int) + sizeof(Allocator) + elemSize * cap))
-      q.allocator = allocator
-      q.cap = cap
-      result = q
+      if p.allocator == nil:
+        let allocator = getLocalAllocator()
+        var q = cast[ptr PayloadBase](allocator.alloc(allocator,
+          headerSize + elemSize * cap))
+        copyMem(q +! headerSize, p +! headerSize, len * elemSize)
+        q.allocator = allocator
+        q.cap = cap
+        result = q
+      else:
+        let allocator = p.allocator
+        var q = cast[ptr PayloadBase](allocator.realloc(allocator, p,
+          headerSize + elemSize * p.cap,
+          headerSize + elemSize * cap))
+        q.allocator = allocator
+        q.cap = cap
+        result = q
 
 proc shrink*[T](x: var seq[T]; newLen: Natural) =
   mixin `=destroy`
@@ -140,6 +153,20 @@ proc grow*[T](x: var seq[T]; newLen: Natural; value: T) =
   for i in oldLen .. newLen-1:
     xu.p.data[i] = value
 
+proc add*[T](x: var seq[T]; value: sink T) {.magic: "AppendSeqElem", noSideEffect.} =
+  ## Generic proc for adding a data item `y` to a container `x`.
+  ##
+  ## For containers that have an order, `add` means *append*. New generic
+  ## containers should also call their adding proc `add` for consistency.
+  ## Generic code becomes much easier to write if the Nim naming scheme is
+  ## respected.
+  let oldLen = x.len
+  var xu = cast[ptr NimSeqV2[T]](addr x)
+  if xu.p == nil or xu.p.cap < oldLen+1:
+    xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, 1, sizeof(T)))
+  xu.len = oldLen+1
+  xu.p.data[oldLen] = value
+
 proc setLen[T](s: var seq[T], newlen: Natural) =
   {.noSideEffect.}:
     if newlen < s.len:
diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim
index 2fcb56202..9c24de33a 100644
--- a/lib/impure/db_mysql.nim
+++ b/lib/impure/db_mysql.nim
@@ -14,7 +14,7 @@
 ## `db_postgres <db_postgres.html>`_.
 ##
 ## Parameter substitution
-## ----------------------
+## ======================
 ##
 ## All ``db_*`` modules support the same form of parameter substitution.
 ## That is, using the ``?`` (question mark) to signify the place where a
@@ -25,10 +25,10 @@
 ##
 ##
 ## Examples
-## --------
+## ========
 ##
 ## Opening a connection to a database
-## ==================================
+## ----------------------------------
 ##
 ## .. code-block:: Nim
 ##     import db_mysql
@@ -36,7 +36,7 @@
 ##     db.close()
 ##
 ## Creating a table
-## ================
+## ----------------
 ##
 ## .. code-block:: Nim
 ##      db.exec(sql"DROP TABLE IF EXISTS myTable")
@@ -45,14 +45,14 @@
 ##                       name varchar(50) not null)"""))
 ##
 ## Inserting data
-## ==============
+## --------------
 ##
 ## .. code-block:: Nim
 ##     db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
 ##             "Dominik")
 ##
 ## Larger example
-## ==============
+## --------------
 ##
 ## .. code-block:: Nim
 ##
diff --git a/lib/impure/db_odbc.nim b/lib/impure/db_odbc.nim
index b621d652d..4c33a5a82 100644
--- a/lib/impure/db_odbc.nim
+++ b/lib/impure/db_odbc.nim
@@ -20,7 +20,7 @@
 ## `db_mysql <db_mysql.html>`_.
 ##
 ## Parameter substitution
-## ----------------------
+## ======================
 ##
 ## All ``db_*`` modules support the same form of parameter substitution.
 ## That is, using the ``?`` (question mark) to signify the place where a
@@ -31,10 +31,10 @@
 ##
 ##
 ## Examples
-## --------
+## ========
 ##
 ## Opening a connection to a database
-## ==================================
+## ----------------------------------
 ##
 ## .. code-block:: Nim
 ##     import db_odbc
@@ -42,7 +42,7 @@
 ##     db.close()
 ##
 ## Creating a table
-## ================
+## ----------------
 ##
 ## .. code-block:: Nim
 ##      db.exec(sql"DROP TABLE IF EXISTS myTable")
@@ -51,14 +51,14 @@
 ##                       name varchar(50) not null)"""))
 ##
 ## Inserting data
-## ==============
+## --------------
 ##
 ## .. code-block:: Nim
 ##     db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
 ##             "Andreas")
 ##
 ## Large example
-## =============
+## -------------
 ##
 ## .. code-block:: Nim
 ##
diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim
index 800673ca0..ec804072f 100644
--- a/lib/impure/db_postgres.nim
+++ b/lib/impure/db_postgres.nim
@@ -14,7 +14,7 @@
 ## `db_mysql <db_mysql.html>`_.
 ##
 ## Parameter substitution
-## ----------------------
+## ======================
 ##
 ## All ``db_*`` modules support the same form of parameter substitution.
 ## That is, using the ``?`` (question mark) to signify the place where a
@@ -38,10 +38,10 @@
 ##           3)
 ##
 ## Examples
-## --------
+## ========
 ##
 ## Opening a connection to a database
-## ==================================
+## ----------------------------------
 ##
 ## .. code-block:: Nim
 ##     import db_postgres
@@ -49,7 +49,7 @@
 ##     db.close()
 ##
 ## Creating a table
-## ================
+## ----------------
 ##
 ## .. code-block:: Nim
 ##      db.exec(sql"DROP TABLE IF EXISTS myTable")
@@ -58,7 +58,7 @@
 ##                       name varchar(50) not null)"""))
 ##
 ## Inserting data
-## ==============
+## --------------
 ##
 ## .. code-block:: Nim
 ##     db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim
index 3910992bb..ad2be5882 100644
--- a/lib/impure/db_sqlite.nim
+++ b/lib/impure/db_sqlite.nim
@@ -14,7 +14,7 @@
 ## `db_mysql <db_mysql.html>`_.
 ##
 ## Parameter substitution
-## ----------------------
+## ======================
 ##
 ## All ``db_*`` modules support the same form of parameter substitution.
 ## That is, using the ``?`` (question mark) to signify the place where a
@@ -24,10 +24,10 @@
 ##     sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
 ##
 ## Examples
-## --------
+## ========
 ##
 ## Opening a connection to a database
-## ==================================
+## ----------------------------------
 ##
 ## .. code-block:: Nim
 ##     import db_sqlite
@@ -35,7 +35,7 @@
 ##     db.close()
 ##
 ## Creating a table
-## ================
+## ----------------
 ##
 ## .. code-block:: Nim
 ##      db.exec(sql"DROP TABLE IF EXISTS myTable")
@@ -44,14 +44,14 @@
 ##                       name varchar(50) not null)"""))
 ##
 ## Inserting data
-## ==============
+## --------------
 ##
 ## .. code-block:: Nim
 ##     db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
 ##             "Jack")
 ##
 ## Larger example
-## ==============
+## --------------
 ##
 ## .. code-block:: nim
 ##
diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim
index 5c5125ba1..7014af42a 100644
--- a/lib/impure/nre.nim
+++ b/lib/impure/nre.nim
@@ -6,18 +6,6 @@
 #    distribution, for details about the copyright.
 #
 
-
-from pcre import nil
-import nre/private/util
-import tables
-from strutils import `%`
-from math import ceil
-import options
-from unicode import runeLenAt
-
-export options
-
-
 ## What is NRE?
 ## ============
 ##
@@ -67,6 +55,15 @@ runnableExamples:
     let matchBounds = firstVowel.get().captureBounds[-1]
     doAssert matchBounds.a == 1
 
+from pcre import nil
+import nre/private/util
+import tables
+from strutils import `%`
+from math import ceil
+import options
+from unicode import runeLenAt
+
+export options
 
 # Type definitions {{{
 type
diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim
index 7439b66e1..219b1bed5 100644
--- a/lib/js/asyncjs.nim
+++ b/lib/js/asyncjs.nim
@@ -50,7 +50,7 @@
 ##   proc loadGame(name: string): Future[Game] {.async.}
 ##
 ## JavaScript compatibility
-## ~~~~~~~~~~~~~~~~~~~~~~~~~
+## ========================
 ##
 ## Nim currently generates `async/await` JavaScript code which is supported in modern
 ## EcmaScript and most modern versions of browsers, Node.js and Electron.
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 08fa60dbc..2fb00d6a7 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -7,23 +7,6 @@
 #    distribution, for details about the copyright.
 #
 
-include "system/inclrtl"
-
-import os, tables, strutils, times, heapqueue, lists, options, asyncstreams
-import options, math
-import asyncfutures except callSoon
-
-import nativesockets, net, deques
-
-export Port, SocketFlag
-export asyncfutures except callSoon
-export asyncstreams
-
-#{.injectStmt: newGcInvariant().}
-
-## AsyncDispatch
-## *************
-##
 ## This module implements asynchronous IO. This includes a dispatcher,
 ## a ``Future`` type implementation, and an ``async`` macro which allows
 ## asynchronous code to be written in a synchronous style with the ``await``
@@ -82,7 +65,7 @@ export asyncstreams
 ## error), if there is no error however it returns the value of the future.
 ##
 ## Asynchronous procedures
-## -----------------------
+## =======================
 ##
 ## Asynchronous procedures remove the pain of working with callbacks. They do
 ## this by allowing you to write asynchronous code the same way as you would
@@ -116,7 +99,7 @@ export asyncstreams
 ## exceptions in async procs.
 ##
 ## Handling Exceptions
-## ~~~~~~~~~~~~~~~~~~~
+## -------------------
 ##
 ## The most reliable way to handle exceptions is to use ``yield`` on a future
 ## then check the future's ``failed`` property. For example:
@@ -142,7 +125,7 @@ export asyncstreams
 ##
 ##
 ## Discarding futures
-## ------------------
+## ==================
 ##
 ## Futures should **never** be discarded. This is because they may contain
 ## errors. If you do not care for the result of a Future then you should
@@ -151,17 +134,31 @@ export asyncstreams
 ## ``waitFor`` for that purpose.
 ##
 ## Examples
-## --------
+## ========
 ##
 ## For examples take a look at the documentation for the modules implementing
 ## asynchronous IO. A good place to start is the
 ## `asyncnet module <asyncnet.html>`_.
 ##
 ## Limitations/Bugs
-## ----------------
+## ================
 ##
 ## * The effect system (``raises: []``) does not work with async procedures.
 
+include "system/inclrtl"
+
+import os, tables, strutils, times, heapqueue, lists, options, asyncstreams
+import options, math
+import asyncfutures except callSoon
+
+import nativesockets, net, deques
+
+export Port, SocketFlag
+export asyncfutures except callSoon
+export asyncstreams
+
+#{.injectStmt: newGcInvariant().}
+
 # TODO: Check if yielded future is nil and throw a more meaningful exception
 
 type
@@ -1827,4 +1824,4 @@ proc setEvent*(ev: AsyncEvent) {.deprecated.} =
   ## Set event ``ev`` to signaled state.
   ##
   ## **Deprecated since v0.18.0:** Use ``trigger`` instead.
-  ev.trigger()
\ No newline at end of file
+  ev.trigger()
diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim
index d28e9fb57..3e741c304 100644
--- a/lib/pure/asyncftpclient.nim
+++ b/lib/pure/asyncftpclient.nim
@@ -16,7 +16,7 @@
 ## * Navigation through the FTP server's directories.
 ##
 ## Connecting to an FTP server
-## ------------------------
+## ===========================
 ##
 ## In order to begin any sort of transfer of files you must first
 ## connect to an FTP server. You can do so with the ``connect`` procedure.
@@ -34,7 +34,7 @@
 ## client will be connected after the ``await ftp.connect()`` call.
 ##
 ## Uploading a new file
-## --------------------
+## ====================
 ##
 ## After a connection is made you can use the ``store`` procedure to upload
 ## a new file to the FTP server. Make sure to check you are in the correct
@@ -53,7 +53,7 @@
 ##      waitFor(main())
 ##
 ## Checking the progress of a file transfer
-## ----------------------------------------
+## ========================================
 ##
 ## The progress of either a file upload or a file download can be checked
 ## by specifying a ``onProgressChanged`` procedure to the ``store`` or
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index 4aed42683..4045609a4 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -14,9 +14,8 @@
 ## application you should use a reverse proxy (for example nginx) instead of
 ## allowing users to connect directly to this server.
 ##
-##
 ## Basic usage
-## -----------
+## ===========
 ##
 ## This example will create an HTTP server on port 8080. The server will
 ## respond to all requests with a ``200 OK`` response code and "Hello World"
diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim
index 23ddf4777..d2750f728 100644
--- a/lib/pure/asyncmacro.nim
+++ b/lib/pure/asyncmacro.nim
@@ -7,8 +7,6 @@
 #    distribution, for details about the copyright.
 #
 
-## AsyncMacro
-## *************
 ## `asyncdispatch` module depends on the `asyncmacro` module to work properly.
 
 import macros, strutils, asyncfutures
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index 94429a657..d7cb5a18a 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -11,7 +11,7 @@
 ## asynchronous dispatcher defined in the ``asyncdispatch`` module.
 ##
 ## Asynchronous IO in Nim
-## ----------------------
+## ======================
 ##
 ## Async IO in Nim consists of multiple layers (from highest to lowest):
 ##
@@ -49,7 +49,7 @@
 ## over all the layers, providing some extra features such as buffering.
 ##
 ## SSL
-## ----
+## ===
 ##
 ## SSL can be enabled by compiling with the ``-d:ssl`` flag.
 ##
@@ -58,10 +58,10 @@
 ## the newly created SSL context to get an SSL socket.
 ##
 ## Examples
-## --------
+## ========
 ##
 ## Chat server
-## ^^^^^^^^^^^
+## -----------
 ##
 ## The following example demonstrates a simple chat server.
 ##
diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim
index 427f93926..5985592ff 100644
--- a/lib/pure/base64.nim
+++ b/lib/pure/base64.nim
@@ -15,7 +15,6 @@
 ## bytes (i.e., a total of 24 bits) can therefore be represented by
 ## four 6-bit Base64 digits.
 ##
-##
 ## Basic usage
 ## ===========
 ##
diff --git a/lib/pure/collections/hashcommon.nim b/lib/pure/collections/hashcommon.nim
new file mode 100644
index 000000000..bbc6d401d
--- /dev/null
+++ b/lib/pure/collections/hashcommon.nim
@@ -0,0 +1,68 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2019 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# An ``include`` file which contains common code for
+# hash sets and tables.
+
+const
+  growthFactor = 2
+
+when not defined(nimHasDefault):
+  template default[T](t: typedesc[T]): T =
+    var v: T
+    v
+
+# hcode for real keys cannot be zero.  hcode==0 signifies an empty slot.  These
+# two procs retain clarity of that encoding without the space cost of an enum.
+proc isEmpty(hcode: Hash): bool {.inline.} =
+  result = hcode == 0
+
+proc isFilled(hcode: Hash): bool {.inline.} =
+  result = hcode != 0
+
+proc nextTry(h, maxHash: Hash): Hash {.inline.} =
+  result = (h + 1) and maxHash
+
+proc mustRehash(length, counter: int): bool {.inline.} =
+  assert(length > counter)
+  result = (length * 2 < counter * 3) or (length - counter < 4)
+
+template rawGetKnownHCImpl() {.dirty.} =
+  if t.dataLen == 0:
+    return -1
+  var h: Hash = hc and maxHash(t)   # start with real hash value
+  while isFilled(t.data[h].hcode):
+    # Compare hc THEN key with boolean short circuit. This makes the common case
+    # zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
+    # It does slow down succeeding lookups by one extra Hash cmp&and..usually
+    # just a few clock cycles, generally worth it for any non-integer-like A.
+    if t.data[h].hcode == hc and t.data[h].key == key:
+      return h
+    h = nextTry(h, maxHash(t))
+  result = -1 - h                   # < 0 => MISSING; insert idx = -1 - result
+
+proc rawGetKnownHC[X, A](t: X, key: A, hc: Hash): int {.inline.} =
+  rawGetKnownHCImpl()
+
+template genHashImpl(key, hc: typed) =
+  hc = hash(key)
+  if hc == 0:       # This almost never taken branch should be very predictable.
+    hc = 314159265  # Value doesn't matter; Any non-zero favorite is fine.
+
+template genHash(key: typed): Hash =
+  var res: Hash
+  genHashImpl(key, res)
+  res
+
+template rawGetImpl() {.dirty.} =
+  genHashImpl(key, hc)
+  rawGetKnownHCImpl()
+
+proc rawGet[X, A](t: X, key: A, hc: var Hash): int {.inline.} =
+  rawGetImpl()
diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim
index 1fd32c9fa..2278be218 100644
--- a/lib/pure/collections/lists.nim
+++ b/lib/pure/collections/lists.nim
@@ -80,14 +80,15 @@ type
   DoublyLinkedNodeObj*[T] = object ## A node a doubly linked list consists of.
     ##
     ## It consists of a `value` field, and pointers to `next` and `prev`.
-    next*, prev*: ref DoublyLinkedNodeObj[T]
+    next*: <//>(ref DoublyLinkedNodeObj[T])
+    prev*: ref DoublyLinkedNodeObj[T]
     value*: T
   DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
 
   SinglyLinkedNodeObj*[T] = object ## A node a singly linked list consists of.
     ##
     ## It consists of a `value` field, and a pointer to `next`.
-    next*: ref SinglyLinkedNodeObj[T]
+    next*: <//>(ref SinglyLinkedNodeObj[T])
     value*: T
   SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T]
 
@@ -95,19 +96,22 @@ type
     ##
     ## Use `initSinglyLinkedList proc <#initSinglyLinkedList>`_ to create
     ## a new empty list.
-    head*, tail*: SinglyLinkedNode[T]
+    head*: <//>(SinglyLinkedNode[T])
+    tail*: SinglyLinkedNode[T]
 
   DoublyLinkedList*[T] = object ## A doubly linked list.
     ##
     ## Use `initDoublyLinkedList proc <#initDoublyLinkedList>`_ to create
     ## a new empty list.
-    head*, tail*: DoublyLinkedNode[T]
+    head*: <//>(DoublyLinkedNode[T])
+    tail*: DoublyLinkedNode[T]
 
   SinglyLinkedRing*[T] = object ## A singly linked ring.
     ##
     ## Use `initSinglyLinkedRing proc <#initSinglyLinkedRing>`_ to create
     ## a new empty ring.
-    head*, tail*: SinglyLinkedNode[T]
+    head*: <//>(SinglyLinkedNode[T])
+    tail*: SinglyLinkedNode[T]
 
   DoublyLinkedRing*[T] = object ## A doubly linked ring.
     ##
@@ -147,7 +151,7 @@ proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] =
     var a = initDoublyLinkedRing[int]()
   discard
 
-proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
+proc newDoublyLinkedNode*[T](value: T): <//>(DoublyLinkedNode[T]) =
   ## Creates a new doubly linked node with the given `value`.
   runnableExamples:
     var n = newDoublyLinkedNode[int](5)
@@ -156,7 +160,7 @@ proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
   new(result)
   result.value = value
 
-proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] =
+proc newSinglyLinkedNode*[T](value: T): <//>(SinglyLinkedNode[T]) =
   ## Creates a new singly linked node with the given `value`.
   runnableExamples:
     var n = newSinglyLinkedNode[int](5)
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index 253340379..e39c1fb80 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -949,6 +949,14 @@ macro mapLiterals*(constructor, op: untyped;
   ## works for nested tuples of arrays of sets etc.
   result = mapLitsImpl(constructor, op, nested.boolVal)
 
+iterator items*[T](xs: iterator: T): T =
+  ## iterates over each element yielded by a closure iterator. This may
+  ## not seem particularly useful on its own, but this allows closure
+  ## iterators to be used by the the mapIt, filterIt, allIt, anyIt, etc.
+  ## templates.
+  for x in xs():
+    yield x
+
 when isMainModule:
   import strutils
   from algorithm import sorted
@@ -1382,5 +1390,13 @@ when isMainModule:
     doAssert outp == @[@["a", "b"], @["c", "d"]]
 
 
+  block:
+    proc iter(len: int): auto =
+      result = iterator(): int =
+        for i in 0..<len:
+          yield i
+
+    doAssert: iter(3).mapIt(2*it).foldl(a + b) == 6
+
   when not defined(testing):
     echo "Finished doc tests"
diff --git a/lib/pure/collections/setimpl.nim b/lib/pure/collections/setimpl.nim
new file mode 100644
index 000000000..eb131b540
--- /dev/null
+++ b/lib/pure/collections/setimpl.nim
@@ -0,0 +1,153 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2019 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# An ``include`` file for the different hash set implementations.
+
+
+template maxHash(t): untyped = high(t.data)
+template dataLen(t): untyped = len(t.data)
+
+include hashcommon
+
+template initImpl(s: typed, size: int) =
+  assert isPowerOfTwo(size)
+  when s is OrderedSet:
+    s.first = -1
+    s.last = -1
+  s.counter = 0
+  newSeq(s.data, size)
+
+template rawInsertImpl() {.dirty.} =
+  if data.len == 0:
+    initImpl(s, defaultInitialSize)
+  data[h].key = key
+  data[h].hcode = hc
+
+proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A,
+                  hc: Hash, h: Hash) =
+  rawInsertImpl()
+
+proc enlarge[A](s: var HashSet[A]) =
+  var n: KeyValuePairSeq[A]
+  newSeq(n, len(s.data) * growthFactor)
+  swap(s.data, n)                   # n is now old seq
+  for i in countup(0, high(n)):
+    if isFilled(n[i].hcode):
+      var j = -1 - rawGetKnownHC(s, n[i].key, n[i].hcode)
+      rawInsert(s, s.data, n[i].key, n[i].hcode, j)
+
+template inclImpl() {.dirty.} =
+  if s.data.len == 0:
+    initImpl(s, defaultInitialSize)
+  var hc: Hash
+  var index = rawGet(s, key, hc)
+  if index < 0:
+    if mustRehash(len(s.data), s.counter):
+      enlarge(s)
+      index = rawGetKnownHC(s, key, hc)
+    rawInsert(s, s.data, key, hc, -1 - index)
+    inc(s.counter)
+
+template containsOrInclImpl() {.dirty.} =
+  if s.data.len == 0:
+    initImpl(s, defaultInitialSize)
+  var hc: Hash
+  var index = rawGet(s, key, hc)
+  if index >= 0:
+    result = true
+  else:
+    if mustRehash(len(s.data), s.counter):
+      enlarge(s)
+      index = rawGetKnownHC(s, key, hc)
+    rawInsert(s, s.data, key, hc, -1 - index)
+    inc(s.counter)
+
+template doWhile(a, b) =
+  while true:
+    b
+    if not a: break
+
+proc exclImpl[A](s: var HashSet[A], key: A) : bool {. inline .} =
+  var hc: Hash
+  var i = rawGet(s, key, hc)
+  var msk = high(s.data)
+  result = true
+
+  if i >= 0:
+    result = false
+    dec(s.counter)
+    while true:         # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
+      var j = i         # The correctness of this depends on (h+1) in nextTry,
+      var r = j         # though may be adaptable to other simple sequences.
+      s.data[i].hcode = 0              # mark current EMPTY
+      s.data[i].key = default(type(s.data[i].key))
+      doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
+        i = (i + 1) and msk            # increment mod table size
+        if isEmpty(s.data[i].hcode):   # end of collision cluster; So all done
+          return
+        r = s.data[i].hcode and msk    # "home" location of key@i
+      shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop
+
+template dollarImpl() {.dirty.} =
+  result = "{"
+  for key in items(s):
+    if result.len > 1: result.add(", ")
+    result.addQuoted(key)
+  result.add("}")
+
+
+
+# --------------------------- OrderedSet ------------------------------
+
+proc rawGet[A](t: OrderedSet[A], key: A, hc: var Hash): int {.inline.} =
+  rawGetImpl()
+
+proc rawInsert[A](s: var OrderedSet[A], data: var OrderedKeyValuePairSeq[A],
+                  key: A, hc: Hash, h: Hash) =
+  rawInsertImpl()
+  data[h].next = -1
+  if s.first < 0: s.first = h
+  if s.last >= 0: data[s.last].next = h
+  s.last = h
+
+proc enlarge[A](s: var OrderedSet[A]) =
+  var n: OrderedKeyValuePairSeq[A]
+  newSeq(n, len(s.data) * growthFactor)
+  var h = s.first
+  s.first = -1
+  s.last = -1
+  swap(s.data, n)
+  while h >= 0:
+    var nxt = n[h].next
+    if isFilled(n[h].hcode):
+      var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode)
+      rawInsert(s, s.data, n[h].key, n[h].hcode, j)
+    h = nxt
+
+proc exclImpl[A](s: var OrderedSet[A], key: A) : bool {.inline.} =
+  if len(s.data) == 0:
+    return true
+  var n: OrderedKeyValuePairSeq[A]
+  newSeq(n, len(s.data))
+  var h = s.first
+  s.first = -1
+  s.last = -1
+  swap(s.data, n)
+  let hc = genHash(key)
+  result = true
+  while h >= 0:
+    var nxt = n[h].next
+    if isFilled(n[h].hcode):
+      if n[h].hcode == hc and n[h].key == key:
+        dec s.counter
+        result = false
+      else:
+        var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode)
+        rawInsert(s, s.data, n[h].key, n[h].hcode, j)
+    h = nxt
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index 29bf31f96..86a72533e 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -26,7 +26,7 @@
 ##   `symmetric difference <#symmetricDifference,HashSet[A],HashSet[A]>`_
 ##
 ## .. code-block::
-##   echo toHashSet([9, 5, 1])         # {9, 1, 5}
+##   echo toHashSet([9, 5, 1])     # {9, 1, 5}
 ##   echo toOrderedSet([9, 5, 1])  # {9, 5, 1}
 ##
 ##   let
@@ -69,148 +69,32 @@ type
     data: KeyValuePairSeq[A]
     counter: int
 
+type
+  OrderedKeyValuePair[A] = tuple[
+    hcode: Hash, next: int, key: A]
+  OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
+  OrderedSet* {.myShallow.} [A] = object ## \
+    ## A generic hash set that remembers insertion order.
+    ##
+    ## Use `init proc <#init,OrderedSet[A],int>`_ or `initOrderedSet proc
+    ## <#initOrderedSet,int>`_ before calling other procs on it.
+    data: OrderedKeyValuePairSeq[A]
+    counter, first, last: int
 
-# ---------------------- helpers -----------------------------------
-
-const growthFactor = 2
-
-when not defined(nimHasDefault):
-  template default[T](t: typedesc[T]): T =
-    ## Used by clear methods to get a default value.
-    var v: T
-    v
-
-# hcode for real keys cannot be zero.  hcode==0 signifies an empty slot.  These
-# two procs retain clarity of that encoding without the space cost of an enum.
-proc isEmpty(hcode: Hash): bool {.inline.} =
-  result = hcode == 0
-
-proc isFilled(hcode: Hash): bool {.inline.} =
-  result = hcode != 0
-
-proc nextTry(h, maxHash: Hash): Hash {.inline.} =
-  result = (h + 1) and maxHash
-
-template rawGetKnownHCImpl() {.dirty.} =
-  var h: Hash = hc and high(s.data)  # start with real hash value
-  while isFilled(s.data[h].hcode):
-    # Compare hc THEN key with boolean short circuit. This makes the common case
-    # zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
-    # It does slow down succeeding lookups by one extra Hash cmp&and..usually
-    # just a few clock cycles, generally worth it for any non-integer-like A.
-    if s.data[h].hcode == hc and s.data[h].key == key:  # compare hc THEN key
-      return h
-    h = nextTry(h, high(s.data))
-  result = -1 - h                   # < 0 => MISSING; insert idx = -1 - result
-
-template genHash(key: typed): Hash =
-  var hc = hash(key)
-  if hc == 0:       # This almost never taken branch should be very predictable.
-    hc = 314159265  # Value doesn't matter; Any non-zero favorite is fine.
-  hc
-
-template rawGetImpl() {.dirty.} =
-  hc = genHash(key)
-  rawGetKnownHCImpl()
-
-template rawInsertImpl() {.dirty.} =
-  data[h].key = key
-  data[h].hcode = hc
-
-proc rawGetKnownHC[A](s: HashSet[A], key: A, hc: Hash): int {.inline.} =
-  rawGetKnownHCImpl()
-
-proc rawGet[A](s: HashSet[A], key: A, hc: var Hash): int {.inline.} =
-  rawGetImpl()
-
-proc rawInsert[A](s: var HashSet[A], data: var KeyValuePairSeq[A], key: A,
-                  hc: Hash, h: Hash) =
-  rawInsertImpl()
-
-proc enlarge[A](s: var HashSet[A]) =
-  var n: KeyValuePairSeq[A]
-  newSeq(n, len(s.data) * growthFactor)
-  swap(s.data, n)                   # n is now old seq
-  for i in countup(0, high(n)):
-    if isFilled(n[i].hcode):
-      var j = -1 - rawGetKnownHC(s, n[i].key, n[i].hcode)
-      rawInsert(s, s.data, n[i].key, n[i].hcode, j)
-
-template inclImpl() {.dirty.} =
-  var hc: Hash
-  var index = rawGet(s, key, hc)
-  if index < 0:
-    if mustRehash(len(s.data), s.counter):
-      enlarge(s)
-      index = rawGetKnownHC(s, key, hc)
-    rawInsert(s, s.data, key, hc, -1 - index)
-    inc(s.counter)
-
-template containsOrInclImpl() {.dirty.} =
-  var hc: Hash
-  var index = rawGet(s, key, hc)
-  if index >= 0:
-    result = true
-  else:
-    if mustRehash(len(s.data), s.counter):
-      enlarge(s)
-      index = rawGetKnownHC(s, key, hc)
-    rawInsert(s, s.data, key, hc, -1 - index)
-    inc(s.counter)
-
-template doWhile(a, b) =
-  while true:
-    b
-    if not a: break
-
-proc exclImpl[A](s: var HashSet[A], key: A) : bool {. inline .} =
-  assert s.isValid, "The set needs to be initialized."
-  var hc: Hash
-  var i = rawGet(s, key, hc)
-  var msk = high(s.data)
-  result = true
+const
+  defaultInitialSize* = 64
 
-  if i >= 0:
-    result = false
-    dec(s.counter)
-    while true:         # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
-      var j = i         # The correctness of this depends on (h+1) in nextTry,
-      var r = j         # though may be adaptable to other simple sequences.
-      s.data[i].hcode = 0              # mark current EMPTY
-      s.data[i].key = default(type(s.data[i].key))
-      doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
-        i = (i + 1) and msk            # increment mod table size
-        if isEmpty(s.data[i].hcode):   # end of collision cluster; So all done
-          return
-        r = s.data[i].hcode and msk    # "home" location of key@i
-      shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop
-
-proc mustRehash(length, counter: int): bool {.inline.} =
-  assert(length > counter)
-  result = (length * 2 < counter * 3) or (length - counter < 4)
-
-template dollarImpl() {.dirty.} =
-  result = "{"
-  for key in items(s):
-    if result.len > 1: result.add(", ")
-    result.addQuoted(key)
-  result.add("}")
+include setimpl
 
 proc rightSize*(count: Natural): int {.inline.}
 
 
-
-
-
-
-
-
 # ---------------------------------------------------------------------
 # ------------------------------ HashSet ------------------------------
 # ---------------------------------------------------------------------
 
 
-proc init*[A](s: var HashSet[A], initialSize=64) =
+proc init*[A](s: var HashSet[A], initialSize = defaultInitialSize) =
   ## Initializes a hash set.
   ##
   ## The `initialSize` parameter needs to be a power of two (default: 64).
@@ -218,9 +102,8 @@ proc init*[A](s: var HashSet[A], initialSize=64) =
   ## `math.nextPowerOfTwo proc <math.html#nextPowerOfTwo>`_ or `rightSize proc
   ## <#rightSize,Natural>`_ from this module.
   ##
-  ## All set variables must be initialized before
-  ## use with other procs from this module, with the exception of `isValid proc
-  ## <#isValid,HashSet[A]>`_ and `len() <#len,HashSet[A]>`_.
+  ## Starting from Nim v0.20, sets are initialized by default and it is
+  ## not necessary to call this function explicitly.
   ##
   ## You can call this proc on a previously initialized hash set, which will
   ## discard all its values. This might be more convenient than iterating over
@@ -235,42 +118,26 @@ proc init*[A](s: var HashSet[A], initialSize=64) =
     init(a)
     assert a.isValid
 
-  assert isPowerOfTwo(initialSize)
-  s.counter = 0
-  newSeq(s.data, initialSize)
+  initImpl(s, initialSize)
 
-proc initHashSet*[A](initialSize=64): HashSet[A] =
+proc initHashSet*[A](initialSize = defaultInitialSize): HashSet[A] =
   ## Wrapper around `init proc <#init,HashSet[A],int>`_ for initialization of
   ## hash sets.
   ##
   ## Returns an empty hash set you can assign directly in ``var`` blocks in a
   ## single line.
   ##
+  ## Starting from Nim v0.20, sets are initialized by default and it is
+  ## not necessary to call this function explicitly.
+  ##
   ## See also:
   ## * `toHashSet proc <#toHashSet,openArray[A]>`_
   runnableExamples:
     var a = initHashSet[int]()
-    assert a.isValid
     a.incl(3)
     assert len(a) == 1
-  result.init(initialSize)
 
-proc isValid*[A](s: HashSet[A]): bool =
-  ## Returns `true` if the set has been initialized (with `initHashSet proc
-  ## <#initHashSet,int>`_ or `init proc <#init,HashSet[A],int>`_).
-  ##
-  ## Most operations over an uninitialized set will crash at runtime and
-  ## `assert <system.html#assert>`_ in debug builds. You can use this proc in
-  ## your own procs to verify that sets passed to your procs are correctly
-  ## initialized.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block ::
-  ##   proc savePreferences(options: HashSet[string]) =
-  ##     assert options.isValid, "Pass an initialized set!"
-  ##     # Do stuff here, may crash in release builds!
-  result = s.data.len > 0
+  result.init(initialSize)
 
 proc `[]`*[A](s: var HashSet[A], key: A): var A =
   ## Returns the element that is actually stored in `s` which has the same
@@ -278,7 +145,6 @@ proc `[]`*[A](s: var HashSet[A], key: A): var A =
   ##
   ## This is useful when one overloaded `hash` and `==` but still needs
   ## reference semantics for sharing.
-  assert s.isValid, "The set needs to be initialized."
   var hc: Hash
   var index = rawGet(s, key, hc)
   if index >= 0: result = s.data[index].key
@@ -305,7 +171,6 @@ proc contains*[A](s: HashSet[A], key: A): bool =
     assert values.contains(2)
     assert 2 in values
 
-  assert s.isValid, "The set needs to be initialized."
   var hc: Hash
   var index = rawGet(s, key, hc)
   result = index >= 0
@@ -325,7 +190,6 @@ proc incl*[A](s: var HashSet[A], key: A) =
     values.incl(2)
     assert values.len == 1
 
-  assert s.isValid, "The set needs to be initialized."
   inclImpl()
 
 proc incl*[A](s: var HashSet[A], other: HashSet[A]) =
@@ -344,8 +208,6 @@ proc incl*[A](s: var HashSet[A], other: HashSet[A]) =
     values.incl(others)
     assert values.len == 5
 
-  assert s.isValid, "The set `s` needs to be initialized."
-  assert other.isValid, "The set `other` needs to be initialized."
   for item in other: incl(s, item)
 
 proc toHashSet*[A](keys: openArray[A]): HashSet[A] =
@@ -368,14 +230,6 @@ proc toHashSet*[A](keys: openArray[A]): HashSet[A] =
   result = initHashSet[A](rightSize(keys.len))
   for key in items(keys): result.incl(key)
 
-proc initSet*[A](initialSize=64): HashSet[A] {.deprecated:
-     "Deprecated since v0.20, use `initHashSet`"} = initHashSet[A](initialSize)
-  ## Deprecated since v0.20, use `initHashSet`.
-
-proc toSet*[A](keys: openArray[A]): HashSet[A] {.deprecated:
-     "Deprecated since v0.20, use `toHashSet`"} = toHashSet[A](keys)
-  ## Deprecated since v0.20, use `toHashSet`.
-
 iterator items*[A](s: HashSet[A]): A =
   ## Iterates over elements of the set `s`.
   ##
@@ -395,8 +249,7 @@ iterator items*[A](s: HashSet[A]): A =
   ##   assert a.len == 2
   ##   echo b
   ##   # --> {(a: 1, b: 3), (a: 0, b: 4)}
-  assert s.isValid, "The set needs to be initialized."
-  for h in 0..high(s.data):
+  for h in 0 .. high(s.data):
     if isFilled(s.data[h].hcode): yield s.data[h].key
 
 proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
@@ -417,7 +270,6 @@ proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
     assert values.containsOrIncl(2) == true
     assert values.containsOrIncl(3) == false
 
-  assert s.isValid, "The set needs to be initialized."
   containsOrInclImpl()
 
 proc excl*[A](s: var HashSet[A], key: A) =
@@ -434,6 +286,7 @@ proc excl*[A](s: var HashSet[A], key: A) =
     s.excl(2)
     s.excl(2)
     assert s.len == 3
+
   discard exclImpl(s, key)
 
 proc excl*[A](s: var HashSet[A], other: HashSet[A]) =
@@ -453,8 +306,6 @@ proc excl*[A](s: var HashSet[A], other: HashSet[A]) =
     assert len(numbers) == 3
     ## numbers == {1, 3, 5}
 
-  assert s.isValid, "The set `s` needs to be initialized."
-  assert other.isValid, "The set `other` needs to be initialized."
   for item in other: discard exclImpl(s, item)
 
 proc missingOrExcl*[A](s: var HashSet[A], key: A): bool =
@@ -474,6 +325,7 @@ proc missingOrExcl*[A](s: var HashSet[A], key: A): bool =
     assert s.missingOrExcl(4) == true
     assert s.missingOrExcl(6) == false
     assert s.missingOrExcl(6) == true
+
   exclImpl(s, key)
 
 proc pop*[A](s: var HashSet[A]): A =
@@ -489,7 +341,7 @@ proc pop*[A](s: var HashSet[A]): A =
     assert s.pop == 2
     doAssertRaises(KeyError, echo s.pop)
 
-  for h in 0..high(s.data):
+  for h in 0 .. high(s.data):
     if isFilled(s.data[h].hcode):
       result = s.data[h].key
       excl(s, result)
@@ -510,7 +362,7 @@ proc clear*[A](s: var HashSet[A]) =
     assert len(s) == 0
 
   s.counter = 0
-  for i in 0..<s.data.len:
+  for i in 0 ..< s.data.len:
     s.data[i].hcode = 0
     s.data[i].key = default(type(s.data[i].key))
 
@@ -525,6 +377,7 @@ proc len*[A](s: HashSet[A]): int =
     assert len(a) == 0
     let s = toHashSet([3, 5, 7])
     assert len(s) == 3
+
   result = s.counter
 
 proc card*[A](s: HashSet[A]): int =
@@ -554,8 +407,6 @@ proc union*[A](s1, s2: HashSet[A]): HashSet[A] =
       c = union(a, b)
     assert c == toHashSet(["a", "b", "c"])
 
-  assert s1.isValid, "The set `s1` needs to be initialized."
-  assert s2.isValid, "The set `s2` needs to be initialized."
   result = s1
   incl(result, s2)
 
@@ -579,9 +430,7 @@ proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] =
       c = intersection(a, b)
     assert c == toHashSet(["b"])
 
-  assert s1.isValid, "The set `s1` needs to be initialized."
-  assert s2.isValid, "The set `s2` needs to be initialized."
-  result = initHashSet[A](min(s1.data.len, s2.data.len))
+  result = initHashSet[A](max(min(s1.data.len, s2.data.len), 2))
   for item in s1:
     if item in s2: incl(result, item)
 
@@ -604,8 +453,6 @@ proc difference*[A](s1, s2: HashSet[A]): HashSet[A] =
       c = difference(a, b)
     assert c == toHashSet(["a"])
 
-  assert s1.isValid, "The set `s1` needs to be initialized."
-  assert s2.isValid, "The set `s2` needs to be initialized."
   result = initHashSet[A]()
   for item in s1:
     if not contains(s2, item):
@@ -631,8 +478,6 @@ proc symmetricDifference*[A](s1, s2: HashSet[A]): HashSet[A] =
       c = symmetricDifference(a, b)
     assert c == toHashSet(["a", "c"])
 
-  assert s1.isValid, "The set `s1` needs to be initialized."
-  assert s2.isValid, "The set `s2` needs to be initialized."
   result = s1
   for item in s2:
     if containsOrIncl(result, item): excl(result, item)
@@ -663,8 +508,6 @@ proc disjoint*[A](s1, s2: HashSet[A]): bool =
     assert disjoint(a, b) == false
     assert disjoint(a, b - a) == true
 
-  assert s1.isValid, "The set `s1` needs to be initialized."
-  assert s2.isValid, "The set `s2` needs to be initialized."
   for item in s1:
     if item in s2: return false
   return true
@@ -681,6 +524,7 @@ proc `<`*[A](s, t: HashSet[A]): bool =
       c = intersection(a, b)
     assert c < a and c < b
     assert(not (a < a))
+
   s.counter != t.counter and s <= t
 
 proc `<=`*[A](s, t: HashSet[A]): bool =
@@ -711,6 +555,7 @@ proc `==`*[A](s, t: HashSet[A]): bool =
       a = toHashSet([1, 2])
       b = toHashSet([2, 1])
     assert a == b
+
   s.counter == t.counter and s <= t
 
 proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] =
@@ -729,8 +574,7 @@ proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] =
 
 proc hash*[A](s: HashSet[A]): Hash =
   ## Hashing of HashSet.
-  assert s.isValid, "The set needs to be initialized."
-  for h in 0..high(s.data):
+  for h in 0 .. high(s.data):
     result = result xor s.data[h].hcode
   result = !$result
 
@@ -747,7 +591,6 @@ proc `$`*[A](s: HashSet[A]): string =
   ##   # --> {2, 4, 5}
   ##   echo toHashSet(["no", "esc'aping", "is \" provided"])
   ##   # --> {no, esc'aping, is " provided}
-  assert s.isValid, "The set needs to be initialized."
   dollarImpl()
 
 proc rightSize*(count: Natural): int {.inline.} =
@@ -760,8 +603,28 @@ proc rightSize*(count: Natural): int {.inline.} =
   result = nextPowerOfTwo(count * 3 div 2  +  4)
 
 
+proc initSet*[A](initialSize = defaultInitialSize): HashSet[A] {.deprecated:
+     "Deprecated since v0.20, use `initHashSet`"} = initHashSet[A](initialSize)
+  ## Deprecated since v0.20, use `initHashSet <#initHashSet,int>`_.
 
+proc toSet*[A](keys: openArray[A]): HashSet[A] {.deprecated:
+     "Deprecated since v0.20, use `toHashSet`"} = toHashSet[A](keys)
+  ## Deprecated since v0.20, use `toHashSet <#toHashSet,openArray[A]>`_.
 
+proc isValid*[A](s: HashSet[A]): bool {.deprecated:
+     "Deprecated since v0.20; sets are initialized by default"} =
+  ## **Deprecated since v0.20; sets are initialized by default**
+  ##
+  ## Returns `true` if the set has been initialized (with `initHashSet proc
+  ## <#initHashSet,int>`_ or `init proc <#init,HashSet[A],int>`_).
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block ::
+  ##   proc savePreferences(options: HashSet[string]) =
+  ##     assert options.isValid, "Pass an initialized set!"
+  ##     # Do stuff here, may crash in release builds!
+  result = s.data.len > 0
 
 
 
@@ -769,89 +632,19 @@ proc rightSize*(count: Natural): int {.inline.} =
 # --------------------------- OrderedSet ------------------------------
 # ---------------------------------------------------------------------
 
-type
-  OrderedKeyValuePair[A] = tuple[
-    hcode: Hash, next: int, key: A]
-  OrderedKeyValuePairSeq[A] = seq[OrderedKeyValuePair[A]]
-  OrderedSet* {.myShallow.} [A] = object ## \
-    ## A generic hash set that remembers insertion order.
-    ##
-    ## Use `init proc <#init,OrderedSet[A],int>`_ or `initOrderedSet proc
-    ## <#initOrderedSet,int>`_ before calling other procs on it.
-    data: OrderedKeyValuePairSeq[A]
-    counter, first, last: int
-
-
-# ---------------------- helpers -----------------------------------
-
 template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
-  var h = s.first
-  var idx = 0
-  while h >= 0:
-    var nxt = s.data[h].next
-    if isFilled(s.data[h].hcode):
-      yieldStmt
-      inc(idx)
-    h = nxt
-
-proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: Hash): int {.inline.} =
-  rawGetKnownHCImpl()
-
-proc rawGet[A](s: OrderedSet[A], key: A, hc: var Hash): int {.inline.} =
-  rawGetImpl()
-
-proc rawInsert[A](s: var OrderedSet[A], data: var OrderedKeyValuePairSeq[A],
-                  key: A, hc: Hash, h: Hash) =
-  rawInsertImpl()
-  data[h].next = -1
-  if s.first < 0: s.first = h
-  if s.last >= 0: data[s.last].next = h
-  s.last = h
-
-proc enlarge[A](s: var OrderedSet[A]) =
-  var n: OrderedKeyValuePairSeq[A]
-  newSeq(n, len(s.data) * growthFactor)
-  var h = s.first
-  s.first = -1
-  s.last = -1
-  swap(s.data, n)
-  while h >= 0:
-    var nxt = n[h].next
-    if isFilled(n[h].hcode):
-      var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode)
-      rawInsert(s, s.data, n[h].key, n[h].hcode, j)
-    h = nxt
-
-proc isValid*[A](s: OrderedSet[A]): bool
-
-proc exclImpl[A](s: var OrderedSet[A], key: A) : bool {. inline .} =
-  assert s.isValid, "The set needs to be initialized."
-  var n: OrderedKeyValuePairSeq[A]
-  newSeq(n, len(s.data))
-  var h = s.first
-  s.first = -1
-  s.last = -1
-  swap(s.data, n)
-  let hc = genHash(key)
-  result = true
-  while h >= 0:
-    var nxt = n[h].next
-    if isFilled(n[h].hcode):
-      if n[h].hcode == hc and n[h].key == key:
-        dec s.counter
-        result = false
-      else:
-        var j = -1 - rawGetKnownHC(s, n[h].key, n[h].hcode)
-        rawInsert(s, s.data, n[h].key, n[h].hcode, j)
-    h = nxt
-
-
-
-# -----------------------------------------------------------------------
-
-
-
-proc init*[A](s: var OrderedSet[A], initialSize=64) =
+  if s.data.len > 0:
+    var h = s.first
+    var idx = 0
+    while h >= 0:
+      var nxt = s.data[h].next
+      if isFilled(s.data[h].hcode):
+        yieldStmt
+        inc(idx)
+      h = nxt
+
+
+proc init*[A](s: var OrderedSet[A], initialSize = defaultInitialSize) =
   ## Initializes an ordered hash set.
   ##
   ## The `initialSize` parameter needs to be a power of two (default: 64).
@@ -859,9 +652,8 @@ proc init*[A](s: var OrderedSet[A], initialSize=64) =
   ## `math.nextPowerOfTwo proc <math.html#nextPowerOfTwo>`_ or `rightSize proc
   ## <#rightSize,Natural>`_ from this module.
   ##
-  ## All set variables must be initialized before
-  ## use with other procs from this module, with the exception of `isValid proc
-  ## <#isValid,HashSet[A]>`_ and `len() <#len,HashSet[A]>`_.
+  ## Starting from Nim v0.20, sets are initialized by default and it is
+  ## not necessary to call this function explicitly.
   ##
   ## You can call this proc on a previously initialized hash set, which will
   ## discard all its values. This might be more convenient than iterating over
@@ -876,26 +668,25 @@ proc init*[A](s: var OrderedSet[A], initialSize=64) =
     init(a)
     assert a.isValid
 
-  assert isPowerOfTwo(initialSize)
-  s.counter = 0
-  s.first = -1
-  s.last = -1
-  newSeq(s.data, initialSize)
+  initImpl(s, initialSize)
 
-proc initOrderedSet*[A](initialSize=64): OrderedSet[A] =
+proc initOrderedSet*[A](initialSize = defaultInitialSize): OrderedSet[A] =
   ## Wrapper around `init proc <#init,OrderedSet[A],int>`_ for initialization of
   ## ordered hash sets.
   ##
   ## Returns an empty ordered hash set you can assign directly in ``var`` blocks
   ## in a single line.
   ##
+  ## Starting from Nim v0.20, sets are initialized by default and it is
+  ## not necessary to call this function explicitly.
+  ##
   ## See also:
   ## * `toOrderedSet proc <#toOrderedSet,openArray[A]>`_
   runnableExamples:
     var a = initOrderedSet[int]()
-    assert a.isValid
     a.incl(3)
     assert len(a) == 1
+
   result.init(initialSize)
 
 proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] =
@@ -918,23 +709,6 @@ proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] =
   result = initOrderedSet[A](rightSize(keys.len))
   for key in items(keys): result.incl(key)
 
-proc isValid*[A](s: OrderedSet[A]): bool =
-  ## Returns `true` if the set has been initialized (with `initHashSet proc
-  ## <#initOrderedSet,int>`_ or `init proc <#init,OrderedSet[A],int>`_).
-  ##
-  ## Most operations over an uninitialized set will crash at runtime and
-  ## `assert <system.html#assert>`_ in debug builds. You can use this proc in
-  ## your own procs to verify that sets passed to your procs are correctly
-  ## initialized.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block ::
-  ##   proc savePreferences(options: OrderedSet[string]) =
-  ##     assert options.isValid, "Pass an initialized set!"
-  ##     # Do stuff here, may crash in release builds!
-  result = s.data.len > 0
-
 proc contains*[A](s: OrderedSet[A], key: A): bool =
   ## Returns true if `key` is in `s`.
   ##
@@ -952,7 +726,6 @@ proc contains*[A](s: OrderedSet[A], key: A): bool =
     assert values.contains(2)
     assert 2 in values
 
-  assert s.isValid, "The set needs to be initialized."
   var hc: Hash
   var index = rawGet(s, key, hc)
   result = index >= 0
@@ -972,7 +745,6 @@ proc incl*[A](s: var OrderedSet[A], key: A) =
     values.incl(2)
     assert values.len == 1
 
-  assert s.isValid, "The set needs to be initialized."
   inclImpl()
 
 proc incl*[A](s: var HashSet[A], other: OrderedSet[A]) =
@@ -988,8 +760,7 @@ proc incl*[A](s: var HashSet[A], other: OrderedSet[A]) =
       others = toOrderedSet([3, 4, 5])
     values.incl(others)
     assert values.len == 5
-  assert s.isValid, "The set `s` needs to be initialized."
-  assert other.isValid, "The set `other` needs to be initialized."
+
   for item in items(other): incl(s, item)
 
 proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool =
@@ -1009,7 +780,6 @@ proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool =
     assert values.containsOrIncl(2) == true
     assert values.containsOrIncl(3) == false
 
-  assert s.isValid, "The set needs to be initialized."
   containsOrInclImpl()
 
 proc excl*[A](s: var OrderedSet[A], key: A) =
@@ -1025,6 +795,7 @@ proc excl*[A](s: var OrderedSet[A], key: A) =
     s.excl(2)
     s.excl(2)
     assert s.len == 3
+
   discard exclImpl(s, key)
 
 proc missingOrExcl*[A](s: var OrderedSet[A], key: A): bool =
@@ -1044,6 +815,7 @@ proc missingOrExcl*[A](s: var OrderedSet[A], key: A): bool =
     assert s.missingOrExcl(4) == true
     assert s.missingOrExcl(6) == false
     assert s.missingOrExcl(6) == true
+
   exclImpl(s, key)
 
 proc clear*[A](s: var OrderedSet[A]) =
@@ -1059,7 +831,7 @@ proc clear*[A](s: var OrderedSet[A]) =
   s.counter = 0
   s.first = -1
   s.last = -1
-  for i in 0..<s.data.len:
+  for i in 0 ..< s.data.len:
     s.data[i].hcode = 0
     s.data[i].next = 0
     s.data[i].key = default(type(s.data[i].key))
@@ -1075,6 +847,7 @@ proc len*[A](s: OrderedSet[A]): int {.inline.} =
     assert len(a) == 0
     let s = toHashSet([3, 5, 7])
     assert len(s) == 3
+
   result = s.counter
 
 proc card*[A](s: OrderedSet[A]): int {.inline.} =
@@ -1110,7 +883,6 @@ proc `==`*[A](s, t: OrderedSet[A]): bool =
 
 proc hash*[A](s: OrderedSet[A]): Hash =
   ## Hashing of OrderedSet.
-  assert s.isValid, "The set needs to be initialized."
   forAllOrderedPairs:
     result = result !& s.data[h].hcode
   result = !$result
@@ -1129,7 +901,6 @@ proc `$`*[A](s: OrderedSet[A]): string =
   ##   # --> {2, 4, 5}
   ##   echo toOrderedSet(["no", "esc'aping", "is \" provided"])
   ##   # --> {no, esc'aping, is " provided}
-  assert s.isValid, "The set needs to be initialized."
   dollarImpl()
 
 
@@ -1152,11 +923,9 @@ iterator items*[A](s: OrderedSet[A]): A =
   ##   # --> Got 5
   ##   # --> Got 8
   ##   # --> Got 4
-  assert s.isValid, "The set needs to be initialized."
   forAllOrderedPairs:
     yield s.data[h].key
 
-
 iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
   ## Iterates through (position, value) tuples of OrderedSet `s`.
   runnableExamples:
@@ -1166,12 +935,27 @@ iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
       p.add(x)
     assert p == @[(0, 'a'), (1, 'b'), (2, 'r'), (3, 'c'), (4, 'd')]
 
-  assert s.isValid, "The set needs to be initialized"
   forAllOrderedPairs:
     yield (idx, s.data[h].key)
 
 
 
+proc isValid*[A](s: OrderedSet[A]): bool {.deprecated:
+     "Deprecated since v0.20; sets are initialized by default"} =
+  ## **Deprecated since v0.20; sets are initialized by default**
+  ##
+  ## Returns `true` if the set has been initialized (with `initHashSet proc
+  ## <#initOrderedSet,int>`_ or `init proc <#init,OrderedSet[A],int>`_).
+  ##
+  ## **Examples:**
+  ##
+  ## .. code-block ::
+  ##   proc savePreferences(options: OrderedSet[string]) =
+  ##     assert options.isValid, "Pass an initialized set!"
+  ##     # Do stuff here, may crash in release builds!
+  result = s.data.len > 0
+
+
 # -----------------------------------------------------------------------
 
 
@@ -1179,7 +963,7 @@ iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
 when isMainModule and not defined(release):
   proc testModule() =
     ## Internal micro test to validate docstrings and such.
-    block isValidTest:
+    block isValidTest: # isValid is deprecated
       var options: HashSet[string]
       proc savePreferences(options: HashSet[string]) =
         assert options.isValid, "Pass an initialized set!"
@@ -1280,7 +1064,7 @@ when isMainModule and not defined(release):
       var b = a.map(proc (x: int): string = $x)
       assert b == toHashSet(["1", "2", "3"])
 
-    block isValidTest:
+    block isValidTest: # isValid is deprecated
       var cards: OrderedSet[string]
       proc saveTarotCards(cards: OrderedSet[string]) =
         assert cards.isValid, "Pass an initialized set!"
@@ -1393,6 +1177,69 @@ when isMainModule and not defined(release):
       bb.incl(y)
       assert aa == bb
 
+    block setsWithoutInit:
+      var
+        a: HashSet[int]
+        b: HashSet[int]
+        c: HashSet[int]
+        d: HashSet[int]
+        e: HashSet[int]
+
+      doAssert a.containsOrIncl(3) == false
+      doAssert a.contains(3)
+      doAssert a.len == 1
+      doAssert a.containsOrIncl(3)
+      a.incl(3)
+      doAssert a.len == 1
+      a.incl(6)
+      doAssert a.len == 2
+
+      b.incl(5)
+      doAssert b.len == 1
+      b.excl(5)
+      b.excl(c)
+      doAssert b.missingOrExcl(5)
+      doAssert b.disjoint(c)
+
+      d = b + c
+      doAssert d.len == 0
+      d = b * c
+      doAssert d.len == 0
+      d = b - c
+      doAssert d.len == 0
+      d = b -+- c
+      doAssert d.len == 0
+
+      doAssert (d < e) == false
+      doAssert d <= e
+      doAssert d == e
+
+    block setsWithoutInit:
+      var
+        a: OrderedSet[int]
+        b: OrderedSet[int]
+        c: OrderedSet[int]
+        d: HashSet[int]
+
+
+      doAssert a.containsOrIncl(3) == false
+      doAssert a.contains(3)
+      doAssert a.len == 1
+      doAssert a.containsOrIncl(3)
+      a.incl(3)
+      doAssert a.len == 1
+      a.incl(6)
+      doAssert a.len == 2
+
+      b.incl(5)
+      doAssert b.len == 1
+      doAssert b.missingOrExcl(5) == false
+      doAssert b.missingOrExcl(5)
+
+      doAssert c.missingOrExcl(9)
+      d.incl(c)
+      doAssert d.len == 0
+
     when not defined(testing):
       echo "Micro tests run successfully."
 
diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim
index 0292a27a2..6ebb00e57 100644
--- a/lib/pure/collections/sharedtables.nim
+++ b/lib/pure/collections/sharedtables.nim
@@ -29,6 +29,14 @@ template maxHash(t): untyped = t.dataLen-1
 
 include tableimpl
 
+template st_maybeRehashPutImpl(enlarge) {.dirty.} =
+  if mustRehash(t.dataLen, t.counter):
+    enlarge(t)
+    index = rawGetKnownHC(t, key, hc)
+  index = -1 - index                  # important to transform for mgetOrPutImpl
+  rawInsert(t, t.data, key, val, hc, index)
+  inc(t.counter)
+
 proc enlarge[A, B](t: var SharedTable[A, B]) =
   let oldSize = t.dataLen
   let size = oldSize * growthFactor
@@ -176,7 +184,7 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A,
       var val: B
       mapper(key, val, pairExists)
       if pairExists:
-        maybeRehashPutImpl(enlarge)
+        st_maybeRehashPutImpl(enlarge)
 
 proc `[]=`*[A, B](t: var SharedTable[A, B], key: A, val: B) =
   ## puts a (key, value)-pair into `t`.
diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim
index 3e34b1488..4f9610db3 100644
--- a/lib/pure/collections/tableimpl.nim
+++ b/lib/pure/collections/tableimpl.nim
@@ -9,49 +9,7 @@
 
 # An ``include`` file for the different table implementations.
 
-# hcode for real keys cannot be zero.  hcode==0 signifies an empty slot.  These
-# two procs retain clarity of that encoding without the space cost of an enum.
-proc isEmpty(hcode: Hash): bool {.inline.} =
-  result = hcode == 0
-
-proc isFilled(hcode: Hash): bool {.inline.} =
-  result = hcode != 0
-
-const
-  growthFactor = 2
-
-proc mustRehash(length, counter: int): bool {.inline.} =
-  assert(length > counter)
-  result = (length * 2 < counter * 3) or (length - counter < 4)
-
-proc nextTry(h, maxHash: Hash): Hash {.inline.} =
-  result = (h + 1) and maxHash
-
-template rawGetKnownHCImpl() {.dirty.} =
-  var h: Hash = hc and maxHash(t)   # start with real hash value
-  while isFilled(t.data[h].hcode):
-    # Compare hc THEN key with boolean short circuit. This makes the common case
-    # zero ==key's for missing (e.g.inserts) and exactly one ==key for present.
-    # It does slow down succeeding lookups by one extra Hash cmp&and..usually
-    # just a few clock cycles, generally worth it for any non-integer-like A.
-    if t.data[h].hcode == hc and t.data[h].key == key:
-      return h
-    h = nextTry(h, maxHash(t))
-  result = -1 - h                   # < 0 => MISSING; insert idx = -1 - result
-
-template genHashImpl(key, hc: typed) =
-  hc = hash(key)
-  if hc == 0:       # This almost never taken branch should be very predictable.
-    hc = 314159265  # Value doesn't matter; Any non-zero favorite is fine.
-
-template genHash(key: typed): Hash =
-  var res: Hash
-  genHashImpl(key, res)
-  res
-
-template rawGetImpl() {.dirty.} =
-  genHashImpl(key, hc)
-  rawGetKnownHCImpl()
+include hashcommon
 
 template rawGetDeepImpl() {.dirty.} =   # Search algo for unconditional add
   genHashImpl(key, hc)
@@ -65,20 +23,16 @@ template rawInsertImpl() {.dirty.} =
   data[h].val = val
   data[h].hcode = hc
 
-proc rawGetKnownHC[X, A](t: X, key: A, hc: Hash): int {.inline.} =
-  rawGetKnownHCImpl()
-
 proc rawGetDeep[X, A](t: X, key: A, hc: var Hash): int {.inline.} =
   rawGetDeepImpl()
 
-proc rawGet[X, A](t: X, key: A, hc: var Hash): int {.inline.} =
-  rawGetImpl()
-
 proc rawInsert[X, A, B](t: var X, data: var KeyValuePairSeq[A, B],
                      key: A, val: B, hc: Hash, h: Hash) =
   rawInsertImpl()
 
 template addImpl(enlarge) {.dirty.} =
+  if t.dataLen == 0:
+    initImpl(t, defaultInitialSize)
   if mustRehash(t.dataLen, t.counter): enlarge(t)
   var hc: Hash
   var j = rawGetDeep(t, key, hc)
@@ -86,6 +40,8 @@ template addImpl(enlarge) {.dirty.} =
   inc(t.counter)
 
 template maybeRehashPutImpl(enlarge) {.dirty.} =
+  if t.dataLen == 0:
+    initImpl(t, defaultInitialSize)
   if mustRehash(t.dataLen, t.counter):
     enlarge(t)
     index = rawGetKnownHC(t, key, hc)
@@ -94,12 +50,16 @@ template maybeRehashPutImpl(enlarge) {.dirty.} =
   inc(t.counter)
 
 template putImpl(enlarge) {.dirty.} =
+  if t.dataLen == 0:
+    initImpl(t, defaultInitialSize)
   var hc: Hash
   var index = rawGet(t, key, hc)
   if index >= 0: t.data[index].val = val
   else: maybeRehashPutImpl(enlarge)
 
 template mgetOrPutImpl(enlarge) {.dirty.} =
+  if t.dataLen == 0:
+    initImpl(t, defaultInitialSize)
   var hc: Hash
   var index = rawGet(t, key, hc)
   if index < 0:
@@ -109,6 +69,8 @@ template mgetOrPutImpl(enlarge) {.dirty.} =
   result = t.data[index].val
 
 template hasKeyOrPutImpl(enlarge) {.dirty.} =
+  if t.dataLen == 0:
+    initImpl(t, defaultInitialSize)
   var hc: Hash
   var index = rawGet(t, key, hc)
   if index < 0:
@@ -116,11 +78,6 @@ template hasKeyOrPutImpl(enlarge) {.dirty.} =
     maybeRehashPutImpl(enlarge)
   else: result = true
 
-when not defined(nimHasDefault):
-  template default[T](t: typedesc[T]): T =
-    var v: T
-    v
-
 template delImplIdx(t, i) =
   let msk = maxHash(t)
   if i >= 0:
@@ -150,9 +107,53 @@ template delImpl() {.dirty.} =
   delImplIdx(t, i)
 
 template clearImpl() {.dirty.} =
-  for i in 0 ..< t.data.len:
+  for i in 0 ..< t.dataLen:
     when compiles(t.data[i].hcode): # CountTable records don't contain a hcode
       t.data[i].hcode = 0
     t.data[i].key = default(type(t.data[i].key))
     t.data[i].val = default(type(t.data[i].val))
   t.counter = 0
+
+template initImpl(result: typed, size: int) =
+  assert isPowerOfTwo(size)
+  result.counter = 0
+  newSeq(result.data, size)
+
+template insertImpl() = # for CountTable
+  if t.dataLen == 0: initImpl(t, defaultInitialSize)
+  if mustRehash(len(t.data), t.counter): enlarge(t)
+  ctRawInsert(t, t.data, key, val)
+  inc(t.counter)
+
+template getOrDefaultImpl(t, key): untyped =
+  mixin rawGet
+  var hc: Hash
+  var index = rawGet(t, key, hc)
+  if index >= 0: result = t.data[index].val
+
+template getOrDefaultImpl(t, key, default: untyped): untyped =
+  mixin rawGet
+  var hc: Hash
+  var index = rawGet(t, key, hc)
+  result = if index >= 0: t.data[index].val else: default
+
+template dollarImpl(): untyped {.dirty.} =
+  if t.len == 0:
+    result = "{:}"
+  else:
+    result = "{"
+    for key, val in pairs(t):
+      if result.len > 1: result.add(", ")
+      result.addQuoted(key)
+      result.add(": ")
+      result.addQuoted(val)
+    result.add("}")
+
+template equalsImpl(s, t: typed): typed =
+  if s.counter == t.counter:
+    # different insertion orders mean different 'data' seqs, so we have
+    # to use the slow route here:
+    for key, val in s:
+      if not t.hasKey(key): return false
+      if t.getOrDefault(key) != val: return false
+    return true
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index c6d5cc9e2..98ece4779 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -19,7 +19,7 @@
 ## For consistency with every other data type in Nim these have **value**
 ## semantics, this means that ``=`` performs a copy of the hash table.
 ##
-## For `ref semantics<manual.html#types-ref-and-pointer-types>`_
+## For `ref semantics<manual.html#types-reference-and-pointer-types>`_
 ## use their ``Ref`` variants: `TableRef<#TableRef>`_,
 ## `OrderedTableRef<#OrderedTableRef>`_, and `CountTableRef<#CountTableRef>`_.
 ##
@@ -237,9 +237,13 @@ type
     ## For creating a new empty TableRef, use `newTable proc
     ## <#newTable,int>`_.
 
+const
+  defaultInitialSize* = 64
 
 # ------------------------------ helpers ---------------------------------
 
+# Do NOT move these to tableimpl.nim, because sharedtables uses that
+# file and has its own implementation.
 template maxHash(t): untyped = high(t.data)
 template dataLen(t): untyped = len(t.data)
 
@@ -260,30 +264,6 @@ template get(t, key): untyped =
     else:
       raise newException(KeyError, "key not found")
 
-template getOrDefaultImpl(t, key): untyped =
-  mixin rawGet
-  var hc: Hash
-  var index = rawGet(t, key, hc)
-  if index >= 0: result = t.data[index].val
-
-template getOrDefaultImpl(t, key, default: untyped): untyped =
-  mixin rawGet
-  var hc: Hash
-  var index = rawGet(t, key, hc)
-  result = if index >= 0: t.data[index].val else: default
-
-template dollarImpl(): untyped {.dirty.} =
-  if t.len == 0:
-    result = "{:}"
-  else:
-    result = "{"
-    for key, val in pairs(t):
-      if result.len > 1: result.add(", ")
-      result.addQuoted(key)
-      result.add(": ")
-      result.addQuoted(val)
-    result.add("}")
-
 proc enlarge[A, B](t: var Table[A, B]) =
   var n: KeyValuePairSeq[A, B]
   newSeq(n, len(t.data) * growthFactor)
@@ -294,16 +274,8 @@ proc enlarge[A, B](t: var Table[A, B]) =
       var j: Hash = eh and maxHash(t)
       while isFilled(t.data[j].hcode):
         j = nextTry(j, maxHash(t))
-      rawInsert(t, t.data, n[i].key, n[i].val, eh, j)
+      rawInsert(t, t.data, move n[i].key, move n[i].val, eh, j)
 
-template equalsImpl(s, t: typed): typed =
-  if s.counter == t.counter:
-    # different insertion orders mean different 'data' seqs, so we have
-    # to use the slow route here:
-    for key, val in s:
-      if not t.hasKey(key): return false
-      if t.getOrDefault(key) != val: return false
-    return true
 
 
 
@@ -311,7 +283,7 @@ template equalsImpl(s, t: typed): typed =
 # ------------------------------ Table ------------------------------
 # -------------------------------------------------------------------
 
-proc initTable*[A, B](initialSize=64): Table[A, B] =
+proc initTable*[A, B](initialsize = defaultInitialSize): Table[A, B] =
   ## Creates a new hash table that is empty.
   ##
   ## ``initialSize`` must be a power of two (default: 64).
@@ -320,6 +292,9 @@ proc initTable*[A, B](initialSize=64): Table[A, B] =
   ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
   ## from this module.
   ##
+  ## Starting from Nim v0.20, tables are initialized by default and it is
+  ## not necessary to call this function explicitly.
+  ##
   ## See also:
   ## * `toTable proc<#toTable,openArray[]>`_
   ## * `newTable proc<#newTable,int>`_ for creating a `TableRef`
@@ -327,9 +302,7 @@ proc initTable*[A, B](initialSize=64): Table[A, B] =
     let
       a = initTable[int, string]()
       b = initTable[char, seq[int]]()
-  assert isPowerOfTwo(initialSize)
-  result.counter = 0
-  newSeq(result.data, initialSize)
+  initImpl(result, initialSize)
 
 proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] =
   ## Creates a new hash table that contains the given ``pairs``.
@@ -343,6 +316,7 @@ proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] =
     let a = [('a', 5), ('b', 9)]
     let b = toTable(a)
     assert b == {'a': 5, 'b': 9}.toTable
+
   result = initTable[A, B](rightSize(pairs.len))
   for key, val in items(pairs): result[key] = val
 
@@ -398,6 +372,7 @@ proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) =
     a['x'] = 7
     a['y'] = 33
     doAssert a == {'x': 7, 'y': 33}.toTable
+
   putImpl(enlarge)
 
 proc hasKey*[A, B](t: Table[A, B], key: A): bool =
@@ -414,6 +389,7 @@ proc hasKey*[A, B](t: Table[A, B], key: A): bool =
     let a = {'a': 5, 'b': 9}.toTable
     doAssert a.hasKey('a') == true
     doAssert a.hasKey('z') == false
+
   var hc: Hash
   result = rawGet(t, key, hc) >= 0
 
@@ -424,6 +400,7 @@ proc contains*[A, B](t: Table[A, B], key: A): bool =
     let a = {'a': 5, 'b': 9}.toTable
     doAssert 'b' in a == true
     doAssert a.contains('z') == false
+
   return hasKey[A, B](t, key)
 
 proc hasKeyOrPut*[A, B](t: var Table[A, B], key: A, val: B): bool =
@@ -443,6 +420,7 @@ proc hasKeyOrPut*[A, B](t: var Table[A, B], key: A, val: B): bool =
     if a.hasKeyOrPut('z', 50):
       a['z'] = 99
     doAssert a == {'a': 99, 'b': 9, 'z': 50}.toTable
+
   hasKeyOrPutImpl(enlarge)
 
 proc getOrDefault*[A, B](t: Table[A, B], key: A): B =
@@ -461,6 +439,7 @@ proc getOrDefault*[A, B](t: Table[A, B], key: A): B =
     let a = {'a': 5, 'b': 9}.toTable
     doAssert a.getOrDefault('a') == 5
     doAssert a.getOrDefault('z') == 0
+
   getOrDefaultImpl(t, key)
 
 proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B =
@@ -478,6 +457,7 @@ proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B =
     let a = {'a': 5, 'b': 9}.toTable
     doAssert a.getOrDefault('a', 99) == 5
     doAssert a.getOrDefault('z', 99) == 99
+
   getOrDefaultImpl(t, key, default)
 
 proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B =
@@ -497,6 +477,7 @@ proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B =
     doAssert a.mgetOrPut('a', 99) == 5
     doAssert a.mgetOrPut('z', 99) == 99
     doAssert a == {'a': 5, 'b': 9, 'z': 99}.toTable
+
   mgetOrPutImpl(enlarge)
 
 proc len*[A, B](t: Table[A, B]): int =
@@ -504,6 +485,7 @@ proc len*[A, B](t: Table[A, B]): int =
   runnableExamples:
     let a = {'a': 5, 'b': 9}.toTable
     doAssert len(a) == 2
+
   result = t.counter
 
 proc add*[A, B](t: var Table[A, B], key: A, val: B) =
@@ -527,6 +509,7 @@ proc del*[A, B](t: var Table[A, B], key: A) =
     doAssert a == {'b': 9, 'c': 13}.toTable
     a.del('z')
     doAssert a == {'b': 9, 'c': 13}.toTable
+
   delImpl()
 
 proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool =
@@ -568,6 +551,7 @@ proc clear*[A, B](t: var Table[A, B]) =
     doAssert len(a) == 3
     clear(a)
     doAssert len(a) == 0
+
   clearImpl()
 
 proc `$`*[A, B](t: Table[A, B]): string =
@@ -583,6 +567,7 @@ proc `==`*[A, B](s, t: Table[A, B]): bool =
       a = {'a': 5, 'b': 9, 'c': 13}.toTable
       b = {'b': 9, 'c': 13, 'a': 5}.toTable
     doAssert a == b
+
   equalsImpl(s, t)
 
 proc rightSize*(count: Natural): int {.inline.} =
@@ -692,6 +677,7 @@ iterator mpairs*[A, B](t: var Table[A, B]): (A, var B) =
     for k, v in a.mpairs:
       v.add(v[0] + 10)
     doAssert a == {'e': @[2, 4, 6, 8, 12], 'o': @[1, 5, 7, 9, 11]}.toTable
+
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
 
@@ -709,6 +695,7 @@ iterator keys*[A, B](t: Table[A, B]): A =
     for k in a.keys:
       a[k].add(99)
     doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.toTable
+
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield t.data[h].key
 
@@ -726,6 +713,7 @@ iterator values*[A, B](t: Table[A, B]): B =
       }.toTable
     for v in a.values:
       doAssert v.len == 4
+
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield t.data[h].val
 
@@ -744,6 +732,7 @@ iterator mvalues*[A, B](t: var Table[A, B]): var B =
     for v in a.mvalues:
       v.add(99)
     doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.toTable
+
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield t.data[h].val
 
@@ -779,7 +768,7 @@ iterator allValues*[A, B](t: Table[A, B]; key: A): B =
 # -------------------------------------------------------------------
 
 
-proc newTable*[A, B](initialSize=64): TableRef[A, B] =
+proc newTable*[A, B](initialsize = defaultInitialSize): <//>TableRef[A, B] =
   ## Creates a new ref hash table that is empty.
   ##
   ## ``initialSize`` must be a power of two (default: 64).
@@ -796,10 +785,11 @@ proc newTable*[A, B](initialSize=64): TableRef[A, B] =
     let
       a = newTable[int, string]()
       b = newTable[char, seq[int]]()
+
   new(result)
   result[] = initTable[A, B](initialSize)
 
-proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] =
+proc newTable*[A, B](pairs: openArray[(A, B)]): <//>TableRef[A, B] =
   ## Creates a new ref hash table that contains the given ``pairs``.
   ##
   ## ``pairs`` is a container consisting of ``(key, value)`` tuples.
@@ -811,10 +801,11 @@ proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] =
     let a = [('a', 5), ('b', 9)]
     let b = newTable(a)
     assert b == {'a': 5, 'b': 9}.newTable
+
   new(result)
   result[] = toTable[A, B](pairs)
 
-proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] =
+proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): <//>TableRef[C, B] =
   ## Index the collection with the proc provided.
   # TODO: As soon as supported, change collection: A to collection: A[B]
   result = newTable[C, B]()
@@ -842,6 +833,7 @@ proc `[]`*[A, B](t: TableRef[A, B], key: A): var B =
     doAssert a['a'] == 5
     doAssertRaises(KeyError):
       echo a['z']
+
   result = t[][key]
 
 proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) =
@@ -857,6 +849,7 @@ proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) =
     a['x'] = 7
     a['y'] = 33
     doAssert a == {'x': 7, 'y': 33}.newTable
+
   t[][key] = val
 
 proc hasKey*[A, B](t: TableRef[A, B], key: A): bool =
@@ -874,6 +867,7 @@ proc hasKey*[A, B](t: TableRef[A, B], key: A): bool =
     let a = {'a': 5, 'b': 9}.newTable
     doAssert a.hasKey('a') == true
     doAssert a.hasKey('z') == false
+
   result = t[].hasKey(key)
 
 proc contains*[A, B](t: TableRef[A, B], key: A): bool =
@@ -883,6 +877,7 @@ proc contains*[A, B](t: TableRef[A, B], key: A): bool =
     let a = {'a': 5, 'b': 9}.newTable
     doAssert 'b' in a == true
     doAssert a.contains('z') == false
+
   return hasKey[A, B](t, key)
 
 proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool =
@@ -902,6 +897,7 @@ proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool =
     if a.hasKeyOrPut('z', 50):
       a['z'] = 99
     doAssert a == {'a': 99, 'b': 9, 'z': 50}.newTable
+
   t[].hasKeyOrPut(key, val)
 
 proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B =
@@ -920,6 +916,7 @@ proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B =
     let a = {'a': 5, 'b': 9}.newTable
     doAssert a.getOrDefault('a') == 5
     doAssert a.getOrDefault('z') == 0
+
   getOrDefault(t[], key)
 
 proc getOrDefault*[A, B](t: TableRef[A, B], key: A, default: B): B =
@@ -937,6 +934,7 @@ proc getOrDefault*[A, B](t: TableRef[A, B], key: A, default: B): B =
     let a = {'a': 5, 'b': 9}.newTable
     doAssert a.getOrDefault('a', 99) == 5
     doAssert a.getOrDefault('z', 99) == 99
+
   getOrDefault(t[], key, default)
 
 proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B =
@@ -956,6 +954,7 @@ proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B =
     doAssert a.mgetOrPut('a', 99) == 5
     doAssert a.mgetOrPut('z', 99) == 99
     doAssert a == {'a': 5, 'b': 9, 'z': 99}.newTable
+
   t[].mgetOrPut(key, val)
 
 proc len*[A, B](t: TableRef[A, B]): int =
@@ -963,6 +962,7 @@ proc len*[A, B](t: TableRef[A, B]): int =
   runnableExamples:
     let a = {'a': 5, 'b': 9}.newTable
     doAssert len(a) == 2
+
   result = t.counter
 
 proc add*[A, B](t: TableRef[A, B], key: A, val: B) =
@@ -988,6 +988,7 @@ proc del*[A, B](t: TableRef[A, B], key: A) =
     doAssert a == {'b': 9, 'c': 13}.newTable
     a.del('z')
     doAssert a == {'b': 9, 'c': 13}.newTable
+
   t[].del(key)
 
 proc take*[A, B](t: TableRef[A, B], key: A, val: var B): bool =
@@ -1012,6 +1013,7 @@ proc take*[A, B](t: TableRef[A, B], key: A, val: var B): bool =
     doAssert a.take('z', i) == false
     doAssert a == {'a': 5, 'c': 13}.newTable
     doAssert i == 0
+
   result = t[].take(key, val)
 
 proc clear*[A, B](t: TableRef[A, B]) =
@@ -1025,6 +1027,7 @@ proc clear*[A, B](t: TableRef[A, B]) =
     doAssert len(a) == 3
     clear(a)
     doAssert len(a) == 0
+
   clearImpl()
 
 proc `$`*[A, B](t: TableRef[A, B]): string =
@@ -1041,6 +1044,7 @@ proc `==`*[A, B](s, t: TableRef[A, B]): bool =
       a = {'a': 5, 'b': 9, 'c': 13}.newTable
       b = {'b': 9, 'c': 13, 'a': 5}.newTable
     doAssert a == b
+
   if isNil(s): result = isNil(t)
   elif isNil(t): result = false
   else: equalsImpl(s[], t[])
@@ -1089,6 +1093,7 @@ iterator mpairs*[A, B](t: TableRef[A, B]): (A, var B) =
     for k, v in a.mpairs:
       v.add(v[0] + 10)
     doAssert a == {'e': @[2, 4, 6, 8, 12], 'o': @[1, 5, 7, 9, 11]}.newTable
+
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield (t.data[h].key, t.data[h].val)
 
@@ -1106,6 +1111,7 @@ iterator keys*[A, B](t: TableRef[A, B]): A =
     for k in a.keys:
       a[k].add(99)
     doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.newTable
+
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield t.data[h].key
 
@@ -1123,6 +1129,7 @@ iterator values*[A, B](t: TableRef[A, B]): B =
       }.newTable
     for v in a.values:
       doAssert v.len == 4
+
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield t.data[h].val
 
@@ -1140,6 +1147,7 @@ iterator mvalues*[A, B](t: TableRef[A, B]): var B =
     for v in a.mvalues:
       v.add(99)
     doAssert a == {'e': @[2, 4, 6, 8, 99], 'o': @[1, 5, 7, 9, 99]}.newTable
+
   for h in 0..high(t.data):
     if isFilled(t.data[h].hcode): yield t.data[h].val
 
@@ -1218,7 +1226,7 @@ template forAllOrderedPairs(yieldStmt: untyped): typed {.dirty.} =
 
 # ----------------------------------------------------------------------
 
-proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] =
+proc initOrderedTable*[A, B](initialsize = defaultInitialSize): OrderedTable[A, B] =
   ## Creates a new ordered hash table that is empty.
   ##
   ## ``initialSize`` must be a power of two (default: 64).
@@ -1227,6 +1235,9 @@ proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] =
   ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
   ## from this module.
   ##
+  ## Starting from Nim v0.20, tables are initialized by default and it is
+  ## not necessary to call this function explicitly.
+  ##
   ## See also:
   ## * `toOrderedTable proc<#toOrderedTable,openArray[]>`_
   ## * `newOrderedTable proc<#newOrderedTable,int>`_ for creating an
@@ -1235,11 +1246,9 @@ proc initOrderedTable*[A, B](initialSize=64): OrderedTable[A, B] =
     let
       a = initOrderedTable[int, string]()
       b = initOrderedTable[char, seq[int]]()
-  assert isPowerOfTwo(initialSize)
-  result.counter = 0
+  initImpl(result, initialSize)
   result.first = -1
   result.last = -1
-  newSeq(result.data, initialSize)
 
 proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] =
   ## Creates a new ordered hash table that contains the given ``pairs``.
@@ -1254,6 +1263,7 @@ proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] =
     let a = [('a', 5), ('b', 9)]
     let b = toOrderedTable(a)
     assert b == {'a': 5, 'b': 9}.toOrderedTable
+
   result = initOrderedTable[A, B](rightSize(pairs.len))
   for key, val in items(pairs): result[key] = val
 
@@ -1278,6 +1288,7 @@ proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
     doAssert a['a'] == 5
     doAssertRaises(KeyError):
       echo a['z']
+
   get(t, key)
 
 proc `[]`*[A, B](t: var OrderedTable[A, B], key: A): var B=
@@ -1309,6 +1320,7 @@ proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
     a['x'] = 7
     a['y'] = 33
     doAssert a == {'x': 7, 'y': 33}.toOrderedTable
+
   putImpl(enlarge)
 
 proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
@@ -1326,6 +1338,7 @@ proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
     let a = {'a': 5, 'b': 9}.toOrderedTable
     doAssert a.hasKey('a') == true
     doAssert a.hasKey('z') == false
+
   var hc: Hash
   result = rawGet(t, key, hc) >= 0
 
@@ -1336,6 +1349,7 @@ proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
     let a = {'a': 5, 'b': 9}.toOrderedTable
     doAssert 'b' in a == true
     doAssert a.contains('z') == false
+
   return hasKey[A, B](t, key)
 
 proc hasKeyOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): bool =
@@ -1355,6 +1369,7 @@ proc hasKeyOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): bool =
     if a.hasKeyOrPut('z', 50):
       a['z'] = 99
     doAssert a == {'a': 99, 'b': 9, 'z': 50}.toOrderedTable
+
   hasKeyOrPutImpl(enlarge)
 
 proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B =
@@ -1373,6 +1388,7 @@ proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B =
     let a = {'a': 5, 'b': 9}.toOrderedTable
     doAssert a.getOrDefault('a') == 5
     doAssert a.getOrDefault('z') == 0
+
   getOrDefaultImpl(t, key)
 
 proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B =
@@ -1390,6 +1406,7 @@ proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B =
     let a = {'a': 5, 'b': 9}.toOrderedTable
     doAssert a.getOrDefault('a', 99) == 5
     doAssert a.getOrDefault('z', 99) == 99
+
   getOrDefaultImpl(t, key, default)
 
 proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B =
@@ -1409,6 +1426,7 @@ proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B =
     doAssert a.mgetOrPut('a', 99) == 5
     doAssert a.mgetOrPut('z', 99) == 99
     doAssert a == {'a': 5, 'b': 9, 'z': 99}.toOrderedTable
+
   mgetOrPutImpl(enlarge)
 
 proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} =
@@ -1416,6 +1434,7 @@ proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} =
   runnableExamples:
     let a = {'a': 5, 'b': 9}.toOrderedTable
     doAssert len(a) == 2
+
   result = t.counter
 
 proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) =
@@ -1440,6 +1459,7 @@ proc del*[A, B](t: var OrderedTable[A, B], key: A) =
     doAssert a == {'b': 9, 'c': 13}.toOrderedTable
     a.del('z')
     doAssert a == {'b': 9, 'c': 13}.toOrderedTable
+
   var n: OrderedKeyValuePairSeq[A, B]
   newSeq(n, len(t.data))
   var h = t.first
@@ -1467,6 +1487,7 @@ proc clear*[A, B](t: var OrderedTable[A, B]) =
     doAssert len(a) == 3
     clear(a)
     doAssert len(a) == 0
+
   clearImpl()
   t.first = -1
   t.last = -1
@@ -1602,6 +1623,7 @@ iterator mpairs*[A, B](t: var OrderedTable[A, B]): (A, var B) =
     for k, v in a.mpairs:
       v.add(v[0] + 10)
     doAssert a == {'o': @[1, 5, 7, 9, 11], 'e': @[2, 4, 6, 8, 12]}.toOrderedTable
+
   forAllOrderedPairs:
     yield (t.data[h].key, t.data[h].val)
 
@@ -1619,6 +1641,7 @@ iterator keys*[A, B](t: OrderedTable[A, B]): A =
     for k in a.keys:
       a[k].add(99)
     doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.toOrderedTable
+
   forAllOrderedPairs:
     yield t.data[h].key
 
@@ -1636,6 +1659,7 @@ iterator values*[A, B](t: OrderedTable[A, B]): B =
       }.toOrderedTable
     for v in a.values:
       doAssert v.len == 4
+
   forAllOrderedPairs:
     yield t.data[h].val
 
@@ -1666,7 +1690,7 @@ iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B =
 # --------------------------- OrderedTableRef -------------------------------
 # ---------------------------------------------------------------------------
 
-proc newOrderedTable*[A, B](initialSize=64): OrderedTableRef[A, B] =
+proc newOrderedTable*[A, B](initialsize = defaultInitialSize): <//>OrderedTableRef[A, B] =
   ## Creates a new ordered ref hash table that is empty.
   ##
   ## ``initialSize`` must be a power of two (default: 64).
@@ -1687,7 +1711,7 @@ proc newOrderedTable*[A, B](initialSize=64): OrderedTableRef[A, B] =
   new(result)
   result[] = initOrderedTable[A, B](initialSize)
 
-proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] =
+proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): <//>OrderedTableRef[A, B] =
   ## Creates a new ordered ref hash table that contains the given ``pairs``.
   ##
   ## ``pairs`` is a container consisting of ``(key, value)`` tuples.
@@ -1700,6 +1724,7 @@ proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] =
     let a = [('a', 5), ('b', 9)]
     let b = newOrderedTable(a)
     assert b == {'a': 5, 'b': 9}.newOrderedTable
+
   result = newOrderedTable[A, B](rightSize(pairs.len))
   for key, val in items(pairs): result.add(key, val)
 
@@ -1740,6 +1765,7 @@ proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
     a['x'] = 7
     a['y'] = 33
     doAssert a == {'x': 7, 'y': 33}.newOrderedTable
+
   t[][key] = val
 
 proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool =
@@ -1757,6 +1783,7 @@ proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool =
     let a = {'a': 5, 'b': 9}.newOrderedTable
     doAssert a.hasKey('a') == true
     doAssert a.hasKey('z') == false
+
   result = t[].hasKey(key)
 
 proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool =
@@ -1766,6 +1793,7 @@ proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool =
     let a = {'a': 5, 'b': 9}.newOrderedTable
     doAssert 'b' in a == true
     doAssert a.contains('z') == false
+
   return hasKey[A, B](t, key)
 
 proc hasKeyOrPut*[A, B](t: var OrderedTableRef[A, B], key: A, val: B): bool =
@@ -1785,6 +1813,7 @@ proc hasKeyOrPut*[A, B](t: var OrderedTableRef[A, B], key: A, val: B): bool =
     if a.hasKeyOrPut('z', 50):
       a['z'] = 99
     doAssert a == {'a': 99, 'b': 9, 'z': 50}.newOrderedTable
+
   result = t[].hasKeyOrPut(key, val)
 
 proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B =
@@ -1803,6 +1832,7 @@ proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B =
     let a = {'a': 5, 'b': 9}.newOrderedTable
     doAssert a.getOrDefault('a') == 5
     doAssert a.getOrDefault('z') == 0
+
   getOrDefault(t[], key)
 
 proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B =
@@ -1820,6 +1850,7 @@ proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B =
     let a = {'a': 5, 'b': 9}.newOrderedTable
     doAssert a.getOrDefault('a', 99) == 5
     doAssert a.getOrDefault('z', 99) == 99
+
   getOrDefault(t[], key, default)
 
 proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B =
@@ -1839,6 +1870,7 @@ proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B =
     doAssert a.mgetOrPut('a', 99) == 5
     doAssert a.mgetOrPut('z', 99) == 99
     doAssert a == {'a': 5, 'b': 9, 'z': 99}.newOrderedTable
+
   result = t[].mgetOrPut(key, val)
 
 proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} =
@@ -1846,6 +1878,7 @@ proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} =
   runnableExamples:
     let a = {'a': 5, 'b': 9}.newOrderedTable
     doAssert len(a) == 2
+
   result = t.counter
 
 proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: B) =
@@ -1868,6 +1901,7 @@ proc del*[A, B](t: var OrderedTableRef[A, B], key: A) =
     doAssert a == {'b': 9, 'c': 13}.newOrderedTable
     a.del('z')
     doAssert a == {'b': 9, 'c': 13}.newOrderedTable
+
   t[].del(key)
 
 proc clear*[A, B](t: var OrderedTableRef[A, B]) =
@@ -1880,6 +1914,7 @@ proc clear*[A, B](t: var OrderedTableRef[A, B]) =
     doAssert len(a) == 3
     clear(a)
     doAssert len(a) == 0
+
   clear(t[])
 
 proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x,y: (A, B)): int, order = SortOrder.Ascending) =
@@ -1899,6 +1934,7 @@ proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x,y: (A, B)): int, order =
     doAssert a == {'a': 10, 'b': 20, 'c': 0}.newOrderedTable
     a.sort(system.cmp, order=SortOrder.Descending)
     doAssert a == {'c': 0, 'b': 20, 'a': 10}.newOrderedTable
+
   t[].sort(cmp, order=order)
 
 proc `$`*[A, B](t: OrderedTableRef[A, B]): string =
@@ -1915,6 +1951,7 @@ proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool =
       a = {'a': 5, 'b': 9, 'c': 13}.newOrderedTable
       b = {'b': 9, 'c': 13, 'a': 5}.newOrderedTable
     doAssert a != b
+
   if isNil(s): result = isNil(t)
   elif isNil(t): result = false
   else: result = s[] == t[]
@@ -1964,6 +2001,7 @@ iterator mpairs*[A, B](t: OrderedTableRef[A, B]): (A, var B) =
     for k, v in a.mpairs:
       v.add(v[0] + 10)
     doAssert a == {'o': @[1, 5, 7, 9, 11], 'e': @[2, 4, 6, 8, 12]}.newOrderedTable
+
   forAllOrderedPairs:
     yield (t.data[h].key, t.data[h].val)
 
@@ -1981,6 +2019,7 @@ iterator keys*[A, B](t: OrderedTableRef[A, B]): A =
     for k in a.keys:
       a[k].add(99)
     doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.newOrderedTable
+
   forAllOrderedPairs:
     yield t.data[h].key
 
@@ -1998,6 +2037,7 @@ iterator values*[A, B](t: OrderedTableRef[A, B]): B =
       }.newOrderedTable
     for v in a.values:
       doAssert v.len == 4
+
   forAllOrderedPairs:
     yield t.data[h].val
 
@@ -2016,6 +2056,7 @@ iterator mvalues*[A, B](t: OrderedTableRef[A, B]): var B =
     for v in a.mvalues:
       v.add(99)
     doAssert a == {'o': @[1, 5, 7, 9, 99], 'e': @[2, 4, 6, 8, 99]}.newOrderedTable
+
   forAllOrderedPairs:
     yield t.data[h].val
 
@@ -2046,7 +2087,7 @@ type
 
 # ------------------------------ helpers ---------------------------------
 
-proc rawInsert[A](t: CountTable[A], data: var seq[tuple[key: A, val: int]],
+proc ctRawInsert[A](t: CountTable[A], data: var seq[tuple[key: A, val: int]],
                   key: A, val: int) =
   var h: Hash = hash(key) and high(data)
   while data[h].val != 0: h = nextTry(h, high(data))
@@ -2057,10 +2098,12 @@ proc enlarge[A](t: var CountTable[A]) =
   var n: seq[tuple[key: A, val: int]]
   newSeq(n, len(t.data) * growthFactor)
   for i in countup(0, high(t.data)):
-    if t.data[i].val != 0: rawInsert(t, n, t.data[i].key, t.data[i].val)
+    if t.data[i].val != 0: ctRawInsert(t, n, t.data[i].key, t.data[i].val)
   swap(t.data, n)
 
 proc rawGet[A](t: CountTable[A], key: A): int =
+  if t.data.len == 0:
+    return -1
   var h: Hash = hash(key) and high(t.data) # start with real hash value
   while t.data[h].val != 0:
     if t.data[h].key == key: return h
@@ -2075,7 +2118,7 @@ proc inc*[A](t: var CountTable[A], key: A, val = 1)
 
 # ----------------------------------------------------------------------
 
-proc initCountTable*[A](initialSize=64): CountTable[A] =
+proc initCountTable*[A](initialsize = defaultInitialSize): CountTable[A] =
   ## Creates a new count table that is empty.
   ##
   ## ``initialSize`` must be a power of two (default: 64).
@@ -2084,13 +2127,14 @@ proc initCountTable*[A](initialSize=64): CountTable[A] =
   ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_
   ## from this module.
   ##
+  ## Starting from Nim v0.20, tables are initialized by default and it is
+  ## not necessary to call this function explicitly.
+  ##
   ## See also:
   ## * `toCountTable proc<#toCountTable,openArray[A]>`_
   ## * `newCountTable proc<#newCountTable,int>`_ for creating a
   ##   `CountTableRef`
-  assert isPowerOfTwo(initialSize)
-  result.counter = 0
-  newSeq(result.data, initialSize)
+  initImpl(result, initialSize)
 
 proc toCountTable*[A](keys: openArray[A]): CountTable[A] =
   ## Creates a new count table with every member of a container ``keys``
@@ -2130,9 +2174,7 @@ proc `[]=`*[A](t: var CountTable[A], key: A, val: int) =
   if h >= 0:
     t.data[h].val = val
   else:
-    if mustRehash(len(t.data), t.counter): enlarge(t)
-    rawInsert(t, t.data, key, val)
-    inc(t.counter)
+    insertImpl()
 
 proc inc*[A](t: var CountTable[A], key: A, val = 1) =
   ## Increments ``t[key]`` by ``val`` (default: 1).
@@ -2141,14 +2183,13 @@ proc inc*[A](t: var CountTable[A], key: A, val = 1) =
     a.inc('a')
     a.inc('b', 10)
     doAssert a == toCountTable("aaabbbbbbbbbbb")
+
   var index = rawGet(t, key)
   if index >= 0:
     inc(t.data[index].val, val)
     if t.data[index].val == 0: dec(t.counter)
   else:
-    if mustRehash(len(t.data), t.counter): enlarge(t)
-    rawInsert(t, t.data, key, val)
-    inc(t.counter)
+    insertImpl()
 
 proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
   ## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n)
@@ -2229,6 +2270,7 @@ proc sort*[A](t: var CountTable[A], order = SortOrder.Descending) =
     doAssert toSeq(a.values) == @[5, 2, 2, 1, 1]
     a.sort(SortOrder.Ascending)
     doAssert toSeq(a.values) == @[1, 1, 2, 2, 5]
+
   t.data.sort(cmp=ctCmp, order=order)
 
 proc merge*[A](s: var CountTable[A], t: CountTable[A]) =
@@ -2238,6 +2280,7 @@ proc merge*[A](s: var CountTable[A], t: CountTable[A]) =
     let b = toCountTable("bcc")
     a.merge(b)
     doAssert a == toCountTable("aaabbbccc")
+
   for key, value in t:
     s.inc(key, value)
 
@@ -2248,6 +2291,7 @@ proc merge*[A](s, t: CountTable[A]): CountTable[A] =
       a = toCountTable("aaabbc")
       b = toCountTable("bcc")
     doAssert merge(a, b) == toCountTable("aaabbbccc")
+
   result = initCountTable[A](nextPowerOfTwo(max(s.len, t.len)))
   for table in @[s, t]:
     for key, value in table:
@@ -2306,6 +2350,7 @@ iterator mpairs*[A](t: var CountTable[A]): (A, var int) =
     for k, v in mpairs(a):
       v = 2
     doAssert a == toCountTable("aabbccddrr")
+
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
 
@@ -2320,6 +2365,7 @@ iterator keys*[A](t: CountTable[A]): A =
     for k in keys(a):
       a[k] = 2
     doAssert a == toCountTable("aabbccddrr")
+
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield t.data[h].key
 
@@ -2334,6 +2380,7 @@ iterator values*[A](t: CountTable[A]): int =
     let a = toCountTable("abracadabra")
     for v in values(a):
       assert v < 10
+
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield t.data[h].val
 
@@ -2349,6 +2396,7 @@ iterator mvalues*[A](t: var CountTable[A]): var int =
     for v in mvalues(a):
       v = 2
     doAssert a == toCountTable("aabbccddrr")
+
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield t.data[h].val
 
@@ -2364,7 +2412,7 @@ iterator mvalues*[A](t: var CountTable[A]): var int =
 
 proc inc*[A](t: CountTableRef[A], key: A, val = 1)
 
-proc newCountTable*[A](initialSize=64): CountTableRef[A] =
+proc newCountTable*[A](initialsize = defaultInitialSize): <//>CountTableRef[A] =
   ## Creates a new ref count table that is empty.
   ##
   ## ``initialSize`` must be a power of two (default: 64).
@@ -2381,7 +2429,7 @@ proc newCountTable*[A](initialSize=64): CountTableRef[A] =
   new(result)
   result[] = initCountTable[A](initialSize)
 
-proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] =
+proc newCountTable*[A](keys: openArray[A]): <//>CountTableRef[A] =
   ## Creates a new ref count table with every member of a container ``keys``
   ## having a count of how many times it occurs in that container.
   result = newCountTable[A](rightSize(keys.len))
@@ -2493,6 +2541,7 @@ proc merge*[A](s, t: CountTableRef[A]) =
       b = newCountTable("bcc")
     a.merge(b)
     doAssert a == newCountTable("aaabbbccc")
+
   s[].merge(t[])
 
 proc `$`*[A](t: CountTableRef[A]): string =
@@ -2551,6 +2600,7 @@ iterator mpairs*[A](t: CountTableRef[A]): (A, var int) =
     for k, v in mpairs(a):
       v = 2
     doAssert a == newCountTable("aabbccddrr")
+
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val)
 
@@ -2565,6 +2615,7 @@ iterator keys*[A](t: CountTableRef[A]): A =
     for k in keys(a):
       a[k] = 2
     doAssert a == newCountTable("aabbccddrr")
+
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield t.data[h].key
 
@@ -2579,6 +2630,7 @@ iterator values*[A](t: CountTableRef[A]): int =
     let a = newCountTable("abracadabra")
     for v in values(a):
       assert v < 10
+
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield t.data[h].val
 
@@ -2593,6 +2645,7 @@ iterator mvalues*[A](t: CountTableRef[A]): var int =
     for v in mvalues(a):
       v = 2
     doAssert a == newCountTable("aabbccddrr")
+
   for h in 0..high(t.data):
     if t.data[h].val != 0: yield t.data[h].val
 
@@ -2813,3 +2866,99 @@ when isMainModule:
     doAssert "test1" == orf.getOrDefault("test1", "test1")
     orf["test2"] = "test2"
     doAssert "test2" == orf.getOrDefault("test2", "test1")
+
+  block tableWithoutInit:
+    var
+      a: Table[string, int]
+      b: Table[string, int]
+      c: Table[string, int]
+      d: Table[string, int]
+      e: Table[string, int]
+
+    a["a"] = 7
+    doAssert a.hasKey("a")
+    doAssert a.len == 1
+    doAssert a["a"] == 7
+    a["a"] = 9
+    doAssert a.len == 1
+    doAssert a["a"] == 9
+
+    doAssert b.hasKeyOrPut("b", 5) == false
+    doAssert b.hasKey("b")
+    doAssert b.hasKeyOrPut("b", 8)
+    doAssert b["b"] == 5
+
+    doAssert c.getOrDefault("a") == 0
+    doAssert c.getOrDefault("a", 3) == 3
+    c["a"] = 6
+    doAssert c.getOrDefault("a", 3) == 6
+
+    doAssert d.mgetOrPut("a", 3) == 3
+    doAssert d.mgetOrPut("a", 6) == 3
+
+    var x = 99
+    doAssert e.take("a", x) == false
+    doAssert x == 99
+    e["a"] = 77
+    doAssert e.take("a", x)
+    doAssert x == 77
+
+  block orderedTableWithoutInit:
+    var
+      a: OrderedTable[string, int]
+      b: OrderedTable[string, int]
+      c: OrderedTable[string, int]
+      d: OrderedTable[string, int]
+
+    a["a"] = 7
+    doAssert a.hasKey("a")
+    doAssert a.len == 1
+    doAssert a["a"] == 7
+    a["a"] = 9
+    doAssert a.len == 1
+    doAssert a["a"] == 9
+
+    doAssert b.hasKeyOrPut("b", 5) == false
+    doAssert b.hasKey("b")
+    doAssert b.hasKeyOrPut("b", 8)
+    doAssert b["b"] == 5
+
+    doAssert c.getOrDefault("a") == 0
+    doAssert c.getOrDefault("a", 3) == 3
+    c["a"] = 6
+    doAssert c.getOrDefault("a", 3) == 6
+
+    doAssert d.mgetOrPut("a", 3) == 3
+    doAssert d.mgetOrPut("a", 6) == 3
+
+  block countTableWithoutInit:
+    var
+      a: CountTable[string]
+      b: CountTable[string]
+      c: CountTable[string]
+      d: CountTable[string]
+      e: CountTable[string]
+
+    a["a"] = 7
+    doAssert a.hasKey("a")
+    doAssert a.len == 1
+    doAssert a["a"] == 7
+    a["a"] = 9
+    doAssert a.len == 1
+    doAssert a["a"] == 9
+
+    doAssert b["b"] == 0
+    b.inc("b")
+    doAssert b["b"] == 1
+
+    doAssert c.getOrDefault("a") == 0
+    doAssert c.getOrDefault("a", 3) == 3
+    c["a"] = 6
+    doAssert c.getOrDefault("a", 3) == 6
+
+    e["f"] = 3
+    merge(d, e)
+    doAssert d.hasKey("f")
+    d.inc("f")
+    merge(d, e)
+    doAssert d["f"] == 7
diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim
index 97bc51bc5..ff12be90f 100644
--- a/lib/pure/dynlib.nim
+++ b/lib/pure/dynlib.nim
@@ -12,10 +12,10 @@
 ## Windows ``LoadLibrary``.
 ##
 ## Examples
-## --------
+## ========
 ##
 ## Loading a simple C function
-## ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+## ---------------------------
 ##
 ## The following example demonstrates loading a function called 'greet'
 ## from a library that is determined at runtime based upon a language choice.
diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim
index fca78fb0f..fd6432bae 100644
--- a/lib/pure/htmlgen.nim
+++ b/lib/pure/htmlgen.nim
@@ -16,7 +16,8 @@
 ## generator. Each commonly used HTML tag has a corresponding macro
 ## that generates a string with its HTML representation.
 ##
-## Example:
+## Examples
+## ========
 ##
 ## .. code-block:: Nim
 ##   var nim = "Nim"
diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim
index f9c076158..bc4750121 100644
--- a/lib/pure/includes/osenv.nim
+++ b/lib/pure/includes/osenv.nim
@@ -1,6 +1,6 @@
 # Include file that implements 'getEnv' and friends. Do not import it!
 
-when not declared(os):
+when not declared(os) and not declared(ospaths):
   {.error: "This is an include file for os.nim!".}
 
 from parseutils import skipIgnoreCase
diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim
index 947bdd9a9..68ce5d95f 100644
--- a/lib/pure/includes/oserr.nim
+++ b/lib/pure/includes/oserr.nim
@@ -1,6 +1,6 @@
 # Include file that implements 'osErrorMsg' and friends. Do not import it!
 
-when not declared(os):
+when not declared(os) and not declared(ospaths):
   {.error: "This is an include file for os.nim!".}
 
 when not defined(nimscript):
diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim
index d043cd321..d85359953 100644
--- a/lib/pure/parsecfg.nim
+++ b/lib/pure/parsecfg.nim
@@ -12,48 +12,46 @@
 ## format, but much more powerful, as it is not a line based parser. String
 ## literals, raw string literals and triple quoted string literals are supported
 ## as in the Nim programming language.
-
-## This is an example of how a configuration file may look like:
+##
+## Example of how a configuration file may look like:
 ##
 ## .. include:: ../../doc/mytest.cfg
 ##     :literal:
 ##
-
-##[ Here is an example of how to use the configuration file parser:
-
-.. code-block:: nim
-
-    import
-      os, parsecfg, strutils, streams
-
-    var f = newFileStream(paramStr(1), fmRead)
-    if f != nil:
-      var p: CfgParser
-      open(p, f, paramStr(1))
-      while true:
-        var e = next(p)
-        case e.kind
-        of cfgEof: break
-        of cfgSectionStart:   ## a ``[section]`` has been parsed
-          echo("new section: " & e.section)
-        of cfgKeyValuePair:
-          echo("key-value-pair: " & e.key & ": " & e.value)
-        of cfgOption:
-          echo("command: " & e.key & ": " & e.value)
-        of cfgError:
-          echo(e.msg)
-      close(p)
-    else:
-      echo("cannot open: " & paramStr(1))
-
-]##
-
+## Here is an example of how to use the configuration file parser:
+##
+## .. code-block:: nim
+##
+##    import os, parsecfg, strutils, streams
+##
+##    var f = newFileStream(paramStr(1), fmRead)
+##    if f != nil:
+##      var p: CfgParser
+##      open(p, f, paramStr(1))
+##      while true:
+##        var e = next(p)
+##        case e.kind
+##        of cfgEof: break
+##        of cfgSectionStart:   ## a ``[section]`` has been parsed
+##          echo("new section: " & e.section)
+##        of cfgKeyValuePair:
+##          echo("key-value-pair: " & e.key & ": " & e.value)
+##        of cfgOption:
+##          echo("command: " & e.key & ": " & e.value)
+##        of cfgError:
+##          echo(e.msg)
+##      close(p)
+##    else:
+##      echo("cannot open: " & paramStr(1))
+##
+##
 ## Examples
-## --------
+## ========
 ##
-## This is an example of a configuration file.
+## Configuration file example
+## --------------------------
 ##
-## ::
+## .. code-block:: nim
 ##
 ##     charset = "utf-8"
 ##     [Package]
@@ -64,8 +62,8 @@
 ##     qq = "10214028"
 ##     email = "lihaifeng@wxm.com"
 ##
-## Creating a configuration file.
-## ==============================
+## Creating a configuration file
+## -----------------------------
 ## .. code-block:: nim
 ##
 ##     import parsecfg
@@ -78,8 +76,8 @@
 ##     dict.setSectionKey("Author","email","lihaifeng@wxm.com")
 ##     dict.writeConfig("config.ini")
 ##
-## Reading a configuration file.
-## =============================
+## Reading a configuration file
+## ----------------------------
 ## .. code-block:: nim
 ##
 ##     import parsecfg
@@ -92,8 +90,8 @@
 ##     var email = dict.getSectionValue("Author","email")
 ##     echo pname & "\n" & name & "\n" & qq & "\n" & email
 ##
-## Modifying a configuration file.
-## ===============================
+## Modifying a configuration file
+## ------------------------------
 ## .. code-block:: nim
 ##
 ##     import parsecfg
@@ -101,8 +99,8 @@
 ##     dict.setSectionKey("Author","name","lhf")
 ##     dict.writeConfig("config.ini")
 ##
-## Deleting a section key in a configuration file.
-## ===============================================
+## Deleting a section key in a configuration file
+## ----------------------------------------------
 ## .. code-block:: nim
 ##
 ##     import parsecfg
@@ -443,17 +441,17 @@ proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} =
 
 # ---------------- Configuration file related operations ----------------
 type
-  Config* = OrderedTableRef[string, OrderedTableRef[string, string]]
+  Config* = OrderedTableRef[string, <//>OrderedTableRef[string, string]]
 
 proc newConfig*(): Config =
   ## Create a new configuration table.
   ## Useful when wanting to create a configuration file.
-  result = newOrderedTable[string, OrderedTableRef[string, string]]()
+  result = newOrderedTable[string, <//>OrderedTableRef[string, string]]()
 
-proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config =
+proc loadConfig*(stream: Stream, filename: string = "[stream]"): <//>Config =
   ## Load the specified configuration from stream into a new Config instance.
   ## `filename` parameter is only used for nicer error messages.
-  var dict = newOrderedTable[string, OrderedTableRef[string, string]]()
+  var dict = newOrderedTable[string, <//>OrderedTableRef[string, string]]()
   var curSection = "" ## Current section,
                       ## the default value of the current section is "",
                       ## which means that the current section is a common
@@ -483,7 +481,7 @@ proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config =
   close(p)
   result = dict
 
-proc loadConfig*(filename: string): Config =
+proc loadConfig*(filename: string): <//>Config =
   ## Load the specified configuration file into a new Config instance.
   let file = open(filename, fmRead)
   let fileStream = newFileStream(file)
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index ba09347a2..a4032f8f3 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -64,104 +64,154 @@ const
 proc toLower(c: char): char {.inline.} =
   result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
 
-proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {.
-  rtl, extern: "npuParseHex", noSideEffect.}  =
-  ## Parses a hexadecimal number and stores its value in ``number``.
+proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0, maxLen = 0): int
+  {.noSideEffect.} =
+  ## Parses a binary number and stores its value in ``number``.
   ##
-  ## Returns the number of the parsed characters or 0 in case of an error. This
-  ## proc is sensitive to the already existing value of ``number`` and will
-  ## likely not do what you want unless you make sure ``number`` is zero. You
-  ## can use this feature to *chain* calls, though the result int will quickly
-  ## overflow.
+  ## Returns the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of ``number`` is not changed.
   ##
-  ## If ``maxLen == 0`` the length of the hexadecimal number has no upper bound.
-  ## Else no more than ``start + maxLen`` characters are parsed, up to the
-  ## length of the string.
+  ## If ``maxLen == 0``, the parsing continues until the first non-bin character
+  ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
+  ## are parsed starting from the ``start`` position.
+  ## 
+  ## It does not check for overflow. If the value represented by the string is
+  ## too big to fit into ``number``, only the value of last fitting characters
+  ## will be stored in ``number`` without producing an error.
   runnableExamples:
-    var value = 0
-    discard parseHex("0x38", value)
-    assert value == 56
-    discard parseHex("0x34", value)
-    assert value == 56 * 256 + 52
-    value = -1
-    discard parseHex("0x38", value)
-    assert value == -200
+    var num: int
+    doAssert parseBin("0100_1110_0110_1001_1110_1101", num) == 29
+    doAssert num == 5138925
+    doAssert parseBin("3", num) == 0
+    var num8: int8
+    doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8) == 32
+    doAssert num8 == 0b1110_1101'i8
+    doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8, 3, 9) == 9
+    doAssert num8 == 0b0100_1110'i8
+    var num8u: uint8
+    doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8u) == 32
+    doAssert num8u == 237
+    var num64: int64
+    doAssert parseBin("0100111001101001111011010100111001101001", num64) == 40
+    doAssert num64 == 336784608873
   var i = start
+  var output = T(0)
   var foundDigit = false
-  # get last index based on minimum `start + maxLen` or `s.len`
-  let last = min(s.len, if maxLen == 0: s.len else: i+maxLen)
-  if i+1 < last and s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
-  elif i < last and s[i] == '#': inc(i)
+  let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
+  if i + 1 < last and s[i] == '0' and (s[i+1] in {'b', 'B'}): inc(i, 2)
   while i < last:
     case s[i]
     of '_': discard
-    of '0'..'9':
-      number = number shl 4 or (ord(s[i]) - ord('0'))
-      foundDigit = true
-    of 'a'..'f':
-      number = number shl 4 or (ord(s[i]) - ord('a') + 10)
-      foundDigit = true
-    of 'A'..'F':
-      number = number shl 4 or (ord(s[i]) - ord('A') + 10)
+    of '0'..'1':
+      output = output shl 1 or T(ord(s[i]) - ord('0'))
       foundDigit = true
     else: break
     inc(i)
-  if foundDigit: result = i-start
+  if foundDigit:
+    number = output
+    result = i - start
 
-proc parseOct*(s: string, number: var int, start = 0, maxLen = 0): int  {.
-  rtl, extern: "npuParseOct", noSideEffect.} =
-  ## Parses an octal number and stores its value in ``number``. Returns
-  ## the number of the parsed characters or 0 in case of an error.
+proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0, maxLen = 0): int
+  {.noSideEffect.} =
+  ## Parses an octal number and stores its value in ``number``.
+  ##
+  ## Returns the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of ``number`` is not changed.
   ##
-  ## If ``maxLen == 0`` the length of the octal number has no upper bound.
-  ## Else no more than ``start + maxLen`` characters are parsed, up to the
-  ## length of the string.
+  ## If ``maxLen == 0``, the parsing continues until the first non-oct character
+  ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
+  ## are parsed starting from the ``start`` position.
+  ## 
+  ## It does not check for overflow. If the value represented by the string is
+  ## too big to fit into ``number``, only the value of last fitting characters
+  ## will be stored in ``number`` without producing an error.
   runnableExamples:
-    var res: int
-    doAssert parseOct("12", res) == 2
-    doAssert res == 10
-    doAssert parseOct("9", res) == 0
+    var num: int
+    doAssert parseOct("0o23464755", num) == 10
+    doAssert num == 5138925
+    doAssert parseOct("8", num) == 0
+    var num8: int8
+    doAssert parseOct("0o_1464_755", num8) == 11
+    doAssert num8 == -19
+    doAssert parseOct("0o_1464_755", num8, 3, 3) == 3
+    doAssert num8 == 102
+    var num8u: uint8
+    doAssert parseOct("1464755", num8u) == 7
+    doAssert num8u == 237
+    var num64: int64
+    doAssert parseOct("2346475523464755", num64) == 16
+    doAssert num64 == 86216859871725
   var i = start
+  var output = T(0)
   var foundDigit = false
-  # get last index based on minimum `start + maxLen` or `s.len`
-  let last = min(s.len, if maxLen == 0: s.len else: i+maxLen)
-  if i+1 < last and s[i] == '0' and (s[i+1] in {'o', 'O'}): inc(i, 2)
+  let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
+  if i + 1 < last and s[i] == '0' and (s[i+1] in {'o', 'O'}): inc(i, 2)
   while i < last:
     case s[i]
     of '_': discard
     of '0'..'7':
-      number = number shl 3 or (ord(s[i]) - ord('0'))
+      output = output shl 3 or T(ord(s[i]) - ord('0'))
       foundDigit = true
     else: break
     inc(i)
-  if foundDigit: result = i-start
+  if foundDigit:
+    number = output
+    result = i - start
 
-proc parseBin*(s: string, number: var int, start = 0, maxLen = 0): int {.
-  rtl, extern: "npuParseBin", noSideEffect.} =
-  ## Parses an binary number and stores its value in ``number``. Returns
-  ## the number of the parsed characters or 0 in case of an error.
+proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0, maxLen = 0): int
+  {.noSideEffect.} =
+  ## Parses a hexadecimal number and stores its value in ``number``.
+  ##
+  ## Returns the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of ``number`` is not changed.
   ##
-  ## If ``maxLen == 0`` the length of the binary number has no upper bound.
-  ## Else no more than ``start + maxLen`` characters are parsed, up to the
-  ## length of the string.
+  ## If ``maxLen == 0``, the parsing continues until the first non-hex character
+  ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
+  ## are parsed starting from the ``start`` position.
+  ## 
+  ## It does not check for overflow. If the value represented by the string is
+  ## too big to fit into ``number``, only the value of last fitting characters
+  ## will be stored in ``number`` without producing an error.
   runnableExamples:
-    var res: int
-    doAssert parseBin("010011100110100101101101", res) == 24
-    doAssert parseBin("3", res) == 0
+    var num: int
+    doAssert parseHex("4E_69_ED", num) == 8
+    doAssert num == 5138925
+    doAssert parseHex("X", num) == 0
+    doAssert parseHex("#ABC", num) == 4
+    var num8: int8
+    doAssert parseHex("0x_4E_69_ED", num8) == 11
+    doAssert num8 == 0xED'i8
+    doAssert parseHex("0x_4E_69_ED", num8, 3, 2) == 2
+    doAssert num8 == 0x4E'i8
+    var num8u: uint8
+    doAssert parseHex("0x_4E_69_ED", num8u) == 11
+    doAssert num8u == 237
+    var num64: int64
+    doAssert parseHex("4E69ED4E69ED", num64) == 12
+    doAssert num64 == 86216859871725
   var i = start
+  var output = T(0)
   var foundDigit = false
-  # get last index based on minimum `start + maxLen` or `s.len`
-  let last = min(s.len, if maxLen == 0: s.len else: i+maxLen)
-  if i+1 < last and s[i] == '0' and (s[i+1] in {'b', 'B'}): inc(i, 2)
+  let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
+  if i + 1 < last and s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
+  elif i < last and s[i] == '#': inc(i)
   while i < last:
     case s[i]
     of '_': discard
-    of '0'..'1':
-      number = number shl 1 or (ord(s[i]) - ord('0'))
+    of '0'..'9':
+      output = output shl 4 or T(ord(s[i]) - ord('0'))
+      foundDigit = true
+    of 'a'..'f':
+      output = output shl 4 or T(ord(s[i]) - ord('a') + 10)
+      foundDigit = true
+    of 'A'..'F':
+      output = output shl 4 or T(ord(s[i]) - ord('A') + 10)
       foundDigit = true
     else: break
     inc(i)
-  if foundDigit: result = i-start
+  if foundDigit:
+    number = output
+    result = i - start
 
 proc parseIdent*(s: string, ident: var string, start = 0): int =
   ## Parses an identifier and stores it in ``ident``. Returns
@@ -605,11 +655,6 @@ when isMainModule:
   var value = 0
   discard parseHex("0x38", value)
   doAssert value == 56
-  discard parseHex("0x34", value)
-  doAssert value == 56 * 256 + 52
-  value = -1
-  discard parseHex("0x38", value)
-  doAssert value == -200
 
   value = -1
   doAssert(parseSaturatedNatural("848", value) == 3)
diff --git a/lib/pure/random.nim b/lib/pure/random.nim
index e29ad7955..dddbd9d58 100644
--- a/lib/pure/random.nim
+++ b/lib/pure/random.nim
@@ -129,7 +129,7 @@ proc next*(r: var Rand): uint64 =
   ##   a given upper bound
   ## * `rand proc<#rand,Rand,range[]>`_ that returns a float
   ## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice
-  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer type
+  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
   ## * `skipRandomNumbers proc<#skipRandomNumbers,Rand>`_
   runnableExamples:
     var r = initRand(2019)
@@ -242,7 +242,7 @@ proc rand*(r: var Rand; max: Natural): int {.benign.} =
   ##   random number generator
   ## * `rand proc<#rand,Rand,range[]>`_ that returns a float
   ## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice
-  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer type
+  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
   runnableExamples:
     var r = initRand(123)
     doAssert r.rand(100) == 0
@@ -268,7 +268,7 @@ proc rand*(max: int): int {.benign.} =
   ##   provided state
   ## * `rand proc<#rand,float>`_ that returns a float
   ## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice
-  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer type
+  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
   runnableExamples:
     randomize(123)
     doAssert rand(100) == 0
@@ -285,7 +285,7 @@ proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} =
   ##   random number generator
   ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
   ## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice
-  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer type
+  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
   runnableExamples:
     var r = initRand(234)
     let f = r.rand(1.0)
@@ -311,7 +311,7 @@ proc rand*(max: float): float {.benign.} =
   ##   provided state
   ## * `rand proc<#rand,int>`_ that returns an integer
   ## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice
-  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer type
+  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
   runnableExamples:
     randomize(234)
     let f = rand(1.0)
@@ -327,7 +327,7 @@ proc rand*[T: Ordinal](r: var Rand; x: HSlice[T, T]): T =
   ##   default random number generator
   ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
   ## * `rand proc<#rand,Rand,range[]>`_ that returns a float
-  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer type
+  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
   runnableExamples:
     var r = initRand(345)
     doAssert r.rand(1..6) == 4
@@ -349,7 +349,7 @@ proc rand*[T: Ordinal](x: HSlice[T, T]): T =
   ##   a provided state
   ## * `rand proc<#rand,int>`_ that returns an integer
   ## * `rand proc<#rand,float>`_ that returns a floating point number
-  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer type
+  ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type
   runnableExamples:
     randomize(345)
     doAssert rand(1..6) == 4
@@ -383,7 +383,13 @@ proc rand*[T: SomeInteger](t: typedesc[T]): T =
     doAssert rand(uint32) == 578980729'u32
     doAssert rand(uint32) == 4052940463'u32
     doAssert rand(uint32) == 2163872389'u32
-  result = cast[T](state.next)
+    doAssert rand(range[1..16]) == 11
+    doAssert rand(range[1..16]) == 4
+    doAssert rand(range[1..16]) == 16
+  when T is range:
+    result = rand(state, low(T)..high(T))
+  else:
+    result = cast[T](state.next)
 
 proc rand*[T](a: openArray[T]): T {.deprecated.} =
   ## **Deprecated since version 0.20.0:**
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index ff2b72725..83c8c71ec 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -8,29 +8,91 @@
 #
 
 ## This module provides a stream interface and two implementations thereof:
-## the `FileStream` and the `StringStream` which implement the stream
-## interface for Nim file objects (`File`) and strings. Other modules
-## may provide other implementations for this standard stream interface.
+## the `FileStream <#FileStream>`_ and the `StringStream <#StringStream>`_
+## which implement the stream interface for Nim file objects (`File`) and
+## strings.
 ##
-## Examples:
+## Other modules may provide other implementations for this standard
+## stream interface.
+##
+## Basic usage
+## ===========
+##
+## The basic flow of using this module is:
+##
+## 1. Open input stream
+## 2. Read or write stream
+## 3. Close stream
+##
+## StringStream example
+## --------------------
 ##
 ## .. code-block:: Nim
 ##
 ##  import streams
-##  var
-##    ss = newStringStream("""The first line
+##
+##  var strm = newStringStream("""The first line
 ##  the second line
 ##  the third line""")
-##    line = ""
-##  while ss.readLine(line):
+##
+##  var line = ""
+##
+##  while strm.readLine(line):
 ##    echo line
-##  ss.close()
 ##
-##  var fs = newFileStream("somefile.txt", fmRead)
-##  if not isNil(fs):
-##    while fs.readLine(line):
+##  # Output:
+##  # The first line
+##  # the second line
+##  # the third line
+##
+##  strm.close()
+##
+## FileStream example
+## ------------------
+##
+## Write file stream example:
+##
+## .. code-block:: Nim
+##
+##  import streams
+##
+##  var strm = newFileStream("somefile.txt", fmWrite)
+##  var line = ""
+##
+##  if not isNil(strm):
+##    strm.writeLine("The first line")
+##    strm.writeLine("the second line")
+##    strm.writeLine("the third line")
+##    strm.close()
+##
+##  # Output (somefile.txt):
+##  # The first line
+##  # the second line
+##  # the third line
+##
+## Read file stream example:
+##
+## .. code-block:: Nim
+##
+##  import streams
+##
+##  var strm = newFileStream("somefile.txt", fmRead)
+##  var line = ""
+##
+##  if not isNil(strm):
+##    while strm.readLine(line):
 ##      echo line
-##    fs.close()
+##    strm.close()
+##
+##  # Output:
+##  # The first line
+##  # the second line
+##  # the third line
+##
+## See also
+## ========
+## * `asyncstreams module <asyncstreams.html>`_
+## * `io module <io.html>`_ for `FileMode enum <io.html#FileMode>`_
 
 include "system/inclrtl"
 
@@ -40,11 +102,14 @@ proc newEIO(msg: string): owned(ref IOError) =
 
 type
   Stream* = ref StreamObj
-  StreamObj* = object of RootObj ## Stream interface that supports
-                                 ## writing or reading. Note that these fields
-                                 ## here shouldn't be used directly. They are
-                                 ## accessible so that a stream implementation
-                                 ## can override them.
+    ## All procedures of this module use this type.
+    ## Procedures don't directly use `StreamObj <#StreamObj>`_.
+  StreamObj* = object of RootObj
+    ## Stream interface that supports writing or reading.
+    ##
+    ## **Note:**
+    ## * That these fields here shouldn't be used directly.
+    ##   They are accessible so that a stream implementation can override them.
     closeImpl*: proc (s: Stream)
       {.nimcall, raises: [Exception, IOError, OSError], tags: [WriteIOEffect], gcsafe.}
     atEndImpl*: proc (s: Stream): bool
@@ -68,32 +133,103 @@ type
       {.nimcall, raises: [Defect, IOError, OSError], tags: [WriteIOEffect], gcsafe.}
 
 proc flush*(s: Stream) =
-  ## flushes the buffers that the stream `s` might use.
+  ## Flushes the buffers that the stream `s` might use.
+  ##
+  ## This procedure causes any unwritten data for that stream to be delivered
+  ## to the host environment to be written to the file.
+  ##
+  ## See also:
+  ## * `close proc <#close,Stream>`_
+  runnableExamples:
+    from os import removeFile
+
+    var strm = newFileStream("somefile.txt", fmWrite)
+
+    doAssert "Before write:" & readFile("somefile.txt") == "Before write:"
+    strm.write("hello")
+    doAssert "After  write:" & readFile("somefile.txt") == "After  write:"
+
+    strm.flush()
+    doAssert "After  flush:" & readFile("somefile.txt") == "After  flush:hello"
+    strm.write("HELLO")
+    strm.flush()
+    doAssert "After  flush:" & readFile("somefile.txt") == "After  flush:helloHELLO"
+
+    strm.close()
+    doAssert "After  close:" & readFile("somefile.txt") == "After  close:helloHELLO"
+    removeFile("somefile.txt")
+
   if not isNil(s.flushImpl): s.flushImpl(s)
 
 proc close*(s: Stream) =
-  ## closes the stream `s`.
+  ## Closes the stream `s`.
+  ##
+  ## See also:
+  ## * `flush proc <#flush,Stream>`_
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    ## do something...
+    strm.close()
   if not isNil(s.closeImpl): s.closeImpl(s)
 
 proc atEnd*(s: Stream): bool =
-  ## checks if more data can be read from `f`. Returns true if all data has
+  ## Checks if more data can be read from `s`. Returns ``true`` if all data has
   ## been read.
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    var line = ""
+    doAssert strm.atEnd() == false
+    while strm.readLine(line):
+      discard
+    doAssert strm.atEnd() == true
+    strm.close()
+
   result = s.atEndImpl(s)
 
 proc setPosition*(s: Stream, pos: int) =
-  ## sets the position `pos` of the stream `s`.
+  ## Sets the position `pos` of the stream `s`.
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    strm.setPosition(4)
+    doAssert strm.readLine() == "first line"
+    strm.setPosition(0)
+    doAssert strm.readLine() == "The first line"
+    strm.close()
+
   s.setPositionImpl(s, pos)
 
 proc getPosition*(s: Stream): int =
-  ## retrieves the current position in the stream `s`.
+  ## Retrieves the current position in the stream `s`.
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    doAssert strm.getPosition() == 0
+    discard strm.readLine()
+    doAssert strm.getPosition() == 15
+    strm.close()
+
   result = s.getPositionImpl(s)
 
 proc readData*(s: Stream, buffer: pointer, bufLen: int): int =
-  ## low level proc that reads data into an untyped `buffer` of `bufLen` size.
+  ## Low level proc that reads data into an untyped `buffer` of `bufLen` size.
+  runnableExamples:
+    var strm = newStringStream("abcde")
+    var buffer: array[6, char]
+    doAssert strm.readData(addr(buffer), 1024) == 5
+    doAssert buffer == ['a', 'b', 'c', 'd', 'e', '\x00']
+    doAssert strm.atEnd() == true
+    strm.close()
+
   result = s.readDataImpl(s, buffer, bufLen)
 
 proc readDataStr*(s: Stream, buffer: var string, slice: Slice[int]): int =
-  ## low level proc that reads data into a string ``buffer`` at ``slice``.
+  ## Low level proc that reads data into a string ``buffer`` at ``slice``.
+  runnableExamples:
+    var strm = newStringStream("abcde")
+    var buffer = "12345"
+    doAssert strm.readDataStr(buffer, 0..3) == 4
+    doAssert buffer == "abcd5"
+    strm.close()
+
   if s.readDataStrImpl != nil:
     result = s.readDataStrImpl(s, buffer, slice)
   else:
@@ -103,6 +239,14 @@ proc readDataStr*(s: Stream, buffer: var string, slice: Slice[int]): int =
 when not defined(js):
   proc readAll*(s: Stream): string =
     ## Reads all available data.
+    ##
+    ## **Note:** Not available for JS backend.
+    runnableExamples:
+      var strm = newStringStream("The first line\nthe second line\nthe third line")
+      doAssert strm.readAll() == "The first line\nthe second line\nthe third line"
+      doAssert strm.atEnd() == true
+      strm.close()
+
     const bufferSize = 1024
     var buffer {.noinit.}: array[bufferSize, char]
     while true:
@@ -116,173 +260,611 @@ when not defined(js):
         break
 
 proc peekData*(s: Stream, buffer: pointer, bufLen: int): int =
-  ## low level proc that reads data into an untyped `buffer` of `bufLen` size
-  ## without moving stream position
+  ## Low level proc that reads data into an untyped `buffer` of `bufLen` size
+  ## without moving stream position.
+  runnableExamples:
+    var strm = newStringStream("abcde")
+    var buffer: array[6, char]
+    doAssert strm.peekData(addr(buffer), 1024) == 5
+    doAssert buffer == ['a', 'b', 'c', 'd', 'e', '\x00']
+    doAssert strm.atEnd() == false
+    strm.close()
+
   result = s.peekDataImpl(s, buffer, bufLen)
 
 proc writeData*(s: Stream, buffer: pointer, bufLen: int) =
-  ## low level proc that writes an untyped `buffer` of `bufLen` size
+  ## Low level proc that writes an untyped `buffer` of `bufLen` size
   ## to the stream `s`.
+  runnableExamples:
+    ## writeData
+    var strm = newStringStream("")
+    var buffer = ['a', 'b', 'c', 'd', 'e']
+    strm.writeData(addr(buffer), sizeof(buffer))
+    doAssert strm.atEnd() == true
+    ## readData
+    strm.setPosition(0)
+    var buffer2: array[6, char]
+    doAssert strm.readData(addr(buffer2), sizeof(buffer2)) == 5
+    doAssert buffer2 == ['a', 'b', 'c', 'd', 'e', '\x00']
+    strm.close()
+
   s.writeDataImpl(s, buffer, bufLen)
 
 proc write*[T](s: Stream, x: T) =
-  ## generic write procedure. Writes `x` to the stream `s`. Implementation:
+  ## Generic write procedure. Writes `x` to the stream `s`. Implementation:
   ##
   ## .. code-block:: Nim
   ##
   ##     s.writeData(s, addr(x), sizeof(x))
+  runnableExamples:
+    var strm = newStringStream("")
+    strm.write("abcde")
+    strm.setPosition(0)
+    doAssert strm.readAll() == "abcde"
+    strm.close()
+
   var y: T
   shallowCopy(y, x)
   writeData(s, addr(y), sizeof(y))
 
 proc write*(s: Stream, x: string) =
-  ## writes the string `x` to the the stream `s`. No length field or
+  ## Writes the string `x` to the the stream `s`. No length field or
   ## terminating zero is written.
+  runnableExamples:
+    var strm = newStringStream("")
+    strm.write("THE FIRST LINE")
+    strm.setPosition(0)
+    doAssert strm.readLine() == "THE FIRST LINE"
+    strm.close()
+
   when nimvm:
     writeData(s, cstring(x), x.len)
   else:
     if x.len > 0: writeData(s, cstring(x), x.len)
 
 proc write*(s: Stream, args: varargs[string, `$`]) =
-  ## writes one or more strings to the the stream. No length fields or
+  ## Writes one or more strings to the the stream. No length fields or
   ## terminating zeros are written.
+  runnableExamples:
+    var strm = newStringStream("")
+    strm.write(1, 2, 3, 4)
+    strm.setPosition(0)
+    doAssert strm.readLine() == "1234"
+    strm.close()
+
   for str in args: write(s, str)
 
 proc writeLine*(s: Stream, args: varargs[string, `$`]) =
-  ## writes one or more strings to the the stream `s` followed
+  ## Writes one or more strings to the the stream `s` followed
   ## by a new line. No length field or terminating zero is written.
+  runnableExamples:
+    var strm = newStringStream("")
+    strm.writeLine(1, 2)
+    strm.writeLine(3, 4)
+    strm.setPosition(0)
+    doAssert strm.readAll() == "12\n34\n"
+    strm.close()
+
   for str in args: write(s, str)
   write(s, "\n")
 
 proc read*[T](s: Stream, result: var T) =
-  ## generic read procedure. Reads `result` from the stream `s`.
+  ## Generic read procedure. Reads `result` from the stream `s`.
+  runnableExamples:
+    var strm = newStringStream("012")
+    ## readInt
+    var i: int8
+    strm.read(i)
+    doAssert i == 48
+    ## readData
+    var buffer: array[2, char]
+    strm.read(buffer)
+    doAssert buffer == ['1', '2']
+    strm.close()
+
   if readData(s, addr(result), sizeof(T)) != sizeof(T):
     raise newEIO("cannot read from stream")
 
 proc peek*[T](s: Stream, result: var T) =
-  ## generic peek procedure. Peeks `result` from the stream `s`.
+  ## Generic peek procedure. Peeks `result` from the stream `s`.
+  runnableExamples:
+    var strm = newStringStream("012")
+    ## peekInt
+    var i: int8
+    strm.peek(i)
+    doAssert i == 48
+    ## peekData
+    var buffer: array[2, char]
+    strm.peek(buffer)
+    doAssert buffer == ['0', '1']
+    strm.close()
+
   if peekData(s, addr(result), sizeof(T)) != sizeof(T):
     raise newEIO("cannot read from stream")
 
 proc readChar*(s: Stream): char =
-  ## reads a char from the stream `s`. Raises `IOError` if an error occurred.
+  ## Reads a char from the stream `s`.
+  ##
+  ## Raises `IOError` if an error occurred.
   ## Returns '\\0' as an EOF marker.
+  runnableExamples:
+    var strm = newStringStream("12\n3")
+    doAssert strm.readChar() == '1'
+    doAssert strm.readChar() == '2'
+    doAssert strm.readChar() == '\n'
+    doAssert strm.readChar() == '3'
+    doAssert strm.readChar() == '\x00'
+    strm.close()
+
   if readData(s, addr(result), sizeof(result)) != 1: result = '\0'
 
 proc peekChar*(s: Stream): char =
-  ## peeks a char from the stream `s`. Raises `IOError` if an error occurred.
+  ## Peeks a char from the stream `s`. Raises `IOError` if an error occurred.
   ## Returns '\\0' as an EOF marker.
+  runnableExamples:
+    var strm = newStringStream("12\n3")
+    doAssert strm.peekChar() == '1'
+    doAssert strm.peekChar() == '1'
+    discard strm.readAll()
+    doAssert strm.peekChar() == '\x00'
+    strm.close()
+
   if peekData(s, addr(result), sizeof(result)) != 1: result = '\0'
 
 proc readBool*(s: Stream): bool =
-  ## reads a bool from the stream `s`. Raises `IOError` if an error occurred.
+  ## Reads a bool from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(true)
+    strm.write(false)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readBool() == true
+    doAssert strm.readBool() == false
+    doAssertRaises(IOError): discard strm.readBool()
+    strm.close()
+
   read(s, result)
 
 proc peekBool*(s: Stream): bool =
-  ## peeks a bool from the stream `s`. Raises `IOError` if an error occurred.
+  ## Peeks a bool from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(true)
+    strm.write(false)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekBool() == true
+    ## not false
+    doAssert strm.peekBool() == true
+    doAssert strm.readBool() == true
+    doAssert strm.peekBool() == false
+    strm.close()
+
   peek(s, result)
 
 proc readInt8*(s: Stream): int8 =
-  ## reads an int8 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Reads an int8 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i8)
+    strm.write(2'i8)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readInt8() == 1'i8
+    doAssert strm.readInt8() == 2'i8
+    doAssertRaises(IOError): discard strm.readInt8()
+    strm.close()
+
   read(s, result)
 
 proc peekInt8*(s: Stream): int8 =
-  ## peeks an int8 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Peeks an int8 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i8)
+    strm.write(2'i8)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekInt8() == 1'i8
+    ## not 2'i8
+    doAssert strm.peekInt8() == 1'i8
+    doAssert strm.readInt8() == 1'i8
+    doAssert strm.peekInt8() == 2'i8
+    strm.close()
+
   peek(s, result)
 
 proc readInt16*(s: Stream): int16 =
-  ## reads an int16 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Reads an int16 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i16)
+    strm.write(2'i16)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readInt16() == 1'i16
+    doAssert strm.readInt16() == 2'i16
+    doAssertRaises(IOError): discard strm.readInt16()
+    strm.close()
+
   read(s, result)
 
 proc peekInt16*(s: Stream): int16 =
-  ## peeks an int16 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Peeks an int16 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i16)
+    strm.write(2'i16)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekInt16() == 1'i16
+    ## not 2'i16
+    doAssert strm.peekInt16() == 1'i16
+    doAssert strm.readInt16() == 1'i16
+    doAssert strm.peekInt16() == 2'i16
+    strm.close()
+
   peek(s, result)
 
 proc readInt32*(s: Stream): int32 =
-  ## reads an int32 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Reads an int32 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i32)
+    strm.write(2'i32)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readInt32() == 1'i32
+    doAssert strm.readInt32() == 2'i32
+    doAssertRaises(IOError): discard strm.readInt32()
+    strm.close()
+
   read(s, result)
 
 proc peekInt32*(s: Stream): int32 =
-  ## peeks an int32 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Peeks an int32 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i32)
+    strm.write(2'i32)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekInt32() == 1'i32
+    ## not 2'i32
+    doAssert strm.peekInt32() == 1'i32
+    doAssert strm.readInt32() == 1'i32
+    doAssert strm.peekInt32() == 2'i32
+    strm.close()
+
   peek(s, result)
 
 proc readInt64*(s: Stream): int64 =
-  ## reads an int64 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Reads an int64 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i64)
+    strm.write(2'i64)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readInt64() == 1'i64
+    doAssert strm.readInt64() == 2'i64
+    doAssertRaises(IOError): discard strm.readInt64()
+    strm.close()
+
   read(s, result)
 
 proc peekInt64*(s: Stream): int64 =
-  ## peeks an int64 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Peeks an int64 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'i64)
+    strm.write(2'i64)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekInt64() == 1'i64
+    ## not 2'i64
+    doAssert strm.peekInt64() == 1'i64
+    doAssert strm.readInt64() == 1'i64
+    doAssert strm.peekInt64() == 2'i64
+    strm.close()
+
   peek(s, result)
 
 proc readUint8*(s: Stream): uint8 =
-  ## reads an uint8 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Reads an uint8 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u8)
+    strm.write(2'u8)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readUint8() == 1'u8
+    doAssert strm.readUint8() == 2'u8
+    doAssertRaises(IOError): discard strm.readUint8()
+    strm.close()
+
   read(s, result)
 
 proc peekUint8*(s: Stream): uint8 =
-  ## peeks an uint8 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Peeks an uint8 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u8)
+    strm.write(2'u8)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekUint8() == 1'u8
+    ## not 2'u8
+    doAssert strm.peekUint8() == 1'u8
+    doAssert strm.readUint8() == 1'u8
+    doAssert strm.peekUint8() == 2'u8
+    strm.close()
+
   peek(s, result)
 
 proc readUint16*(s: Stream): uint16 =
-  ## reads an uint16 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Reads an uint16 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u16)
+    strm.write(2'u16)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readUint16() == 1'u16
+    doAssert strm.readUint16() == 2'u16
+    doAssertRaises(IOError): discard strm.readUint16()
+    strm.close()
+
   read(s, result)
 
 proc peekUint16*(s: Stream): uint16 =
-  ## peeks an uint16 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Peeks an uint16 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u16)
+    strm.write(2'u16)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekUint16() == 1'u16
+    ## not 2'u16
+    doAssert strm.peekUint16() == 1'u16
+    doAssert strm.readUint16() == 1'u16
+    doAssert strm.peekUint16() == 2'u16
+    strm.close()
+
   peek(s, result)
 
 proc readUint32*(s: Stream): uint32 =
-  ## reads an uint32 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Reads an uint32 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u32)
+    strm.write(2'u32)
+    strm.flush()
+    strm.setPosition(0)
+
+    ## get data
+    doAssert strm.readUint32() == 1'u32
+    doAssert strm.readUint32() == 2'u32
+    doAssertRaises(IOError): discard strm.readUint32()
+    strm.close()
+
   read(s, result)
 
 proc peekUint32*(s: Stream): uint32 =
-  ## peeks an uint32 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Peeks an uint32 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u32)
+    strm.write(2'u32)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekUint32() == 1'u32
+    ## not 2'u32
+    doAssert strm.peekUint32() == 1'u32
+    doAssert strm.readUint32() == 1'u32
+    doAssert strm.peekUint32() == 2'u32
+    strm.close()
+
   peek(s, result)
 
 proc readUint64*(s: Stream): uint64 =
-  ## reads an uint64 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Reads an uint64 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u64)
+    strm.write(2'u64)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readUint64() == 1'u64
+    doAssert strm.readUint64() == 2'u64
+    doAssertRaises(IOError): discard strm.readUint64()
+    strm.close()
+
   read(s, result)
 
 proc peekUint64*(s: Stream): uint64 =
-  ## peeks an uint64 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Peeks an uint64 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'u64)
+    strm.write(2'u64)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekUint64() == 1'u64
+    ## not 2'u64
+    doAssert strm.peekUint64() == 1'u64
+    doAssert strm.readUint64() == 1'u64
+    doAssert strm.peekUint64() == 2'u64
+    strm.close()
+
   peek(s, result)
 
 proc readFloat32*(s: Stream): float32 =
-  ## reads a float32 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Reads a float32 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'f32)
+    strm.write(2'f32)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readFloat32() == 1'f32
+    doAssert strm.readFloat32() == 2'f32
+    doAssertRaises(IOError): discard strm.readFloat32()
+    strm.close()
+
   read(s, result)
 
 proc peekFloat32*(s: Stream): float32 =
-  ## peeks a float32 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Peeks a float32 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'f32)
+    strm.write(2'f32)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekFloat32() == 1'f32
+    ## not 2'f32
+    doAssert strm.peekFloat32() == 1'f32
+    doAssert strm.readFloat32() == 1'f32
+    doAssert strm.peekFloat32() == 2'f32
+    strm.close()
+
   peek(s, result)
 
 proc readFloat64*(s: Stream): float64 =
-  ## reads a float64 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Reads a float64 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'f64)
+    strm.write(2'f64)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.readFloat64() == 1'f64
+    doAssert strm.readFloat64() == 2'f64
+    doAssertRaises(IOError): discard strm.readFloat64()
+    strm.close()
+
   read(s, result)
 
 proc peekFloat64*(s: Stream): float64 =
-  ## peeks a float64 from the stream `s`. Raises `IOError` if an error occurred.
+  ## Peeks a float64 from the stream `s`. Raises `IOError` if an error occurred.
+  runnableExamples:
+    var strm = newStringStream()
+    ## setup for reading data
+    strm.write(1'f64)
+    strm.write(2'f64)
+    strm.flush()
+    strm.setPosition(0)
+    ## get data
+    doAssert strm.peekFloat64() == 1'f64
+    ## not 2'f64
+    doAssert strm.peekFloat64() == 1'f64
+    doAssert strm.readFloat64() == 1'f64
+    doAssert strm.peekFloat64() == 2'f64
+    strm.close()
+
   peek(s, result)
 
 proc readStr*(s: Stream, length: int): TaintedString =
-  ## reads a string of length `length` from the stream `s`. Raises `IOError` if
+  ## Reads a string of length `length` from the stream `s`. Raises `IOError` if
   ## an error occurred.
+  runnableExamples:
+    var strm = newStringStream("abcde")
+    doAssert strm.readStr(2) == "ab"
+    doAssert strm.readStr(2) == "cd"
+    doAssert strm.readStr(2) == "e"
+    doAssert strm.readStr(2) == ""
+    strm.close()
+
   result = newString(length).TaintedString
   var L = readData(s, cstring(result), length)
   if L != length: setLen(result.string, L)
 
 proc peekStr*(s: Stream, length: int): TaintedString =
-  ## peeks a string of length `length` from the stream `s`. Raises `IOError` if
+  ## Peeks a string of length `length` from the stream `s`. Raises `IOError` if
   ## an error occurred.
+  runnableExamples:
+    var strm = newStringStream("abcde")
+    doAssert strm.peekStr(2) == "ab"
+    ## not "cd
+    doAssert strm.peekStr(2) == "ab"
+    doAssert strm.readStr(2) == "ab"
+    doAssert strm.peekStr(2) == "cd"
+    strm.close()
+
   result = newString(length).TaintedString
   var L = peekData(s, cstring(result), length)
   if L != length: setLen(result.string, L)
 
 proc readLine*(s: Stream, line: var TaintedString): bool =
-  ## reads a line of text from the stream `s` into `line`. `line` must not be
+  ## Reads a line of text from the stream `s` into `line`. `line` must not be
   ## ``nil``! May throw an IO exception.
+  ##
   ## A line of text may be delimited by ``LF`` or ``CRLF``.
   ## The newline character(s) are not part of the returned string.
   ## Returns ``false`` if the end of the file has been reached, ``true``
   ## otherwise. If ``false`` is returned `line` contains no new data.
+  ##
+  ## See also:
+  ## * `readLine(Stream) proc <#readLine,Stream>`_
+  ## * `peekLine(Stream) proc <#peekLine,Stream>`_
+  ## * `peekLine(Stream, TaintedString) proc <#peekLine,Stream,TaintedString>`_
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    var line = ""
+    doAssert strm.readLine(line) == true
+    doAssert line == "The first line"
+    doAssert strm.readLine(line) == true
+    doAssert line == "the second line"
+    doAssert strm.readLine(line) == true
+    doAssert line == "the third line"
+    doAssert strm.readLine(line) == false
+    doAssert line == ""
+    strm.close()
+
   line.string.setLen(0)
   while true:
     var c = readChar(s)
@@ -297,19 +879,53 @@ proc readLine*(s: Stream, line: var TaintedString): bool =
   result = true
 
 proc peekLine*(s: Stream, line: var TaintedString): bool =
-  ## peeks a line of text from the stream `s` into `line`. `line` must not be
+  ## Peeks a line of text from the stream `s` into `line`. `line` must not be
   ## ``nil``! May throw an IO exception.
+  ##
   ## A line of text may be delimited by ``CR``, ``LF`` or
   ## ``CRLF``. The newline character(s) are not part of the returned string.
   ## Returns ``false`` if the end of the file has been reached, ``true``
   ## otherwise. If ``false`` is returned `line` contains no new data.
+  ##
+  ## See also:
+  ## * `readLine(Stream) proc <#readLine,Stream>`_
+  ## * `readLine(Stream, TaintedString) proc <#readLine,Stream,TaintedString>`_
+  ## * `peekLine(Stream) proc <#peekLine,Stream>`_
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    var line = ""
+    doAssert strm.peekLine(line) == true
+    doAssert line == "The first line"
+    doAssert strm.peekLine(line) == true
+    ## not "the second line"
+    doAssert line == "The first line"
+    doAssert strm.readLine(line) == true
+    doAssert line == "The first line"
+    doAssert strm.peekLine(line) == true
+    doAssert line == "the second line"
+    strm.close()
+
   let pos = getPosition(s)
   defer: setPosition(s, pos)
   result = readLine(s, line)
 
 proc readLine*(s: Stream): TaintedString =
-  ## Reads a line from a stream `s`. Note: This is not very efficient. Raises
-  ## `IOError` if an error occurred.
+  ## Reads a line from a stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** This is not very efficient.
+  ##
+  ## See also:
+  ## * `readLine(Stream, TaintedString) proc <#readLine,Stream,TaintedString>`_
+  ## * `peekLine(Stream) proc <#peekLine,Stream>`_
+  ## * `peekLine(Stream, TaintedString) proc <#peekLine,Stream,TaintedString>`_
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    doAssert strm.readLine() == "The first line"
+    doAssert strm.readLine() == "the second line"
+    doAssert strm.readLine() == "the third line"
+    doAssertRaises(IOError): discard strm.readLine()
+    strm.close()
+
   result = TaintedString""
   if s.atEnd:
     raise newEIO("cannot read from stream")
@@ -324,8 +940,23 @@ proc readLine*(s: Stream): TaintedString =
       result.string.add(c)
 
 proc peekLine*(s: Stream): TaintedString =
-  ## Peeks a line from a stream `s`. Note: This is not very efficient. Raises
-  ## `IOError` if an error occurred.
+  ## Peeks a line from a stream `s`. Raises `IOError` if an error occurred.
+  ##
+  ## **Note:** This is not very efficient.
+  ##
+  ## See also:
+  ## * `readLine(Stream) proc <#readLine,Stream>`_
+  ## * `readLine(Stream, TaintedString) proc <#readLine,Stream,TaintedString>`_
+  ## * `peekLine(Stream, TaintedString) proc <#peekLine,Stream,TaintedString>`_
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    doAssert strm.peekLine() == "The first line"
+    ## not "the second line"
+    doAssert strm.peekLine() == "The first line"
+    doAssert strm.readLine() == "The first line"
+    doAssert strm.peekLine() == "the second line"
+    strm.close()
+
   let pos = getPosition(s)
   defer: setPosition(s, pos)
   result = readLine(s)
@@ -333,6 +964,18 @@ proc peekLine*(s: Stream): TaintedString =
 iterator lines*(s: Stream): TaintedString =
   ## Iterates over every line in the stream.
   ## The iteration is based on ``readLine``.
+  ##
+  ## See also:
+  ## * `readLine(Stream) proc <#readLine,Stream>`_
+  ## * `readLine(Stream, TaintedString) proc <#readLine,Stream,TaintedString>`_
+  runnableExamples:
+    var strm = newStringStream("The first line\nthe second line\nthe third line")
+    var lines: seq[string]
+    for line in strm.lines():
+      lines.add line
+    doAssert lines == @["The first line", "the second line", "the third line"]
+    strm.close()
+
   var line: TaintedString
   while s.readLine(line):
     yield line
@@ -340,9 +983,16 @@ iterator lines*(s: Stream): TaintedString =
 when not defined(js):
 
   type
-    StringStream* = ref StringStreamObj ## a stream that encapsulates a string
+    StringStream* = ref StringStreamObj
+      ## A stream that encapsulates a string.
+      ##
+      ## **Note:** Not available for JS backend.
     StringStreamObj* = object of StreamObj
-      data*: string
+      ## A string stream object.
+      ##
+      ## **Note:** Not available for JS backend.
+      data*: string ## A string data.
+                    ## This is updated when called `writeLine` etc.
       pos: int
 
   proc ssAtEnd(s: Stream): bool =
@@ -404,7 +1054,24 @@ when not defined(js):
       s.data = nil
 
   proc newStringStream*(s: string = ""): owned StringStream =
-    ## creates a new stream from the string `s`.
+    ## Creates a new stream from the string `s`.
+    ##
+    ## **Note:** Not available for JS backend.
+    ##
+    ## See also:
+    ## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from
+    ##   opened File.
+    ## * `newFileStream proc <#newFileStream,string,FileMode,int>`_  creates a
+    ##   file stream from the file name and the mode.
+    ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a
+    ##   file stream from the file name and the mode.
+    runnableExamples:
+      var strm = newStringStream("The first line\nthe second line\nthe third line")
+      doAssert strm.readLine() == "The first line"
+      doAssert strm.readLine() == "the second line"
+      doAssert strm.readLine() == "the third line"
+      strm.close()
+
     new(result)
     result.data = s
     result.pos = 0
@@ -418,8 +1085,14 @@ when not defined(js):
     result.readDataStrImpl = ssReadDataStr
 
   type
-    FileStream* = ref FileStreamObj ## a stream that encapsulates a `File`
+    FileStream* = ref FileStreamObj
+      ## A stream that encapsulates a `File`.
+      ##
+      ## **Note:** Not available for JS backend.
     FileStreamObj* = object of Stream
+      ## A file stream object.
+      ##
+      ## **Note:** Not available for JS backend.
       f: File
 
   proc fsClose(s: Stream) =
@@ -447,7 +1120,35 @@ when not defined(js):
       raise newEIO("cannot write to stream")
 
   proc newFileStream*(f: File): owned FileStream =
-    ## creates a new stream from the file `f`.
+    ## Creates a new stream from the file `f`.
+    ##
+    ## **Note:** Not available for JS backend.
+    ##
+    ## See also:
+    ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream
+    ##   from string.
+    ## * `newFileStream proc <#newFileStream,string,FileMode,int>`_ is the same
+    ##   as using `open proc <system.html#open,File,string,FileMode,int>`_
+    ##   on Examples.
+    ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a
+    ##   file stream from the file name and the mode.
+    runnableExamples:
+      ## Input (somefile.txt):
+      ## The first line
+      ## the second line
+      ## the third line
+      var f: File
+      if open(f, "somefile.txt", fmRead, -1):
+        var strm = newFileStream(f)
+        var line = ""
+        while strm.readLine(line):
+          echo line
+        ## Output:
+        ## The first line
+        ## the second line
+        ## the third line
+        strm.close()
+
     new(result)
     result.f = f
     result.closeImpl = fsClose
@@ -461,26 +1162,76 @@ when not defined(js):
     result.flushImpl = fsFlush
 
   proc newFileStream*(filename: string, mode: FileMode = fmRead, bufSize: int = -1): owned FileStream =
-    ## creates a new stream from the file named `filename` with the mode `mode`.
-    ## If the file cannot be opened, nil is returned. See the `system
-    ## <system.html>`_ module for a list of available FileMode enums.
-    ## **This function returns nil in case of failure. To prevent unexpected
-    ## behavior and ensure proper error handling, use openFileStream instead.**
+    ## Creates a new stream from the file named `filename` with the mode `mode`.
+    ##
+    ## If the file cannot be opened, `nil` is returned. See the `io module
+    ## <io.html>`_ for a list of available `FileMode enums <io.html#FileMode>`_.
+    ##
+    ## **Note:**
+    ## * **This function returns nil in case of failure.**
+    ##   To prevent unexpected behavior and ensure proper error handling,
+    ##   use `openFileStream proc <#openFileStream,string,FileMode,int>`_
+    ##   instead.
+    ## * Not available for JS backend.
+    ##
+    ## See also:
+    ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream
+    ##   from string.
+    ## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from
+    ##   opened File.
+    ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a
+    ##   file stream from the file name and the mode.
+    runnableExamples:
+      from os import removeFile
+      var strm = newFileStream("somefile.txt", fmWrite)
+      if not isNil(strm):
+        strm.writeLine("The first line")
+        strm.writeLine("the second line")
+        strm.writeLine("the third line")
+        strm.close()
+        ## Output (somefile.txt)
+        ## The first line
+        ## the second line
+        ## the third line
+        removeFile("somefile.txt")
+
     var f: File
     if open(f, filename, mode, bufSize): result = newFileStream(f)
 
   proc openFileStream*(filename: string, mode: FileMode = fmRead, bufSize: int = -1): owned FileStream =
-    ## creates a new stream from the file named `filename` with the mode `mode`.
+    ## Creates a new stream from the file named `filename` with the mode `mode`.
     ## If the file cannot be opened, an IO exception is raised.
+    ##
+    ## **Note:** Not available for JS backend.
+    ##
+    ## See also:
+    ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream
+    ##   from string.
+    ## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from
+    ##   opened File.
+    ## * `newFileStream proc <#newFileStream,string,FileMode,int>`_  creates a
+    ##   file stream from the file name and the mode.
+    runnableExamples:
+      try:
+        ## Input (somefile.txt):
+        ## The first line
+        ## the second line
+        ## the third line
+        var strm = openFileStream("somefile.txt")
+        echo strm.readLine()
+        ## Output:
+        ## The first line
+        strm.close()
+      except:
+        stderr.write getCurrentExceptionMsg()
+
     var f: File
     if open(f, filename, mode, bufSize):
       return newFileStream(f)
     else:
-      raise newEIO("cannot open file")
+      raise newEIO("cannot open file\n")
 
-when true:
-  discard
-else:
+when false:
   type
     FileHandleStream* = ref FileHandleStreamObj
     FileHandleStreamObj* = object of Stream
diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim
index 19fbf8ab3..fdf1753a8 100644
--- a/lib/pure/strformat.nim
+++ b/lib/pure/strformat.nim
@@ -419,6 +419,9 @@ proc formatValue*(result: var string; value: SomeInteger; specifier: string) =
   ## Standard format implementation for ``SomeInteger``. It makes little
   ## sense to call this directly, but it is required to exist
   ## by the ``&`` macro.
+  if specifier.len == 0:
+    result.add $value
+    return
   let spec = parseStandardFormatSpecifier(specifier)
   var radix = 10
   case spec.typ
@@ -432,10 +435,13 @@ proc formatValue*(result: var string; value: SomeInteger; specifier: string) =
       " of 'x', 'X', 'b', 'd', 'o' but got: " & spec.typ)
   result.add formatInt(value, radix, spec)
 
-proc formatValue*(result: var string; value: SomeFloat; specifier: string): void =
+proc formatValue*(result: var string; value: SomeFloat; specifier: string) =
   ## Standard format implementation for ``SomeFloat``. It makes little
   ## sense to call this directly, but it is required to exist
   ## by the ``&`` macro.
+  if specifier.len == 0:
+    result.add $value
+    return
   let spec = parseStandardFormatSpecifier(specifier)
 
   var fmode = ffDefault
@@ -457,7 +463,7 @@ proc formatValue*(result: var string; value: SomeFloat; specifier: string): void
   if value >= 0.0:
     if spec.sign != '-':
       sign = true
-      if  value == 0.0:
+      if value == 0.0:
         if 1.0 / value == Inf:
           # only insert the sign if value != negZero
           f.insert($spec.sign, 0)
@@ -467,16 +473,16 @@ proc formatValue*(result: var string; value: SomeFloat; specifier: string): void
     sign = true
 
   if spec.padWithZero:
-    var sign_str = ""
+    var signStr = ""
     if sign:
-      sign_str = $f[0]
+      signStr = $f[0]
       f = f[1..^1]
 
     let toFill = spec.minimumWidth - f.len - ord(sign)
     if toFill > 0:
       f = repeat('0', toFill) & f
     if sign:
-      f = sign_str & f
+      f = signStr & f
 
   # the default for numbers is right-alignment:
   let align = if spec.align == '\0': '>' else: spec.align
@@ -712,6 +718,13 @@ when isMainModule:
   for s in invalidUtf8:
     check &"{s:>5}", repeat(" ", 5-s.len) & s
 
+  # bug #11089
+  let flfoo: float = 1.0
+  check &"{flfoo}", "1.0"
+
+  # bug #11092
+  check &"{high(int64)}", "9223372036854775807"
+  check &"{low(int64)}", "-9223372036854775808"
 
   import json
 
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 8074c09e8..dc79877fd 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -17,7 +17,8 @@
   resolution used by ``getTime()`` depends on the platform and backend
   (JS is limited to millisecond precision).
 
-  Examples:
+  Examples
+  ========
 
   .. code-block:: nim
     import times, os
@@ -38,7 +39,7 @@
     echo "One month from now     : ", now() + 1.months
 
   Parsing and Formatting Dates
-  ----------------------------
+  ============================
 
   The ``DateTime`` type can be parsed and formatted using the different
   ``parse`` and ``format`` procedures.
@@ -128,7 +129,7 @@
   only for years in the range 1..9999).
 
   Duration vs TimeInterval
-  ----------------------------
+  ============================
   The ``times`` module exports two similiar types that are both used to
   represent some amount of time: `Duration <#Duration>`_ and
   `TimeInterval <#TimeInterval>`_.
@@ -137,7 +138,7 @@
   needed).
 
   Duration
-  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ----------------------------
   A ``Duration`` represents a duration of time stored as seconds and
   nanoseconds. A ``Duration`` is always fully normalized, so
 ``initDuration(hours = 1)`` and ``initDuration(minutes = 60)`` are equivilant.
@@ -147,7 +148,7 @@
   is more performant and easier to understand it should generally prefered.
 
   TimeInterval
-  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ----------------------------
   A ``TimeInterval`` represents some amount of time expressed in calendar
   units, for example "1 year and 2 days". Since some units cannot be
   normalized (the length of a year is different for leap years for example),
@@ -164,7 +165,7 @@
   ``Duration`` doesn't have.
 
   How long is a day?
-  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ----------------------------
   It should be especially noted that the handling of days differs between
   ``TimeInterval`` and ``Duration``. The ``Duration`` type always treats a day
   as exactly 86400 seconds. For ``TimeInterval``, it's more complex.
@@ -2651,7 +2652,7 @@ proc microseconds*(dur: Duration): int {.inline, deprecated.} =
     doAssert dur.microseconds == 7008
   result = convert(Nanoseconds, Microseconds, dur.nanosecond)
 
-proc nanoseconds*(dur: Duration): NanosecondRange {.inline.} =
+proc nanoseconds*(dur: Duration): NanosecondRange {.inline, deprecated.} =
   ## Number of whole microseconds represented by the **fractional**
   ## part of the duration.
   ##
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index ce147cccc..60b20c5b8 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -57,8 +57,8 @@
 ##   # Run suites starting with 'bug #' and standalone tests starting with '#'
 ##   nim c -r test 'bug #*::' '::#*'
 ##
-## Example
-## -------
+## Examples
+## ========
 ##
 ## .. code:: nim
 ##
@@ -177,13 +177,13 @@ proc addOutputFormatter*(formatter: OutputFormatter) =
   formatters.add(formatter)
 
 proc newConsoleOutputFormatter*(outputLevel: OutputLevel = PRINT_ALL,
-                                colorOutput = true): ConsoleOutputFormatter =
+                                colorOutput = true): <//>ConsoleOutputFormatter =
   ConsoleOutputFormatter(
     outputLevel: outputLevel,
     colorOutput: colorOutput
   )
 
-proc defaultConsoleFormatter*(): ConsoleOutputFormatter =
+proc defaultConsoleFormatter*(): <//>ConsoleOutputFormatter =
   when declared(stdout):
     # Reading settings
     # On a terminal this branch is executed
@@ -263,7 +263,7 @@ proc xmlEscape(s: string): string =
       else:
         result.add(c)
 
-proc newJUnitOutputFormatter*(stream: Stream): JUnitOutputFormatter =
+proc newJUnitOutputFormatter*(stream: Stream): <//>JUnitOutputFormatter =
   ## Creates a formatter that writes report to the specified stream in
   ## JUnit format.
   ## The ``stream`` is NOT closed automatically when the test are finished,
diff --git a/lib/system.nim b/lib/system.nim
index a04433530..bf2e156b4 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1619,7 +1619,7 @@ template `isnot`*(x, y: untyped): untyped = not (x is y)
   ##   assert @[1, 2] isnot enum
 
 when defined(nimV2) and not defined(nimscript):
-  type owned*{.magic: "BuiltinType".}[T]
+  type owned*{.magic: "BuiltinType".}[T] ## type constructor to mark a ref/ptr or a closure as `owned`.
 
   proc new*[T](a: var owned(ref T)) {.magic: "New", noSideEffect.}
     ## Creates a new object of type ``T`` and returns a safe (traced)
@@ -1637,8 +1637,16 @@ when defined(nimV2) and not defined(nimscript):
       var r: owned(ref t)
     new(r)
     return r
+
+  proc unown*[T](x: T): T {.magic: "Unown", noSideEffect.}
+    ## Use the expression ``x`` ignoring its ownership attribute.
+
+  # This is only required to make 0.20 compile with the 0.19 line.
+  template `<//>`*(t: untyped): untyped = owned(t)
+
 else:
   template owned*(t: typeDesc): typedesc = t
+  template unown*(x: typed): typed = x
 
   proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
     ## Creates a new object of type ``T`` and returns a safe (traced)
@@ -1657,6 +1665,9 @@ else:
     new(r)
     return r
 
+  # This is only required to make 0.20 compile with the 0.19 line.
+  template `<//>`*(t: untyped): untyped = t
+
 template disarm*(x: typed) =
   ## Useful for ``disarming`` dangling pointers explicitly for the
   ## --newruntime. Regardless of whether --newruntime is used or not
@@ -1926,13 +1937,11 @@ const
     ## failure.
 
 when defined(nodejs) and not defined(nimscript):
-  var programResult* {.importc: "process.exitCode".}: int
+  var programResult* {.importc: "process.exitCode", deprecated.}: int
   programResult = 0
-else:
-  var programResult* {.compilerproc, exportc: "nim_program_result".}: int
-    ## Modify this variable to specify the exit code of the program
-    ## under normal circumstances. When the program is terminated
-    ## prematurely using `quit proc <#quit,int>`_, this value is ignored.
+elif hostOS != "standalone":
+  var programResult* {.compilerproc, exportc: "nim_program_result", deprecated.}: int
+    ## deprecated, prefer ``quit``
 
 when defined(nimdoc):
   proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.}
@@ -1994,18 +2003,7 @@ when not defined(JS) and not defined(nimscript) and hostOS != "standalone":
 when not defined(JS) and not defined(nimscript) and hasAlloc and not defined(gcDestructors):
   proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.}
 
-when defined(gcDestructors):
-  proc add*[T](x: var seq[T], y: sink T) {.magic: "AppendSeqElem", noSideEffect.} =
-    ## Generic proc for adding a data item `y` to a container `x`.
-    ##
-    ## For containers that have an order, `add` means *append*. New generic
-    ## containers should also call their adding proc `add` for consistency.
-    ## Generic code becomes much easier to write if the Nim naming scheme is
-    ## respected.
-    let xl = x.len
-    setLen(x, xl + 1)
-    x[xl] = y
-else:
+when not defined(gcDestructors):
   proc add*[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
     ## Generic proc for adding a data item `y` to a container `x`.
     ##
@@ -2239,14 +2237,15 @@ proc addQuitProc*(quitProc: proc() {.noconv.}) {.
 # not be called explicitly! The user may decide to do this manually though.
 
 when not defined(nimscript) and not defined(JS):
-  proc zeroMem*(p: pointer, size: Natural) {.inline, benign.}
+  proc zeroMem*(p: pointer, size: Natural) {.inline, noSideEffect,
+    tags: [], locks: 0, raises: [].}
     ## Overwrites the contents of the memory at ``p`` with the value 0.
     ##
     ## Exactly ``size`` bytes will be overwritten. Like any procedure
     ## dealing with raw memory this is **unsafe**.
 
   proc copyMem*(dest, source: pointer, size: Natural) {.inline, benign,
-    tags: [], locks: 0.}
+    tags: [], locks: 0, raises: [].}
     ## Copies the contents from the memory at ``source`` to the memory
     ## at ``dest``.
     ## Exactly ``size`` bytes will be copied. The memory
@@ -2254,7 +2253,7 @@ when not defined(nimscript) and not defined(JS):
     ## memory this is **unsafe**.
 
   proc moveMem*(dest, source: pointer, size: Natural) {.inline, benign,
-    tags: [], locks: 0.}
+    tags: [], locks: 0, raises: [].}
     ## Copies the contents from the memory at ``source`` to the memory
     ## at ``dest``.
     ##
@@ -2263,7 +2262,8 @@ when not defined(nimscript) and not defined(JS):
     ## and is thus somewhat more safe than ``copyMem``. Like any procedure
     ## dealing with raw memory this is still **unsafe**, though.
 
-  proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect, tags: [], locks: 0.}
+  proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect,
+    tags: [], locks: 0, raises: [].}
     ## Compares the memory blocks ``a`` and ``b``. ``size`` bytes will
     ## be compared.
     ##
diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim
index 23828af91..99f66961d 100644
--- a/lib/system/ansi_c.nim
+++ b/lib/system/ansi_c.nim
@@ -148,4 +148,4 @@ proc rawWrite*(f: CFilePtr, s: cstring) {.compilerproc, nonreloadable, inline.}
   # we cannot throw an exception here!
   discard c_fwrite(s, 1, s.len, f)
 
-{.pop}
+{.pop.}
diff --git a/lib/system/fatal.nim b/lib/system/fatal.nim
index 100b6d482..82704a2e7 100644
--- a/lib/system/fatal.nim
+++ b/lib/system/fatal.nim
@@ -10,6 +10,8 @@ when hostOS == "standalone":
     panic(arg)
 
 elif defined(nimQuirky) and not defined(nimscript):
+  import ansi_c
+
   proc name(t: typedesc): string {.magic: "TypeTrait".}
 
   proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noReturn.} =
diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim
index 727c08da3..4aa1b705f 100644
--- a/lib/system/strmantle.nim
+++ b/lib/system/strmantle.nim
@@ -147,58 +147,58 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
     has_sign = false
 
   # Sign?
-  if s[i] == '+' or s[i] == '-':
+  if i < s.len and (s[i] == '+' or s[i] == '-'):
     has_sign = true
     if s[i] == '-':
       sign = -1.0
     inc(i)
 
   # NaN?
-  if s[i] == 'N' or s[i] == 'n':
+  if i+2 < s.len and (s[i] == 'N' or s[i] == 'n'):
     if s[i+1] == 'A' or s[i+1] == 'a':
       if s[i+2] == 'N' or s[i+2] == 'n':
-        if s[i+3] notin IdentChars:
+        if i+3 >= s.len or s[i+3] notin IdentChars:
           number = NaN
           return i+3 - start
     return 0
 
   # Inf?
-  if s[i] == 'I' or s[i] == 'i':
+  if i+2 < s.len and (s[i] == 'I' or s[i] == 'i'):
     if s[i+1] == 'N' or s[i+1] == 'n':
       if s[i+2] == 'F' or s[i+2] == 'f':
-        if s[i+3] notin IdentChars:
+        if i+3 >= s.len or s[i+3] notin IdentChars:
           number = Inf*sign
           return i+3 - start
     return 0
 
-  if s[i] in {'0'..'9'}:
+  if i < s.len and s[i] in {'0'..'9'}:
     first_digit = (s[i].ord - '0'.ord)
   # Integer part?
-  while s[i] in {'0'..'9'}:
+  while i < s.len and s[i] in {'0'..'9'}:
     inc(kdigits)
     integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
     inc(i)
-    while s[i] == '_': inc(i)
+    while i < s.len and s[i] == '_': inc(i)
 
   # Fractional part?
-  if s[i] == '.':
+  if i < s.len and s[i] == '.':
     inc(i)
     # if no integer part, Skip leading zeros
     if kdigits <= 0:
-      while s[i] == '0':
+      while i < s.len and s[i] == '0':
         inc(frac_exponent)
         inc(i)
-        while s[i] == '_': inc(i)
+        while i < s.len and s[i] == '_': inc(i)
 
-    if first_digit == -1 and s[i] in {'0'..'9'}:
+    if first_digit == -1 and i < s.len and s[i] in {'0'..'9'}:
       first_digit = (s[i].ord - '0'.ord)
     # get fractional part
-    while s[i] in {'0'..'9'}:
+    while i < s.len and s[i] in {'0'..'9'}:
       inc(fdigits)
       inc(frac_exponent)
       integer = integer * 10'u64 + (s[i].ord - '0'.ord).uint64
       inc(i)
-      while s[i] == '_': inc(i)
+      while i < s.len and s[i] == '_': inc(i)
 
   # if has no digits: return error
   if kdigits + fdigits <= 0 and
@@ -206,7 +206,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
      (i == start + 1 and has_sign)): # or only '+' or '-
     return 0
 
-  if s[i] in {'e', 'E'}:
+  if i+1 < s.len and s[i] in {'e', 'E'}:
     inc(i)
     if s[i] == '+' or s[i] == '-':
       if s[i] == '-':
@@ -215,10 +215,10 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
       inc(i)
     if s[i] notin {'0'..'9'}:
       return 0
-    while s[i] in {'0'..'9'}:
+    while i < s.len and s[i] in {'0'..'9'}:
       exponent = exponent * 10 + (ord(s[i]) - ord('0'))
       inc(i)
-      while s[i] == '_': inc(i) # underscores are allowed and ignored
+      while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
 
   var real_exponent = exp_sign*exponent - frac_exponent
   let exp_negative = real_exponent < 0
@@ -259,12 +259,12 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
   result = i - start
   i = start
   # re-parse without error checking, any error should be handled by the code above.
-  if s[i] == '.': i.inc
-  while s[i] in {'0'..'9','+','-'}:
+  if i < s.len and s[i] == '.': i.inc
+  while i < s.len and s[i] in {'0'..'9','+','-'}:
     if ti < maxlen:
       t[ti] = s[i]; inc(ti)
     inc(i)
-    while s[i] in {'.', '_'}: # skip underscore and decimal point
+    while i < s.len and s[i] in {'.', '_'}: # skip underscore and decimal point
       inc(i)
 
   # insert exponent
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index 18ceb900b..2c2fd4325 100644
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -19,7 +19,8 @@
 ## to prevent race conditions and improves efficiency. See `the manual for
 ## details of this memory model <manual.html#threads>`_.
 ##
-## Example:
+## Examples
+## ========
 ##
 ## .. code-block:: Nim
 ##
@@ -328,7 +329,7 @@ else:
 proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) =
   createThread[void](t, tp)
 
-## we need to cache current threadId to not perform syscall all the time
+# we need to cache current threadId to not perform syscall all the time
 var threadId {.threadvar.}: int
 
 when defined(windows):
diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html
index c562e731e..504b64a69 100644
--- a/nimdoc/testproject/expected/testproject.html
+++ b/nimdoc/testproject/expected/testproject.html
@@ -878,7 +878,9 @@ function main() {
   <span class="Identifier">subdir</span> <span class="Operator">/</span> <span class="Identifier">subdir_b</span> <span class="Operator">/</span> <span class="Identifier">utils</span>
 
 <span class="Identifier">doAssert</span> <span class="Identifier">bar</span><span class="Other">(</span><span class="DecNumber">3</span><span class="Other">,</span> <span class="DecNumber">4</span><span class="Other">)</span> <span class="Operator">==</span> <span class="DecNumber">7</span>
-<span class="Identifier">foo</span><span class="Other">(</span><span class="Identifier">enumValueA</span><span class="Other">,</span> <span class="Identifier">enumValueB</span><span class="Other">)</span></pre></p>
+<span class="Identifier">foo</span><span class="Other">(</span><span class="Identifier">enumValueA</span><span class="Other">,</span> <span class="Identifier">enumValueB</span><span class="Other">)</span>
+<span class="Keyword">for</span> <span class="Identifier">x</span> <span class="Keyword">in</span> <span class="StringLit">&quot;xx&quot;</span><span class="Other">:</span>
+  <span class="Keyword">discard</span></pre></p>
   <div class="section" id="6">
 <h1><a class="toc-backref" href="#6">Imports</a></h1>
 <dl class="item">
diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim
index d6fac4751..32bf286e8 100644
--- a/nimdoc/testproject/testproject.nim
+++ b/nimdoc/testproject/testproject.nim
@@ -6,6 +6,8 @@ runnableExamples:
   import subdir / subdir_b / utils
   doAssert bar(3, 4) == 7
   foo(enumValueA, enumValueB)
+  # bug #11078
+  for x in "xx": discard
 
 
 template foo*(a, b: SomeType) =
diff --git a/testament/categories.nim b/testament/categories.nim
index add080020..a2a0c5d5b 100644
--- a/testament/categories.nim
+++ b/testament/categories.nim
@@ -11,6 +11,7 @@
 ## of the compiler.
 
 import important_packages
+import sequtils
 
 const
   specialCategories = [
@@ -514,30 +515,21 @@ proc testNimblePackages(r: var TResults, cat: Category) =
       let buildPath = packagesDir / name
       if not existsDir(buildPath):
         if hasDep:
-          let nimbleProcess = startProcess("nimble", "", ["install", "-y", name],
-                                           options = {poUsePath, poStdErrToStdOut})
-          let nimbleStatus = waitForExitEx(nimbleProcess)
-          nimbleProcess.close
+          let (nimbleOutput, nimbleStatus) = execCmdEx2("nimble", ["install", "-y", name])
           if nimbleStatus != QuitSuccess:
-            r.addResult(test, targetC, "", "'nimble install' failed", reInstallFailed)
+            r.addResult(test, targetC, "", "'nimble install' failed\n" & nimbleOutput, reInstallFailed)
             continue
 
-        let installProcess = startProcess("git", "", ["clone", url, buildPath],
-                                          options = {poUsePath, poStdErrToStdOut})
-        let installStatus = waitForExitEx(installProcess)
-        installProcess.close
+        let (installOutput, installStatus) = execCmdEx2("git", ["clone", url, buildPath])
         if installStatus != QuitSuccess:
-          r.addResult(test, targetC, "", "'git clone' failed", reInstallFailed)
+          r.addResult(test, targetC, "", "'git clone' failed\n" & installOutput, reInstallFailed)
           continue
 
       let cmdArgs = parseCmdLine(cmd)
-      let buildProcess = startProcess(cmdArgs[0], buildPath, cmdArgs[1..^1],
-                                      options = {poUsePath, poStdErrToStdOut})
-      let buildStatus = waitForExitEx(buildProcess)
-      buildProcess.close
 
+      let (buildOutput, buildStatus) = execCmdEx2(cmdArgs[0], cmdArgs[1..^1], workingDir=buildPath)
       if buildStatus != QuitSuccess:
-        r.addResult(test, targetC, "", "package test failed", reBuildFailed)
+        r.addResult(test, targetC, "", "package test failed\n" & buildOutput, reBuildFailed)
       else:
         inc r.passed
         r.addResult(test, targetC, "", "", reSuccess)
@@ -653,8 +645,7 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) =
 
   let args = ["c", "--nimCache:" & outDir, "-d:testing", "--listCmd", "megatest.nim"]
   proc onStdout(line: string) = echo line
-  var (buf, exitCode) = execCmdEx2(command = compilerPrefix, args = args, options = {poStdErrToStdOut, poUsePath}, input = "",
-    onStdout = if verboseMegatest: onStdout else: nil)
+  var (buf, exitCode) = execCmdEx2(command = compilerPrefix, args = args, input = "")
   if exitCode != 0:
     echo buf.string
     quit("megatest compilation failed")
@@ -688,8 +679,22 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) =
 
 # ---------------------------------------------------------------------------
 
-proc processCategory(r: var TResults, cat: Category, options, testsDir: string,
+proc loadSkipFrom(name: string): seq[string] =
+  # One skip per line, comments start with #
+  # used by `nlvm` (at least)
+  try:
+    for line in lines(name):
+      let sline = line.strip()
+      if sline.len > 0 and not sline.startsWith("#"):
+        result.add sline
+  except:
+    echo "Could not load " & name & ", ignoring"
+
+proc processCategory(r: var TResults, cat: Category,
+                     options, testsDir, skipFrom: string,
                      runJoinableTests: bool) =
+  let skips = loadSkipFrom(skipFrom)
+
   case cat.string.normalize
   of "rodfiles":
     when false:
@@ -745,6 +750,9 @@ proc processCategory(r: var TResults, cat: Category, options, testsDir: string,
 
     for i, name in files:
       var test = makeTest(name, options, cat)
+      if skips.anyIt(it in name):
+        test.spec.err = reDisabled
+
       if runJoinableTests or not isJoinableSpec(test.spec) or cat.string in specialCategories:
         discard "run the test"
       else:
diff --git a/testament/tester.nim b/testament/tester.nim
index 1df08aa34..b73190eba 100644
--- a/testament/tester.nim
+++ b/testament/tester.nim
@@ -19,8 +19,6 @@ include compiler/nodejs
 var useColors = true
 var backendLogging = true
 var simulate = false
-var verboseMegatest = false # very verbose but can be useful
-var verboseCommands = false
 
 const
   testsDir = "tests" & DirSep
@@ -39,8 +37,6 @@ Arguments:
   arguments are passed to the compiler
 Options:
   --print                   also print results to the console
-  --verboseMegatest         log to stdout megatetest compilation
-  --verboseCommands         log to stdout info about commands being run
   --simulate                see what tests would be run but don't run them (for debugging)
   --failing                 only show failing/ignored tests
   --targets:"c c++ js objc" run tests for specified targets (default: all)
@@ -48,6 +44,7 @@ Options:
   --directory:dir           Change to directory dir before reading the tests or doing anything else.
   --colors:on|off           Turn messagescoloring on|off.
   --backendLogging:on|off   Disable or enable backend logging. By default turned on.
+  --skipFrom:file           Read tests to skip from `file` - one test per line, # comments ignored
 """ % resultsFile
 
 type
@@ -91,11 +88,11 @@ proc getFileDir(filename: string): string =
   if not result.isAbsolute():
     result = getCurrentDir() / result
 
-proc execCmdEx2(command: string, args: openarray[string], options: set[ProcessOption], input: string, onStdout: proc(line: string) = nil): tuple[
+proc execCmdEx2(command: string, args: openarray[string]; workingDir, input: string = ""): tuple[
                 output: TaintedString,
                 exitCode: int] {.tags:
                 [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} =
-  var p = startProcess(command, args=args, options=options)
+  var p = startProcess(command, workingDir=workingDir, args=args, options={poStdErrToStdOut, poUsePath})
   var outp = outputStream(p)
 
   # There is no way to provide input for the child process
@@ -111,20 +108,11 @@ proc execCmdEx2(command: string, args: openarray[string], options: set[ProcessOp
     if outp.readLine(line):
       result.output.string.add(line.string)
       result.output.string.add("\n")
-      if onStdout != nil: onStdout(line.string)
     else:
       result.exitCode = peekExitCode(p)
       if result.exitCode != -1: break
   close(p)
 
-  if verboseCommands:
-    var command2 = command
-    if args.len > 0: command2.add " " & args.quoteShellCommand
-    echo (msg: "execCmdEx2",
-      command: command2,
-      options: options,
-      exitCode: result.exitCode)
-
 proc nimcacheDir(filename, options: string, target: TTarget): string =
   ## Give each test a private nimcache dir so they don't clobber each other's.
   let hashInput = options & $target
@@ -266,17 +254,25 @@ proc addResult(r: var TResults, test: TTest, target: TTarget,
     maybeStyledEcho styleBright, fgRed, "FAIL: ", fgCyan, name
     maybeStyledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\""
     maybeStyledEcho styleBright, fgRed, "Failure: ", $success
-    maybeStyledEcho fgYellow, "Expected:"
-    maybeStyledEcho styleBright, expected, "\n"
-    maybeStyledEcho fgYellow, "Gotten:"
-    maybeStyledEcho styleBright, given, "\n"
+    if success in {reBuildFailed, reNimcCrash, reInstallFailed}:
+      # expected is empty, no reason to print it.
+      echo given
+    else:
+      maybeStyledEcho fgYellow, "Expected:"
+      maybeStyledEcho styleBright, expected, "\n"
+      maybeStyledEcho fgYellow, "Gotten:"
+      maybeStyledEcho styleBright, given, "\n"
+
 
   if backendLogging and existsEnv("APPVEYOR"):
     let (outcome, msg) =
-      if success == reSuccess:
+      case success
+      of reSuccess:
         ("Passed", "")
-      elif success in {reDisabled, reJoined}:
+      of reDisabled, reJoined:
         ("Skipped", "")
+      of reBuildFailed, reNimcCrash, reInstallFailed:
+        ("Failed", "Failure: " & $success & "\n" & given)
       else:
         ("Failed", "Failure: " & $success & "\nExpected:\n" & expected & "\n\n" & "Gotten:\n" & given)
     var p = startProcess("appveyor", args=["AddTest", test.name.replace("\\", "/") & test.options,
@@ -320,7 +316,7 @@ proc generatedFile(test: TTest, target: TTarget): string =
     let (_, name, _) = test.name.splitFile
     let ext = targetToExt[target]
     result = nimcacheDir(test.name, test.options, target) /
-             ("compiler_" & name.changeFileExt(ext))
+              name.replace("_", "__").changeFileExt(ext)
 
 proc needsCodegenCheck(spec: TSpec): bool =
   result = spec.maxCodeSize > 0 or spec.ccodeCheck.len > 0
@@ -424,7 +420,7 @@ proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) =
       # nested conditionals - the empty rows in between to clarify the "danger"
       var given = callCompiler(expected.getCmd, test.name, test.options, target)
       if given.err != reSuccess:
-        r.addResult(test, target, "", given.msg, given.err)
+        r.addResult(test, target, "", given.nimout, given.err)
         continue
       let isJsTarget = target == targetJS
       var exeFile = changeFileExt(test.name, if isJsTarget: "js" else: ExeExt)
@@ -445,7 +441,7 @@ proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) =
         args = concat(@[exeFile], args)
       else:
         exeCmd = exeFile
-      var (buf, exitCode) = execCmdEx2(exeCmd, args, options = {poStdErrToStdOut}, input = expected.input)
+      var (buf, exitCode) = execCmdEx2(exeCmd, args, input = expected.input)
       # Treat all failure codes from nodejs as 1. Older versions of nodejs used
       # to return other codes, but for us it is sufficient to know that it's not 0.
       if exitCode != 0: exitCode = 1
@@ -514,6 +510,8 @@ const disabledFilesDefault = @[
   "LockFreeHash.nim",
   "sharedstrings.nim",
   "tableimpl.nim",
+  "setimpl.nim",
+  "hashcommon.nim",
 
   # Error: undeclared identifier: 'hasThreadSupport'
   "ioselectors_epoll.nim",
@@ -545,14 +543,13 @@ proc main() =
   var optFailing = false
   var targetsStr = ""
   var isMainProcess = true
+  var skipFrom = ""
 
   var p = initOptParser()
   p.next()
   while p.kind == cmdLongoption:
     case p.key.string.normalize
     of "print", "verbose": optPrintResults = true
-    of "verbosemegatest": verboseMegatest = true
-    of "verbosecommands": verboseCommands = true
     of "failing": optFailing = true
     of "pedantic": discard "now always enabled"
     of "targets":
@@ -580,6 +577,8 @@ proc main() =
         backendLogging = false
       else:
         quit Usage
+    of "skipfrom":
+      skipFrom = p.val.string
     else:
       quit Usage
     p.next()
@@ -598,6 +597,9 @@ proc main() =
 
     myself &= " " & quoteShell("--nim:" & compilerPrefix)
 
+    if skipFrom.len > 0:
+      myself &= " " & quoteShell("--skipFrom:" & skipFrom)
+
     var cats: seq[string]
     let rest = if p.cmdLineRest.string.len > 0: " " & p.cmdLineRest.string else: ""
     for kind, dir in walkDir(testsDir):
@@ -617,13 +619,13 @@ proc main() =
     if simulate:
       for i, cati in cats:
         progressStatus(i)
-        processCategory(r, Category(cati), p.cmdLineRest.string, testsDir, runJoinableTests = false)
+        processCategory(r, Category(cati), p.cmdLineRest.string, testsDir, skipFrom, runJoinableTests = false)
     else:
       quit osproc.execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams}, beforeRunEvent = progressStatus)
   of "c", "cat", "category":
     var cat = Category(p.key)
     p.next
-    processCategory(r, cat, p.cmdLineRest.string, testsDir, runJoinableTests = true)
+    processCategory(r, cat, p.cmdLineRest.string, testsDir, skipFrom, runJoinableTests = true)
   of "pcat":
     # 'pcat' is used for running a category in parallel. Currently the only
     # difference is that we don't want to run joinable tests here as they
@@ -631,7 +633,7 @@ proc main() =
     isMainProcess = false
     var cat = Category(p.key)
     p.next
-    processCategory(r, cat, p.cmdLineRest.string, testsDir, runJoinableTests = false)
+    processCategory(r, cat, p.cmdLineRest.string, testsDir, skipFrom, runJoinableTests = false)
   of "r", "run":
     # at least one directory is required in the path, to use as a category name
     let pathParts = split(p.key.string, {DirSep, AltSep})
diff --git a/tests/ccgbugs/t5701.nim b/tests/ccgbugs/t5701.nim
index ee6e48498..19d64a230 100644
--- a/tests/ccgbugs/t5701.nim
+++ b/tests/ccgbugs/t5701.nim
@@ -2,6 +2,7 @@ discard """
   output: '''(1, 1)
 (2, 2)
 (3, 3)
+@[1, 2, 3, 4]
 '''
 """
 
@@ -15,3 +16,12 @@ proc foo(args: varargs[int]) =
     discard
 
 foo(1,2,3)
+
+# 10999
+
+proc varargsToSeq(vals: varargs[int32]): seq[int32] =
+  result = newSeqOfCap[int32](vals.len)
+  for v in vals:
+    result.add v
+
+echo varargsToSeq(1, 2, 3, 4)
diff --git a/tests/destructor/tdestructor.nim b/tests/destructor/tdestructor.nim
index 09dce19ab..780a45288 100644
--- a/tests/destructor/tdestructor.nim
+++ b/tests/destructor/tdestructor.nim
@@ -7,8 +7,8 @@ mygeneric1 constructed
 mygeneric1 destroyed
 ----
 mygeneric2 constructed
-myobj destroyed
 mygeneric2 destroyed
+myobj destroyed
 ----
 mygeneric3 constructed
 mygeneric1 destroyed
@@ -20,10 +20,10 @@ mygeneric2 destroyed
 ----
 ----
 myobj destroyed
-mygeneric1 destroyed
 myobj destroyed
 myobj destroyed
 myobj destroyed
+mygeneric1 destroyed
 ---
 myobj destroyed
 myobj destroyed
diff --git a/tests/destructor/tdestructor3.nim b/tests/destructor/tdestructor3.nim
index 9c41abe81..a1de284ae 100644
--- a/tests/destructor/tdestructor3.nim
+++ b/tests/destructor/tdestructor3.nim
@@ -4,8 +4,8 @@ destroy
 destroy
 5
 123
-destroy Foo: 5
-destroy Foo: 123'''
+destroy Foo: 123
+destroy Foo: 5'''
 joinable: false
 """
 
diff --git a/tests/destructor/tdont_return_unowned_from_owned.nim b/tests/destructor/tdont_return_unowned_from_owned.nim
new file mode 100644
index 000000000..5794dec1d
--- /dev/null
+++ b/tests/destructor/tdont_return_unowned_from_owned.nim
@@ -0,0 +1,43 @@
+discard """
+  cmd: "nim check --newruntime --hints:off $file"
+  nimout: '''tdont_return_unowned_from_owned.nim(24, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
+tdont_return_unowned_from_owned.nim(27, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
+tdont_return_unowned_from_owned.nim(30, 6) Error: type mismatch: got <Obj>
+but expected one of:
+proc new[T](a: var ref T; finalizer: proc (x: ref T) {.nimcall.})
+2 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them
+
+expression: new(result)
+tdont_return_unowned_from_owned.nim(30, 6) Error: illformed AST:
+tdont_return_unowned_from_owned.nim(38, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
+tdont_return_unowned_from_owned.nim(39, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
+tdont_return_unowned_from_owned.nim(43, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type
+'''
+  errormsg: "cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type"
+  line: 43
+"""
+# bug #11073
+type
+  Obj = ref object
+
+proc newObjA(): Obj =
+  result = new Obj
+
+proc newObjB(): Obj =
+  result = Obj()
+
+proc newObjC(): Obj =
+  new(result)
+
+let a = newObjA()
+let b = newObjB()
+let c = newObjC()
+
+proc testA(result: var (RootRef, RootRef)) =
+  let r: owned RootRef = RootRef()
+  result[0] = r
+  result[1] = RootRef()
+
+proc testB(): RootRef =
+  let r: owned RootRef = RootRef()
+  result = r
diff --git a/tests/destructor/tgcdestructors.nim b/tests/destructor/tgcdestructors.nim
index 620ba7d66..bc9f57d20 100644
--- a/tests/destructor/tgcdestructors.nim
+++ b/tests/destructor/tgcdestructors.nim
@@ -4,7 +4,9 @@ discard """
 ho
 ha
 @["arg", "asdfklasdfkl", "asdkfj", "dfasj", "klfjl"]
-22 22'''
+@[1, 2, 3]
+@["red", "yellow", "orange", "rtrt1", "pink"]
+30 30'''
 """
 
 import allocators
@@ -132,6 +134,41 @@ proc other =
 
 other()
 
+# bug #11050
+
+type
+  Obj* = object
+    f*: seq[int]
+
+method main(o: Obj) {.base.} =
+  for newb in o.f:
+    discard
+
+# test that o.f was not moved!
+proc testforNoMove =
+  var o = Obj(f: @[1, 2, 3])
+  main(o)
+  echo o.f
+
+testforNoMove()
+
+# bug #11065
+type
+  Warm = seq[string]
+
+proc testWarm =
+  var w: Warm
+  w = @["red", "yellow", "orange"]
+
+  var x = "rt"
+  var y = "rt1"
+  w.add(x & y)
+
+  w.add("pink")
+  echo w
+
+testWarm()
+
 #echo s
 let (a, d) = allocCounters()
 discard cprintf("%ld %ld\n", a, d)
diff --git a/tests/destructor/tobjfield_analysis.nim b/tests/destructor/tobjfield_analysis.nim
index 24cf02aee..83f394c3b 100644
--- a/tests/destructor/tobjfield_analysis.nim
+++ b/tests/destructor/tobjfield_analysis.nim
@@ -2,30 +2,46 @@ discard """
   output: '''works'''
 """
 
+#  bug #11095
+
 type
-  MyVal = object
-    f: ptr float
+  MyVal[T] = object
+    f: ptr T
 
-proc `=destroy`(x: var MyVal) =
+proc `=destroy`[T](x: var MyVal[T]) =
   if x.f != nil:
     dealloc(x.f)
 
-proc `=sink`(x1: var MyVal, x2: Myval) =
+proc `=sink`[T](x1: var MyVal[T], x2: MyVal[T]) =
   if x1.f != x2.f:
     `=destroy`(x1)
     x1.f = x2.f
 
-proc `=`(x1: var MyVal, x2: Myval) {.error.}
+proc `=`[T](x1: var MyVal[T], x2: MyVal[T]) {.error.}
 
-proc newVal(x: float): MyVal =
-  result.f = create(float)
+proc newVal[T](x: sink T): MyVal[T] =
+  result.f = create(T)
   result.f[] = x
 
-proc sinkMe(x: sink MyVal) =
+proc set[T](x: var MyVal[T], val: T) =
+  x.f[] = val
+
+proc sinkMe[T](x: sink MyVal[T]) =
   discard
 
+var flag = false
+
 proc main =
-  var y = (newVal(3.0), newVal(4.0))
+  var y = case flag
+    of true:
+      var x1 = newVal[float](1.0)
+      var x2 = newVal[float](2.0)
+      (newVal(x1), newVal(x2))
+
+    of false:
+      var x1 = newVal[float](1.0)
+      var x2 = newVal[float](2.0)
+      (newVal(x1), newVal(x2))
 
   sinkMe y[0]
   sinkMe y[1]
diff --git a/tests/destructor/towned_binary_tree.nim b/tests/destructor/towned_binary_tree.nim
new file mode 100644
index 000000000..372b1d3d8
--- /dev/null
+++ b/tests/destructor/towned_binary_tree.nim
@@ -0,0 +1,92 @@
+discard """
+  cmd: '''nim c --newruntime $file'''
+  output: '''331665
+allocs 0'''
+"""
+
+#  bug #11053
+
+import random
+
+type Node = ref object
+  x, y: int32
+  left, right: owned Node
+
+proc newNode(x: int32): owned Node =
+  result = Node(x: x, y: rand(high int32).int32)
+
+proc merge(lower, greater: owned Node): owned Node =
+  if lower.isNil:
+    result = greater
+  elif greater.isNil:
+    result = lower
+  elif lower.y < greater.y:
+    lower.right = merge(lower.right, greater)
+    result = lower
+  else:
+    greater.left = merge(lower, greater.left)
+    result = greater
+
+proc splitBinary(orig: owned Node, value: int32): (owned Node, owned Node) =
+  if orig.isNil:
+    result = (nil, nil)
+  elif orig.x < value:
+    let splitPair = splitBinary(orig.right, value)
+    orig.right = splitPair[0]
+    result = (orig, splitPair[1])
+  else:
+    let splitPair = splitBinary(orig.left, value)
+    orig.left = splitPair[1]
+    result = (splitPair[0], orig)
+
+proc merge3(lower, equal, greater: owned Node): owned Node =
+  merge(merge(lower, equal), greater)
+
+proc split(orig: owned Node, value: int32): tuple[lower, equal, greater: owned Node] =
+  let
+    (lower, equalGreater) = splitBinary(orig, value)
+    (equal, greater) = splitBinary(equalGreater, value + 1)
+  result = (lower, equal, greater)
+
+type Tree = object
+  root: owned Node
+
+proc hasValue(self: var Tree, x: int32): bool =
+  let splited = split(move self.root, x)
+  result = not splited.equal.isNil
+  self.root = merge3(splited.lower, splited.equal, splited.greater)
+
+proc insert(self: var Tree, x: int32) =
+  var splited = split(move self.root, x)
+  if splited.equal.isNil:
+    splited.equal = newNode(x)
+  self.root = merge3(splited.lower, splited.equal, splited.greater)
+
+proc erase(self: var Tree, x: int32) =
+  let splited = split(move self.root, x)
+  self.root = merge(splited.lower, splited.greater)
+
+proc main() =
+  var
+    tree = Tree()
+    cur = 5'i32
+    res = 0
+
+  for i in 1 ..< 1000000:
+    let a = i mod 3
+    cur = (cur * 57 + 43) mod 10007
+    case a:
+    of 0:
+      tree.insert(cur)
+    of 1:
+      tree.erase(cur)
+    of 2:
+      if tree.hasValue(cur):
+        res += 1
+    else:
+      discard
+  echo res
+
+when isMainModule:
+  main()
+  echo "allocs ", allocs
diff --git a/tests/destructor/twidgets.nim b/tests/destructor/twidgets.nim
new file mode 100644
index 000000000..5d533fe44
--- /dev/null
+++ b/tests/destructor/twidgets.nim
@@ -0,0 +1,78 @@
+discard """
+  cmd: '''nim c --newruntime $file'''
+  output: '''button
+clicked!
+1 1  alloc/dealloc pairs: 0'''
+"""
+
+import core / allocators
+import system / ansi_c
+
+type
+  Widget* = ref object of RootObj
+    drawImpl: owned(proc (self: Widget))
+
+  Button* = ref object of Widget
+    caption: string
+    onclick: owned(proc())
+
+  Window* = ref object of Widget
+    elements: seq[owned Widget]
+
+
+proc newButton(caption: string; onclick: owned(proc())): owned Button =
+  proc draw(self: Widget) =
+    let b = Button(self)
+    echo b.caption
+
+  #result = Button(drawImpl: draw, caption: caption, onclick: onclick)
+  new(result)
+  result.drawImpl = draw
+  result.caption = caption
+  result.onclick = onclick
+
+iterator unitems*[T](a: seq[owned T]): T {.inline.} =
+  ## Iterates over each item of `a`.
+  var i = 0
+  let L = len(a)
+  while i < L:
+    yield a[i]
+    inc(i)
+    assert(len(a) == L, "seq modified while iterating over it")
+
+proc newWindow(): owned Window =
+  proc windraw(self: Widget) =
+    let w = Window(self)
+    for i in 0..<len(w.elements):
+      let e = Widget(w.elements[i])
+      let d = (proc(self: Widget))e.drawImpl
+      if not d.isNil: d(e)
+
+  result = Window(drawImpl: windraw, elements: @[])
+
+proc draw(w: Widget) =
+  let d = (proc(self: Widget))w.drawImpl
+  if not d.isNil: d(w)
+
+proc add*(w: Window; elem: owned Widget) =
+  w.elements.add elem
+
+proc main =
+  var w = newWindow()
+
+  var b = newButton("button", nil)
+  let u: Button = b
+  b.onclick = proc () =
+    b.caption = "clicked!"
+  w.add b
+
+  w.draw()
+  # simulate button click:
+  u.onclick()
+
+  w.draw()
+
+main()
+
+let (a, d) = allocCounters()
+discard cprintf("%ld %ld  alloc/dealloc pairs: %ld\n", a, d, allocs)
diff --git a/tests/destructor/twidgets_unown.nim b/tests/destructor/twidgets_unown.nim
new file mode 100644
index 000000000..bd4cd76af
--- /dev/null
+++ b/tests/destructor/twidgets_unown.nim
@@ -0,0 +1,68 @@
+discard """
+  cmd: '''nim c --newruntime $file'''
+  output: '''button
+clicked!
+3 3  alloc/dealloc pairs: 0'''
+"""
+
+import core / allocators
+import system / ansi_c
+
+type
+  Widget* = ref object of RootObj
+    drawImpl: owned(proc (self: Widget))
+
+  Button* = ref object of Widget
+    caption: string
+    onclick: owned(proc())
+
+  Window* = ref object of Widget
+    elements: seq[owned Widget]
+
+
+proc newButton(caption: string; onclick: owned(proc())): owned Button =
+  proc draw(self: Widget) =
+    let b = Button(self)
+    echo b.caption
+
+  #result = Button(drawImpl: draw, caption: caption, onclick: onclick)
+  new(result)
+  result.drawImpl = draw
+  result.caption = caption
+  result.onclick = onclick
+
+proc newWindow(): owned Window =
+  proc windraw(self: Widget) =
+    let w = Window(self)
+    for e in unown(w.elements):
+      let d = unown e.drawImpl
+      if not d.isNil: d(e)
+
+  result = Window(drawImpl: windraw, elements: @[])
+
+proc draw(w: Widget) =
+  let d = unown w.drawImpl
+  if not d.isNil: d(w)
+
+proc add*(w: Window; elem: owned Widget) =
+  w.elements.add elem
+
+proc main =
+  var w = newWindow()
+
+  var b = newButton("button", nil)
+  let u = unown b
+  b.onclick = proc () =
+    b.caption = "clicked!"
+  w.add b
+
+  w.draw()
+  # simulate button click:
+  u.onclick()
+
+  w.draw()
+
+main()
+
+let (a, d) = allocCounters()
+discard cprintf("%ld %ld  alloc/dealloc pairs: %ld\n", a, d, allocs)
diff --git a/tests/discard/tdiscardable.nim b/tests/discard/tdiscardable.nim
index 81e17866a..032050139 100644
--- a/tests/discard/tdiscardable.nim
+++ b/tests/discard/tdiscardable.nim
@@ -3,6 +3,8 @@ output: '''
 tdiscardable
 1
 1
+something defered
+something defered
 '''
 """
 
@@ -45,3 +47,21 @@ proc foo: (proc: int) =
   return bar
 
 discard foo()
+
+# bug #10842
+
+proc myDiscardable(): int {.discardable.} =
+  discard
+
+proc main1() =
+  defer:
+    echo "something defered"
+  discard myDiscardable()
+
+proc main2() =
+  defer:
+    echo "something defered"
+  myDiscardable()
+
+main1()
+main2()
diff --git a/tests/macros/tnewproc.nim b/tests/macros/tnewproc.nim
new file mode 100644
index 000000000..a5bfd6dca
--- /dev/null
+++ b/tests/macros/tnewproc.nim
@@ -0,0 +1,51 @@
+import macros
+
+macro test(a: untyped): untyped =
+  # proc hello*(x: int = 3, y: float32): int {.inline.} = discard
+  let
+    nameNode = nnkPostfix.newTree(
+      newIdentNode("*"),
+      newIdentNode("hello")
+    )
+    params = @[
+      newIdentNode("int"),
+      nnkIdentDefs.newTree(
+        newIdentNode("x"),
+        newIdentNode("int"),
+        newLit(3)
+      ),
+      nnkIdentDefs.newTree(
+        newIdentNode("y"),
+        newIdentNode("float32"),
+        newEmptyNode()
+      )
+    ]
+    paramsNode = nnkFormalParams.newTree(params)
+    pragmasNode = nnkPragma.newTree(
+      newIdentNode("inline")
+    )
+    bodyNode = nnkStmtList.newTree(
+      nnkDiscardStmt.newTree(
+        newEmptyNode()
+      )
+    )
+
+  var
+    expected = nnkProcDef.newTree(
+      nameNode,
+      newEmptyNode(),
+      newEmptyNode(),
+      paramsNode,
+      pragmasNode,
+      newEmptyNode(),
+      bodyNode
+    )
+
+  doAssert expected == newProc(name=nameNode, params=params,
+                                    body = bodyNode, pragmas=pragmasNode)
+  expected.pragma = newEmptyNode()
+  doAssert expected == newProc(name=nameNode, params=params,
+                                    body = bodyNode)
+
+test:
+  42
diff --git a/tests/modules/mnotuniquename.nim b/tests/modules/mnotuniquename.nim
index e69de29bb..54d5883cd 100644
--- a/tests/modules/mnotuniquename.nim
+++ b/tests/modules/mnotuniquename.nim
@@ -0,0 +1 @@
+proc flat*() = echo "flat"
diff --git a/tests/modules/tnotuniquename.nim b/tests/modules/tnotuniquename.nim
index 403c8a47e..bc401e662 100644
--- a/tests/modules/tnotuniquename.nim
+++ b/tests/modules/tnotuniquename.nim
@@ -1,7 +1,10 @@
 discard """
-  errormsg: "module names need to be unique per Nimble package"
-  file: "tnotuniquename/mnotuniquename.nim"
+  output: '''nested
+flat'''
 """
 
 import mnotuniquename
-import tnotuniquename/mnotuniquename as nun
+import tnotuniquename_dir/mnotuniquename as nun
+
+nested()
+flat()
diff --git a/tests/modules/tnotuniquename/mnotuniquename.nim b/tests/modules/tnotuniquename/mnotuniquename.nim
deleted file mode 100644
index e69de29bb..000000000
--- a/tests/modules/tnotuniquename/mnotuniquename.nim
+++ /dev/null
diff --git a/tests/modules/tnotuniquename2.nim b/tests/modules/tnotuniquename2.nim
index 7aa4de515..e4501bc24 100644
--- a/tests/modules/tnotuniquename2.nim
+++ b/tests/modules/tnotuniquename2.nim
@@ -1,6 +1,7 @@
 discard """
   errormsg: "module names need to be unique per Nimble package"
   file: "tnotuniquename/mnotuniquename.nim"
+  disabled: "true"
 """
 
 import mnotuniquename
diff --git a/tests/modules/tnotuniquename_dir/mnotuniquename.nim b/tests/modules/tnotuniquename_dir/mnotuniquename.nim
new file mode 100644
index 000000000..11e52d9d0
--- /dev/null
+++ b/tests/modules/tnotuniquename_dir/mnotuniquename.nim
@@ -0,0 +1,2 @@
+
+proc nested*() = echo "nested"
diff --git a/tests/types/tillegaltuplerecursiongeneric.nim b/tests/types/tillegaltuplerecursiongeneric.nim
index e5191e4d7..da65d48eb 100644
--- a/tests/types/tillegaltuplerecursiongeneric.nim
+++ b/tests/types/tillegaltuplerecursiongeneric.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "illegal recursion in type 'RefTreeInt'"
+  errormsg: "illegal recursion in type 'RefTree'"
 """
 
 type
diff --git a/tests/types/tillegaltuplerecursiongeneric2.nim b/tests/types/tillegaltuplerecursiongeneric2.nim
new file mode 100644
index 000000000..36ebd63be
--- /dev/null
+++ b/tests/types/tillegaltuplerecursiongeneric2.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "illegal recursion in type 'TPearl'"
+"""
+
+type
+  TPearl[T] = tuple
+    next: TPearl[T]
+
+var x: TPearl[int]
diff --git a/tests/types/tillegaltyperecursion2.nim b/tests/types/tillegaltyperecursion2.nim
index b5ffdda72..c539a361d 100644
--- a/tests/types/tillegaltyperecursion2.nim
+++ b/tests/types/tillegaltyperecursion2.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "invalid recursion in type 'Executor'"
+  errormsg: "illegal recursion in type 'Executor'"
   line: 8
 """
 # bug reported by PR #5637