summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--changelog.md10
-rw-r--r--compiler/ccgexprs.nim51
-rw-r--r--compiler/closureiters.nim2
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/docgen.nim11
-rw-r--r--compiler/extccomp.nim15
-rw-r--r--compiler/lambdalifting.nim14
-rw-r--r--compiler/lineinfos.nim5
-rw-r--r--compiler/modulepaths.nim21
-rw-r--r--compiler/options.nim24
-rw-r--r--compiler/parser.nim1
-rw-r--r--compiler/renderer.nim25
-rw-r--r--compiler/sem.nim8
-rw-r--r--compiler/semcall.nim10
-rw-r--r--compiler/semexprs.nim7
-rw-r--r--compiler/semfold.nim2
-rw-r--r--compiler/semmagic.nim67
-rw-r--r--compiler/semtypes.nim43
-rw-r--r--compiler/types.nim10
-rw-r--r--compiler/vm.nim29
-rw-r--r--compiler/vmdef.nim5
-rw-r--r--compiler/vmgen.nim133
-rw-r--r--config/nimdoc.cfg4
-rw-r--r--doc/manual.rst23
-rw-r--r--doc/mytest.cfg1
-rw-r--r--koch.nim9
-rw-r--r--lib/core/macros.nim15
-rw-r--r--lib/packages/docutils/rstgen.nim12
-rw-r--r--lib/pure/collections/sequtils.nim26
-rw-r--r--lib/pure/collections/sets.nim12
-rw-r--r--lib/pure/encodings.nim8
-rw-r--r--lib/pure/includes/oserr.nim2
-rw-r--r--lib/pure/ioselects/ioselectors_epoll.nim29
-rw-r--r--lib/pure/ioselects/ioselectors_kqueue.nim27
-rw-r--r--lib/pure/ioselects/ioselectors_poll.nim25
-rw-r--r--lib/pure/ioselects/ioselectors_select.nim9
-rw-r--r--lib/pure/json.nim35
-rw-r--r--lib/pure/mimetypes.nim1
-rw-r--r--lib/pure/net.nim2
-rw-r--r--lib/pure/os.nim8
-rw-r--r--lib/pure/ospaths.nim7
-rw-r--r--lib/pure/random.nim17
-rw-r--r--lib/pure/selectors.nim9
-rw-r--r--lib/pure/times.nim35
-rw-r--r--lib/system.nim10
-rw-r--r--lib/system/alloc.nim16
-rw-r--r--lib/system/ansi_c.nim3
-rw-r--r--lib/system/gc.nim5
-rw-r--r--lib/system/gc_common.nim3
-rw-r--r--lib/system/sysio.nim8
-rw-r--r--lib/system/sysstr.nim77
-rw-r--r--tests/async/t6100.nim15
-rw-r--r--tests/async/t7985.nim19
-rw-r--r--tests/async/tasync_misc.nim47
-rw-r--r--tests/closure/tclosure3.nim2
-rw-r--r--tests/errmsgs/t8434.nim16
-rw-r--r--tests/fragmentation/tfragment_alloc.nim9
-rw-r--r--tests/gc/growobjcrash.nim2
-rw-r--r--tests/generics/t8403.nim11
-rw-r--r--tests/generics/t8439.nim10
-rw-r--r--tests/iter/t338.nim20
-rw-r--r--tests/macros/tbindsym.nim42
-rw-r--r--tests/macros/tmacrostmt.nim33
-rw-r--r--tests/misc/tsizeof.nim22
-rw-r--r--tests/pragmas/tcustom_pragma.nim20
-rw-r--r--tests/sets/tsetpop.nim22
-rw-r--r--tests/sets/tsets.nim10
-rw-r--r--tests/stdlib/tencoding.nim21
-rw-r--r--tests/stdlib/tjsonmacro.nim105
-rw-r--r--tests/stdlib/tos.nim2
-rw-r--r--tests/system/t7894.nim18
-rw-r--r--tests/system/talloc.nim43
-rw-r--r--tests/system/talloc2.nim3
-rw-r--r--tests/system/tdeepcopy.nim6
-rw-r--r--tests/system/tio.nim5
-rw-r--r--tests/system/tparams.nim15
-rw-r--r--tests/vm/tref.nim7
-rw-r--r--tools/dochack/dochack.nim36
-rw-r--r--tools/dochack/fuzzysearch.nim139
-rw-r--r--tools/nimweb.nim104
81 files changed, 1314 insertions, 425 deletions
diff --git a/.gitignore b/.gitignore
index fc090130b..f2037c1b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,6 +58,7 @@ dist/
 .*/
 ~*
 
-# testament cruft
+# testament cruft; TODO: generate these in a gitignore'd dir in the first place.
 testresults/
 test.txt
+/test.ini
diff --git a/changelog.md b/changelog.md
index 43b351ef1..045286ef9 100644
--- a/changelog.md
+++ b/changelog.md
@@ -22,7 +22,8 @@
   become an error in the future.
 - The ``'c`` and ``'C'`` prefix for octal literals is now deprecated to
   bring the language in line with the standard library (e.g. ``parseOct``).
-
+- The dot style for import paths (e.g ``import path.to.module`` instead of
+  ``import path/to/module``) has been deprecated.
 
 #### Breaking changes in the standard library
 
@@ -59,6 +60,8 @@
   1-based coordinates on POSIX for correct behaviour; the Windows behaviour
   was always correct).
 
+- ``lineInfoObj`` now returns absolute path instead of project path.
+  It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc.
 
 #### Breaking changes in the compiler
 
@@ -193,4 +196,9 @@
 - Nintendo Switch was added as a new platform target. See [the compiler user guide](https://nim-lang.org/docs/nimc.html)
   for more info.
 
+- macros.bindSym now capable to accepts not only literal string or string constant expression.
+  bindSym enhancement make it also can accepts computed string or ident node inside macros /
+  compile time functions / static blocks. Only in templates / regular code it retains it's old behavior.
+  This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch
+
 ### Bugfixes
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 69175fd18..5af0fe4e0 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1085,7 +1085,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
   #    appendChar(s, 'z');
   #  }
   var
-    a, dest: TLoc
+    a, dest, call: TLoc
     appends, lens: Rope
   assert(d.k == locNone)
   var L = 0
@@ -1109,8 +1109,9 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
     linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n",
             addrLoc(p.config, dest), lens, rope(L))
   else:
-    linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n",
-            rdLoc(dest), lens, rope(L))
+    initLoc(call, locCall, e, OnHeap)
+    call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, rope(L)])
+    genAssignment(p, dest, call, {})
   add(p.s(cpsStmts), appends)
   gcUsage(p.config, e)
 
@@ -1119,17 +1120,20 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
   #    seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x));
   #    seq->data[seq->len-1] = x;
   let seqAppendPattern = if not p.module.compileToCpp:
-                           "$1 = ($2) #incrSeqV3(&($1)->Sup, $3);$n"
+                           "($2) #incrSeqV3(&($1)->Sup, $3)"
                          else:
-                           "$1 = ($2) #incrSeqV3($1, $3);$n"
-  var a, b, dest, tmpL: TLoc
+                           "($2) #incrSeqV3($1, $3)"
+  var a, b, dest, tmpL, call: TLoc
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
   let seqType = skipTypes(e.sons[1].typ, {tyVar})
-  lineCg(p, cpsStmts, seqAppendPattern, [
-      rdLoc(a),
-      getTypeDesc(p.module, e.sons[1].typ),
-      genTypeInfo(p.module, seqType, e.info)])
+  initLoc(call, locCall, e, OnHeap)
+  call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a),
+    getTypeDesc(p.module, e.sons[1].typ),
+    genTypeInfo(p.module, seqType, e.info)])
+  # emit the write barrier if required, but we can always move here, so
+  # use 'genRefAssign' for the seq.
+  genRefAssign(p, a, call, {})
   #if bt != b.t:
   #  echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
   initLoc(dest, locExpr, e.sons[2], OnHeap)
@@ -1536,7 +1540,7 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
   if p.config.selectedGc == gcDestructors:
     genCall(p, e, d)
     return
-  var a, b: TLoc
+  var a, b, call: TLoc
   assert(d.k == locNone)
   var x = e.sons[1]
   if x.kind in {nkAddr, nkHiddenAddr}: x = x[0]
@@ -1544,20 +1548,30 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
   initLocExpr(p, e.sons[2], b)
   let t = skipTypes(e.sons[1].typ, {tyVar})
   let setLenPattern = if not p.module.compileToCpp:
-      "$1 = ($3) #setLengthSeqV2(&($1)->Sup, $4, $2);$n"
+      "($3) #setLengthSeqV2(&($1)->Sup, $4, $2)"
     else:
-      "$1 = ($3) #setLengthSeqV2($1, $4, $2);$n"
+      "($3) #setLengthSeqV2($1, $4, $2)"
 
-  lineCg(p, cpsStmts, setLenPattern, [
+  initLoc(call, locCall, e, OnHeap)
+  call.r = ropecg(p.module, setLenPattern, [
       rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
       genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)])
+  genAssignment(p, a, call, {})
   gcUsage(p.config, e)
 
 proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
   if p.config.selectedGc == gcDestructors:
     binaryStmtAddr(p, e, d, "#setLengthStrV2($1, $2);$n")
   else:
-    binaryStmt(p, e, d, "$1 = #setLengthStr($1, $2);$n")
+    var a, b, call: TLoc
+    if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr")
+    initLocExpr(p, e.sons[1], a)
+    initLocExpr(p, e.sons[2], b)
+
+    initLoc(call, locCall, e, OnHeap)
+    call.r = ropecg(p.module, "#setLengthStr($1, $2)", [
+        rdLoc(a), rdLoc(b)])
+    genAssignment(p, a, call, {})
     gcUsage(p.config, e)
 
 proc genSwap(p: BProc, e: PNode, d: var TLoc) =
@@ -1878,7 +1892,12 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     if p.config.selectedGC == gcDestructors:
       binaryStmtAddr(p, e, d, "#nimAddCharV1($1, $2);$n")
     else:
-      binaryStmt(p, e, d, "$1 = #addChar($1, $2);$n")
+      var dest, b, call: TLoc
+      initLoc(call, locCall, e, OnHeap)
+      initLocExpr(p, e.sons[1], dest)
+      initLocExpr(p, e.sons[2], b)
+      call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)])
+      genAssignment(p, dest, call, {})
   of mAppendStrStr: genStrAppend(p, e, d)
   of mAppendSeqElem:
     if p.config.selectedGc == gcDestructors:
diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim
index 6fa856b2f..9e4885b66 100644
--- a/compiler/closureiters.nim
+++ b/compiler/closureiters.nim
@@ -588,7 +588,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
           let branch = n[i]
           case branch.kind
           of nkOfBranch:
-            branch[1] = ctx.convertExprBodyToAsgn(branch[1], tmp)
+            branch[^1] = ctx.convertExprBodyToAsgn(branch[^1], tmp)
           of nkElse:
             branch[0] = ctx.convertExprBodyToAsgn(branch[0], tmp)
           else:
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index fcc7998df..d853ce969 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -73,3 +73,4 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimNotNil")
   defineSymbol("nimVmExportFixed")
   defineSymbol("nimNewRuntime")
+  defineSymbol("nimIncrSeqV3")
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 75b599ae9..8d233566c 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -259,7 +259,7 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
     of tkSpaces, tkInvalid:
       add(result, literal)
     of tkCurlyDotLe:
-      dispA(d.conf, result, "<span>" &  # This span is required for the JS to work properly
+      dispA(d.conf, result, "<span>" & # This span is required for the JS to work properly
         """<span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span>
 </span>
 <span class="pragmawrap">
@@ -517,12 +517,11 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
       path = path[cwd.len+1 .. ^1].replace('\\', '/')
     let gitUrl = getConfigVar(d.conf, "git.url")
     if gitUrl.len > 0:
-      var commit = getConfigVar(d.conf, "git.commit")
-      if commit.len == 0: commit = "master"
+      let commit = getConfigVar(d.conf, "git.commit", "master")
+      let develBranch = getConfigVar(d.conf, "git.devel", "devel")
       dispA(d.conf, seeSrcRope, "$1", "", [ropeFormatNamedVars(d.conf, docItemSeeSrc,
-          ["path", "line", "url", "commit"], [rope path,
-          rope($n.info.line), rope gitUrl,
-          rope commit])])
+          ["path", "line", "url", "commit", "devel"], [rope path,
+          rope($n.info.line), rope gitUrl, rope commit, rope develBranch])])
 
   add(d.section[k], ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item"),
     ["name", "header", "desc", "itemID", "header_plain", "itemSym",
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 575e30a79..16b0d614d 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -92,7 +92,7 @@ compiler nintendoSwitchGCC:
     optSize: " -Os -ffast-math ",
     compilerExe: "aarch64-none-elf-gcc",
     cppCompiler: "aarch64-none-elf-g++",
-    compileTmpl: "-MMD -MP -MF $dfile -c $options $include -o $objfile $file",
+    compileTmpl: "-w -MMD -MP -MF $dfile -c $options $include -o $objfile $file",
     buildGui: " -mwindows",
     buildDll: " -shared",
     buildLib: "aarch64-none-elf-gcc-ar rcs $libfile $objfiles",
@@ -645,7 +645,7 @@ proc compileCFile(conf: ConfigRef; list: CFileList, script: var Rope, cmds: var
     if optCompileOnly notin conf.globalOptions:
       add(cmds, compileCmd)
       let (_, name, _) = splitFile(it.cname)
-      add(prettyCmds, "CC: " & name)
+      add(prettyCmds, if hintCC in conf.notes: "CC: " & name else: "")
     if optGenScript in conf.globalOptions:
       add(script, compileCmd)
       add(script, "\n")
@@ -659,7 +659,7 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
         libname = getCurrentDir() / libname
     else:
       libname = (libNameTmpl(conf) % splitFile(conf.projectName).name)
-    result = CC[conf.cCompiler].buildLib % ["libfile", libname,
+    result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(libname),
                                        "objfiles", objfiles]
   else:
     var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe")
@@ -668,8 +668,10 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
     if needsExeExt(conf): linkerExe = addFileExt(linkerExe, "exe")
     if noAbsolutePaths(conf): result = linkerExe
     else: result = joinPath(conf.cCompilerpath, linkerExe)
-    let buildgui = if optGenGuiApp in conf.globalOptions: CC[conf.cCompiler].buildGui
-                   else: ""
+    let buildgui = if optGenGuiApp in conf.globalOptions and conf.target.targetOS == osWindows:
+                     CC[conf.cCompiler].buildGui
+                   else:
+                     ""
     var exefile, builddll: string
     if optGenDynLib in conf.globalOptions:
       exefile = platform.OS[conf.target.targetOS].dllFrmt % splitFile(projectfile).name
@@ -771,7 +773,8 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) =
   var prettyCmds: TStringSeq = @[]
   let prettyCb = proc (idx: int) =
     when declared(echo):
-      echo prettyCmds[idx]
+      let cmd = prettyCmds[idx]
+      if cmd != "": echo cmd
   compileCFile(conf, conf.toCompile, script, cmds, prettyCmds)
   if optCompileOnly notin conf.globalOptions:
     execCmdsInParallel(conf, cmds, prettyCb)
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 0a4801150..d8c0461ce 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -222,7 +222,7 @@ proc interestingIterVar(s: PSym): bool {.inline.} =
   # XXX optimization: Only lift the variable if it lives across
   # yield/return boundaries! This can potentially speed up
   # closure iterators quite a bit.
-  result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
+  result = s.kind in {skResult, skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
 
 template isIterator*(owner: PSym): bool =
   owner.kind == skIterator and owner.typ.callConv == ccClosure
@@ -458,6 +458,10 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
   of nkLambdaKinds, nkIteratorDef, nkFuncDef:
     if n.typ != nil:
       detectCapturedVars(n[namePos], owner, c)
+  of nkReturnStmt:
+    if n[0].kind in {nkAsgn, nkFastAsgn}:
+      detectCapturedVars(n[0].sons[1], owner, c)
+    else: assert n[0].kind == nkEmpty
   else:
     for i in 0..<n.len:
       detectCapturedVars(n[i], owner, c)
@@ -687,6 +691,13 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
     if n.len == 2:
       n.sons[1] = liftCapturedVars(n[1], owner, d, c)
       if n[1].kind == nkClosure: result = n[1]
+  of nkReturnStmt:
+    if n[0].kind in {nkAsgn, nkFastAsgn}:
+      # we have a `result = result` expression produced by the closure
+      # transform, let's not touch the LHS in order to make the lifting pass
+      # correct when `result` is lifted
+      n[0].sons[1] = liftCapturedVars(n[0].sons[1], owner, d, c)
+    else: assert n[0].kind == nkEmpty
   else:
     if owner.isIterator:
       if nfLL in n.flags:
@@ -757,6 +768,7 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PN
     if d.somethingToDo:
       var c = initLiftingPass(fn)
       result = liftCapturedVars(body, fn, d, c)
+      # echo renderTree(result, {renderIds})
       if c.envvars.getOrDefault(fn.id) != nil:
         result = newTree(nkStmtList, rawClosureCreation(fn, d, c), result)
     else:
diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim
index b8678e6ba..261dcb44e 100644
--- a/compiler/lineinfos.nim
+++ b/compiler/lineinfos.nim
@@ -37,7 +37,7 @@ type
     warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
     warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
     warnInconsistentSpacing, warnUser,
-    hintSuccess, hintSuccessX,
+    hintSuccess, hintSuccessX, hintCC,
     hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
     hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
     hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
@@ -94,6 +94,7 @@ const
     warnUser: "$1",
     hintSuccess: "operation successful",
     hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
+    hintCC: "CC: \'$1\'", # unused
     hintLineTooLong: "line too long",
     hintXDeclaredButNotUsed: "'$1' is declared but not used",
     hintConvToBaseNotNeeded: "conversion to base object is not needed",
@@ -136,7 +137,7 @@ const
     "Spacing", "User"]
 
   HintsToStr* = [
-    "Success", "SuccessX", "LineTooLong",
+    "Success", "SuccessX", "CC", "LineTooLong",
     "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
     "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
     "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim
index 87c7f3541..e5cbf3a2c 100644
--- a/compiler/modulepaths.nim
+++ b/compiler/modulepaths.nim
@@ -45,13 +45,6 @@ when false:
     if best.len > 0 and fileExists(res):
       result = res
 
-const stdlibDirs = [
-  "pure", "core", "arch",
-  "pure/collections",
-  "pure/concurrency", "impure",
-  "wrappers", "wrappers/linenoise",
-  "windows", "posix", "js"]
-
 when false:
   proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): string =
     template attempt(a) =
@@ -120,7 +113,9 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
   case n.kind
   of nkStrLit, nkRStrLit, nkTripleStrLit:
     try:
-      result = pathSubs(conf, n.strVal, toFullPath(conf, n.info).splitFile().dir)
+      result =
+        pathSubs(conf, n.strVal, toFullPath(conf, n.info).splitFile().dir)
+          .replace(" ")
     except ValueError:
       localError(conf, n.info, "invalid path: " & n.strVal)
       result = n.strVal
@@ -147,16 +142,9 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
           result = ""
     else:
       let modname = getModuleName(conf, n[2])
-      if $n1 == "std":
-        template attempt(a) =
-          let x = addFileExt(a, "nim")
-          if fileExists(x): return x
-        for candidate in stdlibDirs:
-          attempt(conf.libpath / candidate / modname)
-
       # hacky way to implement 'x / y /../ z':
       result = getModuleName(conf, n1)
-      result.add renderTree(n0, {renderNoComments})
+      result.add renderTree(n0, {renderNoComments}).replace(" ")
       result.add modname
   of nkPrefix:
     when false:
@@ -167,6 +155,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
     # hacky way to implement 'x / y /../ z':
     result = renderTree(n, {renderNoComments}).replace(" ")
   of nkDotExpr:
+    localError(conf, n.info, warnDeprecated, "using '.' instead of '/' in import paths")
     result = renderTree(n, {renderNoComments}).replace(".", "/")
   of nkImportAs:
     result = getModuleName(conf, n.sons[0])
diff --git a/compiler/options.nim b/compiler/options.nim
index 98c20a9b5..a776961fc 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -116,7 +116,8 @@ type
     callOperator,
     parallel,
     destructor,
-    notnil
+    notnil,
+    dynamicBindSym
 
   SymbolFilesOption* = enum
     disabledSf, writeOnlySf, readOnlySf, v2Sf
@@ -406,8 +407,8 @@ proc mainCommandArg*(conf: ConfigRef): string =
 proc existsConfigVar*(conf: ConfigRef; key: string): bool =
   result = hasKey(conf.configVars, key)
 
-proc getConfigVar*(conf: ConfigRef; key: string): string =
-  result = conf.configVars.getOrDefault key
+proc getConfigVar*(conf: ConfigRef; key: string, default = ""): string =
+  result = conf.configVars.getOrDefault(key, default)
 
 proc setConfigVar*(conf: ConfigRef; key, val: string) =
   conf.configVars[key] = val
@@ -556,13 +557,28 @@ proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): string {.pro
           result = rawFindFile2(conf, f.toLowerAscii)
   patchModule(conf)
 
+const stdlibDirs = [
+  "pure", "core", "arch",
+  "pure/collections",
+  "pure/concurrency", "impure",
+  "wrappers", "wrappers/linenoise",
+  "windows", "posix", "js"]
+
 proc findModule*(conf: ConfigRef; modulename, currentModule: string): string =
   # returns path to module
   const pkgPrefix = "pkg/"
-  let m = addFileExt(modulename, NimExt)
+  const stdPrefix = "std/"
+  var m = addFileExt(modulename, NimExt)
   if m.startsWith(pkgPrefix):
     result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true)
   else:
+    if m.startsWith(stdPrefix):
+      let stripped = m.substr(stdPrefix.len)
+      for candidate in stdlibDirs:
+        let path = (conf.libpath / candidate / stripped)
+        if fileExists(path):
+          m = path
+          break
     let currentPath = currentModule.splitFile.dir
     result = currentPath / m
     if not existsFile(result):
diff --git a/compiler/parser.nim b/compiler/parser.nim
index c513fac68..5664a9f67 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -568,6 +568,7 @@ proc parsePar(p: var TParser): PNode =
   result = newNodeP(nkPar, p)
   getTok(p)
   optInd(p, result)
+  flexComment(p, result)
   if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase,
                        tkTry, tkDefer, tkFinally, tkExcept, tkFor, tkBlock,
                        tkConst, tkLet, tkWhen, tkVar,
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 83cf288ff..ce27e1cd9 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -627,15 +627,23 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
       gcoms(g)
     if doIndent: dedent(g)
   else:
-    if rfLongMode in c.flags: indentNL(g)
+    indentNL(g)
     gsub(g, n)
     gcoms(g)
+    dedent(g)
     optNL(g)
-    if rfLongMode in c.flags: dedent(g)
+
+
+proc gcond(g: var TSrcGen, n: PNode) =
+  if n.kind == nkStmtListExpr:
+    put(g, tkParLe, "(")
+  gsub(g, n)  
+  if n.kind == nkStmtListExpr:
+    put(g, tkParRi, ")")
 
 proc gif(g: var TSrcGen, n: PNode) =
   var c: TContext
-  gsub(g, n.sons[0].sons[0])
+  gcond(g, n.sons[0].sons[0])
   initContext(c)
   putWithSpace(g, tkColon, ":")
   if longMode(g, n) or (lsub(g, n.sons[0].sons[1]) + g.lineLen > MaxLineLen):
@@ -650,7 +658,7 @@ proc gif(g: var TSrcGen, n: PNode) =
 proc gwhile(g: var TSrcGen, n: PNode) =
   var c: TContext
   putWithSpace(g, tkWhile, "while")
-  gsub(g, n.sons[0])
+  gcond(g, n.sons[0])
   putWithSpace(g, tkColon, ":")
   initContext(c)
   if longMode(g, n) or (lsub(g, n.sons[1]) + g.lineLen > MaxLineLen):
@@ -714,7 +722,7 @@ proc gcase(g: var TSrcGen, n: PNode) =
   var last = if n.sons[length-1].kind == nkElse: -2 else: -1
   if longMode(g, n, 0, last): incl(c.flags, rfLongMode)
   putWithSpace(g, tkCase, "case")
-  gsub(g, n.sons[0])
+  gcond(g, n.sons[0])
   gcoms(g)
   optNL(g)
   gsons(g, n, c, 1, last)
@@ -785,10 +793,7 @@ proc gblock(g: var TSrcGen, n: PNode) =
   if longMode(g, n) or (lsub(g, n.sons[1]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)
-  # XXX I don't get why this is needed here! gstmts should already handle this!
-  indentNL(g)
   gstmts(g, n.sons[1], c)
-  dedent(g)
 
 proc gstaticStmt(g: var TSrcGen, n: PNode) =
   var c: TContext
@@ -1104,13 +1109,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     put(g, tkAccent, "`")
   of nkIfExpr:
     putWithSpace(g, tkIf, "if")
-    if n.len > 0: gsub(g, n.sons[0], 0)
+    if n.len > 0: gcond(g, n.sons[0].sons[0])
     putWithSpace(g, tkColon, ":")
     if n.len > 0: gsub(g, n.sons[0], 1)
     gsons(g, n, emptyContext, 1)
   of nkElifExpr:
     putWithSpace(g, tkElif, " elif")
-    gsub(g, n, 0)
+    gcond(g, n[0])
     putWithSpace(g, tkColon, ":")
     gsub(g, n, 1)
   of nkElseExpr:
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 0d484d276..2c3fab5a5 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -608,16 +608,18 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
   rod.storeNode(c.graph, c.module, result)
 
 proc testExamples(c: PContext) =
+  let outputDir = c.config.getNimcacheDir / "runnableExamples"
+  createDir(outputDir)
   let inp = toFullPath(c.config, c.module.info)
-  let outp = inp.changeFileExt"" & "_examples.nim"
+  let outp = outputDir / extractFilename(inp.changeFileExt"" & "_examples.nim")
   let nimcache = outp.changeFileExt"" & "_nimcache"
-  renderModule(c.runnableExamples, inp, outp)
+  renderModule(c.runnableExamples, inp, outp, conf = c.config)
   let backend = if isDefined(c.config, "js"): "js"
                 elif isDefined(c.config, "cpp"): "cpp"
                 elif isDefined(c.config, "objc"): "objc"
                 else: "c"
   if os.execShellCmd(os.getAppFilename() & " " & backend & " --nimcache:" & nimcache & " -r " & outp) != 0:
-    quit "[Examples] failed"
+    quit "[Examples] failed: see " & outp
   else:
     removeFile(outp)
     removeFile(outp.changeFileExt(ExeExt))
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 67fe99232..aa5394a71 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -505,7 +505,15 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
 
   for i in 1..sonsLen(n)-1:
     let formal = s.ast.sons[genericParamsPos].sons[i-1].typ
-    let arg = n[i].typ
+    var arg = n[i].typ
+    # try transforming the argument into a static one before feeding it into
+    # typeRel
+    if formal.kind == tyStatic and arg.kind != tyStatic:
+      let evaluated = c.semTryConstExpr(c, n[i])
+      if evaluated != nil:
+        arg = newTypeS(tyStatic, c)
+        arg.sons = @[evaluated.typ]
+        arg.n = evaluated
     let tm = typeRel(m, formal, arg)
     if tm in {isNone, isConvertible}: return nil
   var newInst = generateInstance(c, s, m.bindings, n.info)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 3a72d1f5a..c9f9eb33f 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -2031,9 +2031,10 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
           c.runnableExamples = newTree(nkStmtList,
             newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
         let imports = newTree(nkStmtList)
-        extractImports(n.lastSon, imports)
+        var saved_lastSon = copyTree n.lastSon
+        extractImports(saved_lastSon, imports)
         for imp in imports: c.runnableExamples.add imp
-        c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree n.lastSon)
+        c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree saved_lastSon)
       result = setMs(n, s)
     else:
       result = c.graph.emptyNode
@@ -2126,7 +2127,7 @@ proc semSetConstr(c: PContext, n: PNode): PNode =
         n.sons[i] = semExprWithType(c, n.sons[i])
         if typ == nil:
           typ = skipTypes(n.sons[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
-    if not isOrdinalType(typ):
+    if not isOrdinalType(typ, allowEnumWithHoles=true):
       localError(c.config, n.info, errOrdinalTypeExpected)
       typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
     elif lengthOrd(c.config, typ) > MaxSetElements:
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 2f495bc7f..d2abfac13 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -609,7 +609,7 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
         if computeSize(g.config, a.typ) < 0:
           localError(g.config, a.info, "cannot evaluate 'sizeof' because its type is not defined completely")
           result = nil
-        elif skipTypes(a.typ, typedescInst+{tyRange}).kind in
+        elif skipTypes(a.typ, typedescInst+{tyRange, tyArray}).kind in
              IntegralTypes+NilableTypes+{tySet}:
           #{tyArray,tyObject,tyTuple}:
           result = newIntNodeT(getSize(g.config, a.typ), n, g)
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index b5875c67a..8bfa5545e 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -207,6 +207,67 @@ proc semBindSym(c: PContext, n: PNode): PNode =
   else:
     errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal)
 
+proc opBindSym(c: PContext, scope: PScope, n: PNode, isMixin: int, info: PNode): PNode =
+  if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit, nkIdent}:
+    localError(c.config, info.info, errStringOrIdentNodeExpected)
+    return errorNode(c, n)
+
+  if isMixin < 0 or isMixin > high(TSymChoiceRule).int:
+    localError(c.config, info.info, errConstExprExpected)
+    return errorNode(c, n)
+
+  let id = if n.kind == nkIdent: n
+    else: newIdentNode(getIdent(c.cache, n.strVal), info.info)
+
+  let tmpScope = c.currentScope
+  c.currentScope = scope
+  let s = qualifiedLookUp(c, id, {checkUndeclared})
+  if s != nil:
+    # we need to mark all symbols:
+    result = symChoice(c, id, s, TSymChoiceRule(isMixin))
+  else:
+    errorUndeclaredIdentifier(c, info.info, if n.kind == nkIdent: n.ident.s
+      else: n.strVal)
+  c.currentScope = tmpScope
+
+proc semDynamicBindSym(c: PContext, n: PNode): PNode =
+  # inside regular code, bindSym resolves to the sym-choice
+  # nodes (see tinspectsymbol)
+  if not (c.inStaticContext > 0 or getCurrOwner(c).isCompileTimeProc):
+    return semBindSym(c, n)
+
+  if c.graph.vm.isNil:
+    setupGlobalCtx(c.module, c.graph)
+
+  let
+    vm = PCtx c.graph.vm
+    # cache the current scope to
+    # prevent it lost into oblivion
+    scope = c.currentScope
+
+  # cannot use this
+  # vm.config.features.incl dynamicBindSym
+
+  proc bindSymWrapper(a: VmArgs) =
+    # capture PContext and currentScope
+    # param description:
+    #   0. ident, a string literal / computed string / or ident node
+    #   1. bindSym rule
+    #   2. info node
+    a.setResult opBindSym(c, scope, a.getNode(0), a.getInt(1).int, a.getNode(2))
+
+  let
+    # altough we use VM callback here, it is not
+    # executed like 'normal' VM callback
+    idx = vm.registerCallback("bindSymImpl", bindSymWrapper)
+    # dummy node to carry idx information to VM
+    idxNode = newIntTypeNode(nkIntLit, idx, c.graph.getSysType(TLineInfo(), tyInt))
+
+  result = copyNode(n)
+  for x in n: result.add x
+  result.add n # info node
+  result.add idxNode
+
 proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
 
 proc semOf(c: PContext, n: PNode): PNode =
@@ -270,7 +331,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
   of mOf: result = semOf(c, n)
   of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic)
   of mShallowCopy: result = semShallowCopy(c, n, flags)
-  of mNBindSym: result = semBindSym(c, n)
+  of mNBindSym:
+    if dynamicBindSym notin c.features:
+      result = semBindSym(c, n)
+    else:
+      result = semDynamicBindSym(c, n)
   of mProcCall:
     result = n
     result.typ = n[1].typ
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 29ed1b870..28dd15209 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -11,6 +11,7 @@
 # included from sem.nim
 
 const
+  errStringOrIdentNodeExpected = "string or ident node expected"
   errStringLiteralExpected = "string literal expected"
   errIntLiteralExpected = "integer literal expected"
   errWrongNumberOfVariables = "wrong number of variables"
@@ -136,7 +137,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
     addSonSkipIntLit(result, base)
     if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base)
     if base.kind != tyGenericParam:
-      if not isOrdinalType(base):
+      if not isOrdinalType(base, allowEnumWithHoles = true):
         localError(c.config, n.info, errOrdinalTypeExpected)
       elif lengthOrd(c.config, base) > MaxSetElements:
         localError(c.config, n.info, errSetTooBig)
@@ -1508,26 +1509,20 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     of mSet: result = semSet(c, n, prev)
     of mOrdinal: result = semOrdinal(c, n, prev)
     of mSeq:
-      let s = c.graph.sysTypes[tySequence]
-      assert s != nil
-      assert prev == nil
-      result = copyType(s, s.owner, keepId=false)
-      # XXX figure out why this has children already...
-      result.sons.setLen 0
-      result.n = nil
       if c.config.selectedGc == gcDestructors:
-        result.flags = {tfHasAsgn}
+        let s = c.graph.sysTypes[tySequence]
+        assert s != nil
+        assert prev == nil
+        result = copyType(s, s.owner, keepId=false)
+        # XXX figure out why this has children already...
+        result.sons.setLen 0
+        result.n = nil
+        if c.config.selectedGc == gcDestructors:
+          result.flags = {tfHasAsgn}
+        else:
+          result.flags = {}
+        semContainerArg(c, n, "seq", result)
       else:
-        result.flags = {}
-      semContainerArg(c, n, "seq", result)
-      when false:
-        debugT = true
-        echo "Start!"
-        #debug result
-        assert(not containsGenericType(result))
-        debugT = false
-        echo "End!"
-      when false:
         result = semContainer(c, n, tySequence, "seq", prev)
         if c.config.selectedGc == gcDestructors:
           incl result.flags, tfHasAsgn
@@ -1603,8 +1598,14 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
         result = prev
   of nkSym:
     let s = getGenSym(c, n.sym)
-    if s.kind == skType and s.typ != nil:
-      var t = s.typ
+    if s.kind == skType and s.typ != nil or
+      s.kind == skParam and s.typ.kind == tyTypeDesc:
+      var t =
+        if s.kind == skType:
+          s.typ
+        else:
+          internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
+          s.typ.base
       let alias = maybeAliasType(c, t, prev)
       if alias != nil:
         result = alias
diff --git a/compiler/types.nim b/compiler/types.nim
index 60a596a9a..514f5cee5 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -498,9 +498,15 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
       add(result, typeToString(t.sons[i]))
     result.add "]"
   of tyAnd:
-    result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
+    for i, son in t.sons:
+      result.add(typeToString(son))
+      if i < t.sons.high:
+        result.add(" and ")
   of tyOr:
-    result = typeToString(t.sons[0]) & " or " & typeToString(t.sons[1])
+    for i, son in t.sons:
+      result.add(typeToString(son))
+      if i < t.sons.high:
+        result.add(" or ")
   of tyNot:
     result = "not " & typeToString(t.sons[0])
   of tyExpr:
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 373a64e39..a6ec4788b 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -616,19 +616,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       let rc = instr.regC
       case regs[ra].kind
       of rkNodeAddr:
-        # XXX: Workaround for vmgen bug:
         let n = regs[rc].regToNode
-        if (nfIsRef in regs[ra].nodeAddr[].flags or
-            regs[ra].nodeAddr[].kind == nkNilLit) and nfIsRef notin n.flags:
-          if regs[ra].nodeAddr[].kind == nkNilLit:
-            stackTrace(c, tos, pc, errNilAccess)
-          regs[ra].nodeAddr[][] = n[]
-          regs[ra].nodeAddr[].flags.incl nfIsRef
         # `var object` parameters are sent as rkNodeAddr. When they are mutated
         # vmgen generates opcWrDeref, which means that we must dereference
         # twice.
         # TODO: This should likely be handled differently in vmgen.
-        elif (nfIsRef notin regs[ra].nodeAddr[].flags and
+        if (nfIsRef notin regs[ra].nodeAddr[].flags and
             nfIsRef notin n.flags):
           regs[ra].nodeAddr[][] = n[]
         else:
@@ -1229,9 +1222,25 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
           node.typ.callConv == ccClosure and node.sons[0].kind == nkNilLit and
           node.sons[1].kind == nkNilLit))
     of opcNBindSym:
+      # cannot use this simple check
+      # if dynamicBindSym notin c.config.features:
+
+      # bindSym with static input
       decodeBx(rkNode)
       regs[ra].node = copyTree(c.constants.sons[rbx])
       regs[ra].node.flags.incl nfIsRef
+    of opcNDynBindSym:
+      # experimental bindSym
+      let
+        rb = instr.regB
+        rc = instr.regC
+        idx = int(regs[rb+rc-1].intVal)
+        callback = c.callbacks[idx].value
+        args = VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[pointer](regs),
+                currentException: c.currentExceptionB,
+                currentLineInfo: c.debug[pc])
+      callback(args)
+      regs[ra].node.flags.incl nfIsRef
     of opcNChild:
       decodeBC(rkNode)
       let idx = regs[rc].intVal.int
@@ -1416,7 +1425,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNGetFile:
       decodeB(rkNode)
       let n = regs[rb].node
-      regs[ra].node = newStrNode(nkStrLit, toFilename(c.config, n.info))
+      regs[ra].node = newStrNode(nkStrLit, toFullPath(c.config, n.info))
       regs[ra].node.info = n.info
       regs[ra].node.typ = n.typ
     of opcNGetLine:
@@ -1780,7 +1789,7 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode =
 
 include vmops
 
-proc setupGlobalCtx(module: PSym; graph: ModuleGraph) =
+proc setupGlobalCtx*(module: PSym; graph: ModuleGraph) =
   if graph.vm.isNil:
     graph.vm = newCtx(module, graph.cache, graph)
     registerAdditionalOps(PCtx graph.vm)
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index f7466b392..866b79568 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -136,7 +136,7 @@ type
     opcLdGlobalAddr, # dest = addr(globals[Bx])
 
     opcLdImmInt,  # dest = immediate value
-    opcNBindSym,
+    opcNBindSym, opcNDynBindSym,
     opcSetType,   # dest.typ = types[Bx]
     opcTypeTrait,
     opcMarshalLoad, opcMarshalStore,
@@ -229,7 +229,8 @@ proc refresh*(c: PCtx, module: PSym) =
   c.prc = PProc(blocks: @[])
   c.loopIterations = MaxLoopIterations
 
-proc registerCallback*(c: PCtx; name: string; callback: VmCallback) =
+proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.discardable.} =
+  result = c.callbacks.len
   c.callbacks.add((name, callback))
 
 const
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index d2243376c..3b5ea4beb 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -37,7 +37,9 @@ when hasFFI:
   import evalffi
 
 type
-  TGenFlag = enum gfAddrOf, gfFieldAccess
+  TGenFlag = enum
+    gfNode # Affects how variables are loaded - always loads as rkNode
+    gfNodeAddr # Affects how variables are loaded - always loads as rkNodeAddr
   TGenFlags = set[TGenFlag]
 
 proc debugInfo(c: PCtx; info: TLineInfo): string =
@@ -563,7 +565,7 @@ proc genIndex(c: PCtx; n: PNode; arr: PType): TRegister =
 proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
   case le.kind
   of nkBracketExpr:
-    let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess})
+    let dest = c.genx(le.sons[0], {gfNode})
     let idx = c.genIndex(le.sons[1], le.sons[0].typ)
     c.gABC(le, opcWrArr, dest, idx, value)
     c.freeTemp(dest)
@@ -571,17 +573,17 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
   of nkDotExpr, nkCheckedFieldExpr:
     # XXX field checks here
     let left = if le.kind == nkDotExpr: le else: le.sons[0]
-    let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess})
+    let dest = c.genx(left.sons[0], {gfNode})
     let idx = genField(c, left.sons[1])
     c.gABC(left, opcWrObj, dest, idx, value)
     c.freeTemp(dest)
   of nkDerefExpr, nkHiddenDeref:
-    let dest = c.genx(le.sons[0], {gfAddrOf})
+    let dest = c.genx(le.sons[0], {gfNode})
     c.gABC(le, opcWrDeref, dest, 0, value)
     c.freeTemp(dest)
   of nkSym:
     if le.sym.isGlobal:
-      let dest = c.genx(le, {gfAddrOf})
+      let dest = c.genx(le, {gfNodeAddr})
       c.gABC(le, opcWrDeref, dest, 0, value)
       c.freeTemp(dest)
   else:
@@ -815,6 +817,43 @@ proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) =
   c.freeTemp(tmp2)
   c.freeTemp(tmp3)
 
+proc genBindSym(c: PCtx; n: PNode; dest: var TDest) =
+  # nah, cannot use c.config.features because sempass context
+  # can have local experimental switch
+  # if dynamicBindSym notin c.config.features:
+  if n.len == 2: # hmm, reliable?
+    # bindSym with static input
+    if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}:
+      let idx = c.genLiteral(n[1])
+      if dest < 0: dest = c.getTemp(n.typ)
+      c.gABx(n, opcNBindSym, dest, idx)
+    else:
+      localError(c.config, n.info, "invalid bindSym usage")
+  else:
+    # experimental bindSym
+    if dest < 0: dest = c.getTemp(n.typ)
+    let x = c.getTempRange(n.len, slotTempUnknown)
+
+    # callee symbol
+    var tmp0 = TDest(x)
+    c.genLit(n.sons[0], tmp0)
+
+    # original parameters
+    for i in 1..<n.len-2:
+      var r = TRegister(x+i)
+      c.gen(n.sons[i], r)
+
+    # info node
+    var tmp1 = TDest(x+n.len-2)
+    c.genLit(n.sons[^2], tmp1)
+
+    # payload idx
+    var tmp2 = TDest(x+n.len-1)
+    c.genLit(n.sons[^1], tmp2)
+
+    c.gABC(n, opcNDynBindSym, dest, x, n.len)
+    c.freeTempRange(x, n.len)
+
 proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   case m
   of mAnd: c.genAndOr(n, opcFJmp, dest)
@@ -1135,13 +1174,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mNNewNimNode: genBinaryABC(c, n, dest, opcNNewNimNode)
   of mNCopyNimNode: genUnaryABC(c, n, dest, opcNCopyNimNode)
   of mNCopyNimTree: genUnaryABC(c, n, dest, opcNCopyNimTree)
-  of mNBindSym:
-    if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}:
-      let idx = c.genLiteral(n[1])
-      if dest < 0: dest = c.getTemp(n.typ)
-      c.gABx(n, opcNBindSym, dest, idx)
-    else:
-      localError(c.config, n.info, "invalid bindSym usage")
+  of mNBindSym: genBindSym(c, n, dest)
   of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent)
   of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent)
   of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode)
@@ -1252,41 +1285,21 @@ proc canElimAddr(n: PNode): PNode =
       # addr ( deref ( x )) --> x
       result = n.sons[0].sons[0]
 
-proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
-                  flags: TGenFlags) =
-  # a nop for certain types
-  let isAddr = opc in {opcAddrNode, opcAddrReg}
-  if isAddr and (let m = canElimAddr(n); m != nil):
+proc genAddr(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) =
+  if (let m = canElimAddr(n); m != nil):
     gen(c, m, dest, flags)
     return
 
-  let af = if n[0].kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr}: {gfAddrOf, gfFieldAccess}
-           else: {gfAddrOf}
-  let newflags = if isAddr: flags+af else: flags
-  # consider:
-  # proc foo(f: var ref int) =
-  #   f = new(int)
-  # proc blah() =
-  #   var x: ref int
-  #   foo x
-  #
-  # The type of 'f' is 'var ref int' and of 'x' is 'ref int'. Hence for
-  # nkAddr we must not use 'unneededIndirection', but for deref we use it.
-  if not isAddr and unneededIndirection(n.sons[0]):
-    gen(c, n.sons[0], dest, newflags)
-    if gfAddrOf notin flags and fitsRegister(n.typ):
-      c.gABC(n, opcNodeToReg, dest, dest)
-  elif isAddr and isGlobal(n.sons[0]):
+  let af = if n[0].kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr}: {gfNode}
+           else: {gfNodeAddr}
+  let newflags = flags-{gfNode, gfNodeAddr}+af
+
+  if isGlobal(n.sons[0]):
     gen(c, n.sons[0], dest, flags+af)
   else:
     let tmp = c.genx(n.sons[0], newflags)
     if dest < 0: dest = c.getTemp(n.typ)
-    if not isAddr:
-      gABC(c, n, opc, dest, tmp)
-      assert n.typ != nil
-      if gfAddrOf notin flags and fitsRegister(n.typ):
-        c.gABC(n, opcNodeToReg, dest, dest)
-    elif c.prc.slots[tmp].kind >= slotTempUnknown:
+    if c.prc.slots[tmp].kind >= slotTempUnknown:
       gABC(c, n, opcAddrNode, dest, tmp)
       # hack ahead; in order to fix bug #1781 we mark the temporary as
       # permanent, so that it's not used for anything else:
@@ -1297,6 +1310,19 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
       gABC(c, n, opcAddrReg, dest, tmp)
     c.freeTemp(tmp)
 
+proc genDeref(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) =
+  if unneededIndirection(n.sons[0]):
+    gen(c, n.sons[0], dest, flags)
+    if {gfNodeAddr, gfNode} * flags == {} and fitsRegister(n.typ):
+      c.gABC(n, opcNodeToReg, dest, dest)
+  else:
+    let tmp = c.genx(n.sons[0], flags)
+    if dest < 0: dest = c.getTemp(n.typ)
+    gABC(c, n, opcLdDeref, dest, tmp)
+    assert n.typ != nil
+    if {gfNodeAddr, gfNode} * flags == {} and fitsRegister(n.typ):
+      c.gABC(n, opcNodeToReg, dest, dest)
+
 proc whichAsgnOpc(n: PNode): TOpcode =
   case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind
   of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
@@ -1382,7 +1408,7 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode;
 proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
   case le.kind
   of nkBracketExpr:
-    let dest = c.genx(le.sons[0], {gfAddrOf, gfFieldAccess})
+    let dest = c.genx(le.sons[0], {gfNode})
     let idx = c.genIndex(le.sons[1], le.sons[0].typ)
     let tmp = c.genx(ri)
     if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
@@ -1394,13 +1420,13 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
   of nkDotExpr, nkCheckedFieldExpr:
     # XXX field checks here
     let left = if le.kind == nkDotExpr: le else: le.sons[0]
-    let dest = c.genx(left.sons[0], {gfAddrOf, gfFieldAccess})
+    let dest = c.genx(left.sons[0], {gfNode})
     let idx = genField(c, left.sons[1])
     let tmp = c.genx(ri)
     c.preventFalseAlias(left, opcWrObj, dest, idx, tmp)
     c.freeTemp(tmp)
   of nkDerefExpr, nkHiddenDeref:
-    let dest = c.genx(le.sons[0], {gfAddrOf})
+    let dest = c.genx(le.sons[0], {gfNode})
     let tmp = c.genx(ri)
     c.preventFalseAlias(le, opcWrDeref, dest, 0, tmp)
     c.freeTemp(tmp)
@@ -1409,7 +1435,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
     checkCanEval(c, le)
     if s.isGlobal:
       withTemp(tmp, le.typ):
-        c.gen(le, tmp, {gfAddrOf})
+        c.gen(le, tmp, {gfNodeAddr})
         let val = c.genx(ri)
         c.preventFalseAlias(le, opcWrDeref, tmp, 0, val)
         c.freeTemp(val)
@@ -1427,7 +1453,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
       else:
         gen(c, ri, dest)
   else:
-    let dest = c.genx(le, {gfAddrOf})
+    let dest = c.genx(le, {gfNodeAddr})
     genAsgn(c, dest, ri, requiresCopy)
 
 proc genTypeLit(c: PCtx; t: PType; dest: var TDest) =
@@ -1463,6 +1489,8 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
     c.freeTemp(tmp)
 
 proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
+  # gfNodeAddr and gfNode are mutually exclusive
+  assert card(flags * {gfNodeAddr, gfNode}) < 2
   let s = n.sym
   if s.isGlobal:
     if sfCompileTime in s.flags or c.mode == emRepl:
@@ -1474,13 +1502,13 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
       else: genGlobalInit(c, n, s)
     if dest < 0: dest = c.getTemp(n.typ)
     assert s.typ != nil
-    if gfAddrOf notin flags and fitsRegister(s.typ):
+    if gfNodeAddr in flags:
+      c.gABx(n, opcLdGlobalAddr, dest, s.position)
+    elif fitsRegister(s.typ) and gfNode notin flags:
       var cc = c.getTemp(n.typ)
       c.gABx(n, opcLdGlobal, cc, s.position)
       c.gABC(n, opcNodeToReg, dest, cc)
       c.freeTemp(cc)
-    elif {gfAddrOf, gfFieldAccess} * flags == {gfAddrOf}:
-      c.gABx(n, opcLdGlobalAddr, dest, s.position)
     else:
       c.gABx(n, opcLdGlobal, dest, s.position)
   else:
@@ -1498,7 +1526,8 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
       cannotEval(c, n)
 
 template needsRegLoad(): untyped =
-  gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar, tyLent}))
+  {gfNode, gfNodeAddr} * flags == {} and
+    fitsRegister(n.typ.skipTypes({tyVar, tyLent}))
 
 proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
                    flags: TGenFlags) =
@@ -1634,7 +1663,7 @@ proc genVarSection(c: PCtx; n: PNode) =
             c.globals.add(sa)
             s.position = c.globals.len
         if a.sons[2].kind != nkEmpty:
-          let tmp = c.genx(a.sons[0], {gfAddrOf})
+          let tmp = c.genx(a.sons[0], {gfNodeAddr})
           let val = c.genx(a.sons[2])
           c.genAdditionalCopy(a.sons[2], opcWrDeref, tmp, 0, val)
           c.freeTemp(val)
@@ -1839,8 +1868,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
   of nkDotExpr: genObjAccess(c, n, dest, flags)
   of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags)
   of nkBracketExpr: genArrAccess(c, n, dest, flags)
-  of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcLdDeref, flags)
-  of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags)
+  of nkDerefExpr, nkHiddenDeref: genDeref(c, n, dest, flags)
+  of nkAddr, nkHiddenAddr: genAddr(c, n, dest, flags)
   of nkIfStmt, nkIfExpr: genIf(c, n, dest)
   of nkWhenStmt:
     # This is "when nimvm" node. Chose the first branch.
diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg
index 6b6ec2d83..96e91283a 100644
--- a/config/nimdoc.cfg
+++ b/config/nimdoc.cfg
@@ -55,6 +55,8 @@ doc.item.toc = """
 # HTML rendered for doc.item's seeSrc variable. Note that this will render to
 # the empty string if you don't pass anything through --docSeeSrcURL. Available
 # substitutaion variables here are:
+# * $commit: branch/commit to use in source link.
+# * $devel: branch to use in edit link.
 # * $path: relative path to the file being processed.
 # * $line: line of the item in the original source file.
 # * $url: whatever you did pass through the --docSeeSrcUrl switch (which also
@@ -62,7 +64,7 @@ doc.item.toc = """
 doc.item.seesrc = """&nbsp;&nbsp;<a
 href="${url}/tree/${commit}/${path}#L${line}"
 class="link-seesrc" target="_blank">Source</a>
-<a href="${url}/edit/devel/${path}#L${line}" class="link-seesrc" target="_blank" >Edit</a>
+<a href="${url}/edit/${devel}/${path}#L${line}" class="link-seesrc" target="_blank" >Edit</a>
 """
 
 doc.toc = """
diff --git a/doc/manual.rst b/doc/manual.rst
index 7298b02a3..abdc4ce69 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -6229,10 +6229,10 @@ imported:
     :test: "nim c $1"
     :status: 1
 
-  import strutils except `%`, toUpper
+  import strutils except `%`, toUpperAscii
 
   # doesn't work then:
-  echo "$1" % "abc".toUpper
+  echo "$1" % "abc".toUpperAscii
 
 
 It is not checked that the ``except`` list is really exported from the module.
@@ -6261,24 +6261,24 @@ A module alias can be introduced via the ``as`` keyword:
 
   echo su.format("$1", "lalelu")
 
-The original module name is then not accessible. The
-notations ``path/to/module`` or ``path.to.module`` or ``"path/to/module"``
-can be used to refer to a module in subdirectories:
+The original module name is then not accessible. The notations
+``path/to/module`` or ``"path/to/module"`` can be used to refer to a module
+in subdirectories:
 
 .. code-block:: nim
-  import lib.pure.strutils, lib/pure/os, "lib/pure/times"
+  import lib/pure/os, "lib/pure/times"
 
-Note that the module name is still ``strutils`` and not ``lib.pure.strutils``
+Note that the module name is still ``strutils`` and not ``lib/pure/strutils``
 and so one **cannot** do:
 
 .. code-block:: nim
-  import lib.pure.strutils
-  echo lib.pure.strutils
+  import lib/pure/strutils
+  echo lib/pure/strutils.toUpperAscii("abc")
 
 Likewise the following does not make sense as the name is ``strutils`` already:
 
 .. code-block:: nim
-  import lib.pure.strutils as strutils
+  import lib/pure/strutils as strutils
 
 
 Collective imports from a directory
@@ -6297,7 +6297,8 @@ name is not a valid Nim identifier it needs to be a string literal:
 Pseudo import/include paths
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-A directory can also be a so called "pseudo directory".
+A directory can also be a so called "pseudo directory". They can be used to
+avoid ambiguity when there are multiple modules with the same path.
 
 There are two pseudo directories:
 
diff --git a/doc/mytest.cfg b/doc/mytest.cfg
index be1118c46..1385e493d 100644
--- a/doc/mytest.cfg
+++ b/doc/mytest.cfg
@@ -3,6 +3,7 @@
 
 [Common]
 cc=gcc     # '=' and ':' are the same
+--foo="bar"   # '--cc' and 'cc' are the same, 'bar' and '"bar"' are the same
 --verbose
 
 [Windows]
diff --git a/koch.nim b/koch.nim
index 9411a95de..ad6a774e8 100644
--- a/koch.nim
+++ b/koch.nim
@@ -50,6 +50,7 @@ Boot options:
 
 Commands for core developers:
   web [options]            generates the website and the full documentation
+                           (see `nimweb.nim` for cmd line options)
   website [options]        generates only the website
   csource -d:release       builds the C sources for installation
   pdf                      builds the PDF documentation
@@ -241,7 +242,15 @@ proc zip(args: string) =
   exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" %
        ["tools/niminst/niminst".exe, VersionAsString])
 
+proc ensureCleanGit() =
+   let (outp, status) = osproc.execCmdEx("git diff")
+   if outp.len != 0:
+     quit "Not a clean git repository; 'git diff' not empty!"
+   if status != 0:
+     quit "Not a clean git repository; 'git diff' returned non-zero!"
+
 proc xz(args: string) =
+  ensureCleanGit()
   bundleNimbleSrc()
   bundleNimsuggest(false)
   nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 8a1be3720..d4e8ada0a 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -382,16 +382,24 @@ type
 
 {.deprecated: [TBindSymRule: BindSymRule].}
 
-proc bindSym*(ident: static[string], rule: BindSymRule = brClosed): NimNode {.
+proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {.
               magic: "NBindSym", noSideEffect.}
   ## creates a node that binds `ident` to a symbol node. The bound symbol
   ## may be an overloaded symbol.
+  ## if `ident` is a NimNode, it must have nkIdent kind.
   ## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is
   ## returned or ``nkSym`` if the symbol is not ambiguous.
   ## If ``rule == brOpen`` either an ``nkOpenSymChoice`` tree is
   ## returned or ``nkSym`` if the symbol is not ambiguous.
   ## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
   ## returned even if the symbol is not ambiguous.
+  ##
+  ## experimental feature:
+  ## use {.experimental: "dynamicBindSym".} to activate it
+  ## if called from template / regular code, `ident` and `rule` must be
+  ## constant expression / literal value.
+  ## if called from macros / compile time procs / static blocks,
+  ## `ident` and `rule` can be VM computed value.
 
 proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
   magic: "NGenSym", noSideEffect.}
@@ -425,6 +433,7 @@ proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
 proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.}
 
 proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} =
+  ## returns ``LineInfo`` of ``n``, using absolute path for ``filename``
   result.filename = n.getFile
   result.line = n.getLine
   result.column = n.getColumn
@@ -1284,7 +1293,7 @@ proc customPragmaNode(n: NimNode): NimNode =
   let
     typ = n.getTypeInst()
 
-  if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy: 
+  if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy:
     return typ[1][1]
   elif typ.typeKind == ntyTypeDesc:
     let impl = typ[1].getImpl()
@@ -1319,6 +1328,8 @@ proc customPragmaNode(n: NimNode): NimNode =
           if identDefs.kind == nnkRecCase:
             identDefsStack.add(identDefs[0])
             for i in 1..<identDefs.len:
+              # if it is and empty branch, skip
+              if identDefs[i][0].kind == nnkNilLit: continue
               if identDefs[i][1].kind == nnkIdentDefs:
                 identDefsStack.add(identDefs[i][1])
               else: # nnkRecList
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index ef456f093..43a429a17 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -449,10 +449,11 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
         desc = if not symbols[j].linkDesc.isNil: symbols[j].linkDesc else: ""
       if desc.len > 0:
         result.addf("""<li><a class="reference external"
-          title="$3" href="$1">$2</a></li>
+          title="$3" data-doc-search-tag="$2" href="$1">$2</a></li>
           """, [url, text, desc])
       else:
-        result.addf("""<li><a class="reference external" href="$1">$2</a></li>
+        result.addf("""<li><a class="reference external" 
+          data-doc-search-tag="$2" href="$1">$2</a></li>
           """, [url, text])
       inc j
     result.add("</ul></dd>\n")
@@ -493,6 +494,7 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
   # Build a list of levels and extracted titles to make processing easier.
   var
     titleRef: string
+    titleTag: string
     levels: seq[tuple[level: int, text: string]]
     L = 0
     level = 1
@@ -519,10 +521,12 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
     let link = entries[L].link
     if link.isDocumentationTitle:
       titleRef = link
+      titleTag = levels[L].text
     else:
       result.add(level.indentToLevel(levels[L].level))
-      result.add("<li><a href=\"" & link & "\">" &
-        levels[L].text & "</a></li>\n")
+      result.addf("""<li><a class="reference" data-doc-search-tag="$1" href="$2"> 
+        $3</a></li>
+        """, [titleTag & " : " & levels[L].text, link, levels[L].text])
     inc L
   result.add(level.indentToLevel(1) & "</ul>\n")
   assert(not titleRef.isNil,
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index 44c59c627..db33e41af 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -854,20 +854,20 @@ when isMainModule:
     doAssert numbers.distribute(6)[0] == @[1, 2]
     doAssert numbers.distribute(6)[5] == @[7]
     let a = @[1, 2, 3, 4, 5, 6, 7]
-    doAssert a.distribute(1, true)   == @[@[1, 2, 3, 4, 5, 6, 7]]
-    doAssert a.distribute(1, false)  == @[@[1, 2, 3, 4, 5, 6, 7]]
-    doAssert a.distribute(2, true)   == @[@[1, 2, 3, 4], @[5, 6, 7]]
-    doAssert a.distribute(2, false)  == @[@[1, 2, 3, 4], @[5, 6, 7]]
-    doAssert a.distribute(3, true)   == @[@[1, 2, 3], @[4, 5], @[6, 7]]
-    doAssert a.distribute(3, false)  == @[@[1, 2, 3], @[4, 5, 6], @[7]]
-    doAssert a.distribute(4, true)   == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
-    doAssert a.distribute(4, false)  == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
-    doAssert a.distribute(5, true)   == @[@[1, 2], @[3, 4], @[5], @[6], @[7]]
-    doAssert a.distribute(5, false)  == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]]
-    doAssert a.distribute(6, true)   == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]]
-    doAssert a.distribute(6, false)  == @[
+    doAssert a.distribute(1, true) == @[@[1, 2, 3, 4, 5, 6, 7]]
+    doAssert a.distribute(1, false) == @[@[1, 2, 3, 4, 5, 6, 7]]
+    doAssert a.distribute(2, true) == @[@[1, 2, 3, 4], @[5, 6, 7]]
+    doAssert a.distribute(2, false) == @[@[1, 2, 3, 4], @[5, 6, 7]]
+    doAssert a.distribute(3, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
+    doAssert a.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]]
+    doAssert a.distribute(4, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
+    doAssert a.distribute(4, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
+    doAssert a.distribute(5, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]]
+    doAssert a.distribute(5, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]]
+    doAssert a.distribute(6, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]]
+    doAssert a.distribute(6, false) == @[
       @[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]]
-    doAssert a.distribute(8, false)  == a.distribute(8, true)
+    doAssert a.distribute(8, false) == a.distribute(8, true)
     doAssert a.distribute(90, false) == a.distribute(90, true)
     var b = @[0]
     for f in 1 .. 25: b.add(f)
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index 59c90bc2b..fdc3b4b03 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -347,6 +347,18 @@ proc excl*[A](s: var HashSet[A], other: HashSet[A]) =
   assert other.isValid, "The set `other` needs to be initialized."
   for item in other: discard exclImpl(s, item)
 
+proc pop*[A](s: var HashSet[A]): A =
+  ## Remove and return an arbitrary element from the set `s`.
+  ##
+  ## Raises KeyError if the set `s` is empty.
+  ##
+  for h in 0..high(s.data):
+    if isFilled(s.data[h].hcode):
+      result = s.data[h].key
+      excl(s, result)
+      return result
+  raise newException(KeyError, "set is empty")
+
 proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
   ## Includes `key` in the set `s` and tells if `key` was added to `s`.
   ##
diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim
index c67cd7579..3c1cf73f4 100644
--- a/lib/pure/encodings.nim
+++ b/lib/pure/encodings.nim
@@ -332,7 +332,7 @@ when defined(windows):
     if s.len == 0: return ""
     # educated guess of capacity:
     var cap = s.len + s.len shr 2
-    result = newStringOfCap(cap*2)
+    result = newString(cap*2)
     # convert to utf-16 LE
     var m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
                                 lpMultiByteStr = cstring(s),
@@ -347,7 +347,7 @@ when defined(windows):
                                 lpWideCharStr = nil,
                                 cchWideChar = cint(0))
       # and do the conversion properly:
-      result = newStringOfCap(cap*2)
+      result = newString(cap*2)
       m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
                               lpMultiByteStr = cstring(s),
                               cbMultiByte = cint(s.len),
@@ -364,7 +364,7 @@ when defined(windows):
     if int(c.dest) == 1200: return
     # otherwise the fun starts again:
     cap = s.len + s.len shr 2
-    var res = newStringOfCap(cap)
+    var res = newString(cap)
     m = wideCharToMultiByte(
       codePage = c.dest,
       dwFlags = 0'i32,
@@ -382,7 +382,7 @@ when defined(windows):
         lpMultiByteStr = nil,
         cbMultiByte = cint(0))
       # and do the conversion properly:
-      res = newStringOfCap(cap)
+      res = newString(cap)
       m = wideCharToMultiByte(
         codePage = c.dest,
         dwFlags = 0'i32,
diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim
index eb350cbd4..493e8e174 100644
--- a/lib/pure/includes/oserr.nim
+++ b/lib/pure/includes/oserr.nim
@@ -60,7 +60,7 @@ proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
   if additionalInfo.len == 0:
     e.msg = osErrorMsg(errorCode)
   else:
-    e.msg = osErrorMsg(errorCode) & "\nAdditional info: " & additionalInfo
+    e.msg = osErrorMsg(errorCode) & "\nAdditional info: '" & additionalInfo & "'"
   if e.msg == "":
     e.msg = "unknown OS error"
   raise e
diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim
index 36145abc7..8b3f14f34 100644
--- a/lib/pure/ioselects/ioselectors_epoll.nim
+++ b/lib/pure/ioselects/ioselectors_epoll.nim
@@ -92,6 +92,9 @@ proc newSelector*[T](): Selector[T] =
     result.maxFD = maxFD
     result.fds = newSeq[SelectorKey[T]](maxFD)
 
+  for i in 0 ..< maxFD:
+    result.fds[i].ident = InvalidIdent
+
 proc close*[T](s: Selector[T]) =
   let res = posix.close(s.epollFD)
   when hasThreadSupport:
@@ -100,12 +103,6 @@ proc close*[T](s: Selector[T]) =
   if res != 0:
     raiseIOSelectorsError(osLastError())
 
-template clearKey[T](key: ptr SelectorKey[T]) =
-  var empty: T
-  key.ident = 0
-  key.events = {}
-  key.data = empty
-
 proc newSelectEvent*(): SelectEvent =
   let fdci = eventfd(0, 0)
   if fdci == -1:
@@ -135,7 +132,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
                         events: set[Event], data: T) =
   let fdi = int(fd)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0, "Descriptor $# already registered" % $fdi)
+  doAssert(s.fds[fdi].ident == InvalidIdent, "Descriptor $# already registered" % $fdi)
   s.setKey(fdi, events, 0, data)
   if events != {}:
     var epv = EpollEvent(events: EPOLLRDHUP)
@@ -152,7 +149,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle, events: set[Event]
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor $# is not registered in the selector!" % $fdi)
   doAssert(pkey.events * maskEvents == {})
   if pkey.events != events:
@@ -180,7 +177,7 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor $# is not registered in the selector!" % $fdi)
   if pkey.events != {}:
     when not defined(android):
@@ -243,7 +240,7 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
   let fdi = int(ev.efd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0, "Event is not registered in the queue!")
+  doAssert(pkey.ident != InvalidIdent, "Event is not registered in the queue!")
   doAssert(Event.User in pkey.events)
   var epv = EpollEvent()
   if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) != 0:
@@ -262,7 +259,7 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
   setNonBlocking(fdi.cint)
 
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
 
   var events = {Event.Timer}
   var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
@@ -307,7 +304,7 @@ when not defined(android):
     setNonBlocking(fdi.cint)
 
     s.checkFd(fdi)
-    doAssert(s.fds[fdi].ident == 0)
+    doAssert(s.fds[fdi].ident == InvalidIdent)
 
     var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
     epv.data.u64 = fdi.uint
@@ -334,7 +331,7 @@ when not defined(android):
     setNonBlocking(fdi.cint)
 
     s.checkFd(fdi)
-    doAssert(s.fds[fdi].ident == 0)
+    doAssert(s.fds[fdi].ident == InvalidIdent)
 
     var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
     epv.data.u64 = fdi.uint
@@ -347,7 +344,7 @@ when not defined(android):
 
 proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
   let fdi = int(ev.efd)
-  doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!")
+  doAssert(s.fds[fdi].ident == InvalidIdent, "Event is already registered in the queue!")
   s.setKey(fdi, {Event.User}, 0, data)
   var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
   epv.data.u64 = ev.efd.uint
@@ -381,7 +378,7 @@ proc selectInto*[T](s: Selector[T], timeout: int,
       let fdi = int(resTable[i].data.u64)
       let pevents = resTable[i].events
       var pkey = addr(s.fds[fdi])
-      doAssert(pkey.ident != 0)
+      doAssert(pkey.ident != InvalidIdent)
       var rkey = ReadyKey(fd: fdi, events: {})
 
       if (pevents and EPOLLERR) != 0 or (pevents and EPOLLHUP) != 0:
@@ -482,7 +479,7 @@ template isEmpty*[T](s: Selector[T]): bool =
   (s.count == 0)
 
 proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
-  return s.fds[fd.int].ident != 0
+  return s.fds[fd.int].ident != InvalidIdent
 
 proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
   let fdi = int(fd)
diff --git a/lib/pure/ioselects/ioselectors_kqueue.nim b/lib/pure/ioselects/ioselectors_kqueue.nim
index 10e23c072..142e988d0 100644
--- a/lib/pure/ioselects/ioselectors_kqueue.nim
+++ b/lib/pure/ioselects/ioselectors_kqueue.nim
@@ -114,6 +114,9 @@ proc newSelector*[T](): Selector[T] =
     result.fds = newSeq[SelectorKey[T]](maxFD)
     result.changes = newSeqOfCap[KEvent](MAX_KQUEUE_EVENTS)
 
+  for i in 0 ..< maxFD:
+    result.fds[i].ident = InvalidIdent
+
   result.sock = usock
   result.kqFD = kqFD
   result.maxFD = maxFD.int
@@ -128,12 +131,6 @@ proc close*[T](s: Selector[T]) =
   if res1 != 0 or res2 != 0:
     raiseIOSelectorsError(osLastError())
 
-template clearKey[T](key: ptr SelectorKey[T]) =
-  var empty: T
-  key.ident = 0
-  key.events = {}
-  key.data = empty
-
 proc newSelectEvent*(): SelectEvent =
   var fds: array[2, cint]
   if posix.pipe(fds) != 0:
@@ -221,7 +218,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
                         events: set[Event], data: T) =
   let fdi = int(fd)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
   s.setKey(fdi, events, 0, data)
 
   if events != {}:
@@ -242,7 +239,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor $# is not registered in the queue!" % $fdi)
   doAssert(pkey.events * maskEvents == {})
 
@@ -269,7 +266,7 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
                        data: T): int {.discardable.} =
   let fdi = getUnique(s)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
 
   let events = if oneshot: {Event.Timer, Event.Oneshot} else: {Event.Timer}
   let flags: cushort = if oneshot: EV_ONESHOT or EV_ADD else: EV_ADD
@@ -291,7 +288,7 @@ proc registerSignal*[T](s: Selector[T], signal: int,
                         data: T): int {.discardable.} =
   let fdi = getUnique(s)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
 
   s.setKey(fdi, {Event.Signal}, signal, data)
   var nmask, omask: Sigset
@@ -315,7 +312,7 @@ proc registerProcess*[T](s: Selector[T], pid: int,
                          data: T): int {.discardable.} =
   let fdi = getUnique(s)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
 
   var kflags: cushort = EV_ONESHOT or EV_ADD
   setKey(s, fdi, {Event.Process, Event.Oneshot}, pid, data)
@@ -331,7 +328,7 @@ proc registerProcess*[T](s: Selector[T], pid: int,
 
 proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
   let fdi = ev.rfd.int
-  doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!")
+  doAssert(s.fds[fdi].ident == InvalidIdent, "Event is already registered in the queue!")
   setKey(s, fdi, {Event.User}, 0, data)
 
   modifyKQueue(s, fdi.uint, EVFILT_READ, EV_ADD, 0, 0, nil)
@@ -374,7 +371,7 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor [" & $fdi & "] is not registered in the queue!")
 
   if pkey.events != {}:
@@ -434,7 +431,7 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
   let fdi = int(ev.rfd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0, "Event is not registered in the queue!")
+  doAssert(pkey.ident != InvalidIdent, "Event is not registered in the queue!")
   doAssert(Event.User in pkey.events)
   modifyKQueue(s, uint(fdi), EVFILT_READ, EV_DELETE, 0, 0, nil)
   when not declared(CACHE_EVENTS):
@@ -593,7 +590,7 @@ template isEmpty*[T](s: Selector[T]): bool =
   (s.count == 0)
 
 proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
-  return s.fds[fd.int].ident != 0
+  return s.fds[fd.int].ident != InvalidIdent
 
 proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
   let fdi = int(fd)
diff --git a/lib/pure/ioselects/ioselectors_poll.nim b/lib/pure/ioselects/ioselectors_poll.nim
index c36750c8d..9d708b0c1 100644
--- a/lib/pure/ioselects/ioselectors_poll.nim
+++ b/lib/pure/ioselects/ioselectors_poll.nim
@@ -70,6 +70,9 @@ proc newSelector*[T](): Selector[T] =
     result.fds = newSeq[SelectorKey[T]](maxFD)
     result.pollfds = newSeq[TPollFd](maxFD)
 
+  for i in 0 ..< maxFD:
+    result.fds[i].ident = InvalidIdent
+
 proc close*[T](s: Selector[T]) =
   when hasThreadSupport:
     deinitLock(s.lock)
@@ -77,12 +80,6 @@ proc close*[T](s: Selector[T]) =
     deallocSharedArray(s.pollfds)
     deallocShared(cast[pointer](s))
 
-template clearKey[T](key: ptr SelectorKey[T]) =
-  var empty: T
-  key.ident = 0
-  key.events = {}
-  key.data = empty
-
 template pollAdd[T](s: Selector[T], sock: cint, events: set[Event]) =
   withPollLock(s):
     var pollev: cshort = 0
@@ -135,7 +132,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
                         events: set[Event], data: T) =
   var fdi = int(fd)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
   setKey(s, fdi, events, 0, data)
   if events != {}: s.pollAdd(fdi.cint, events)
 
@@ -146,7 +143,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor [" & $fdi & "] is not registered in the queue!")
   doAssert(pkey.events * maskEvents == {})
 
@@ -162,7 +159,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
 
 proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
   var fdi = int(ev.rfd)
-  doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!")
+  doAssert(s.fds[fdi].ident == InvalidIdent, "Event is already registered in the queue!")
   var events = {Event.User}
   setKey(s, fdi, events, 0, data)
   events.incl(Event.Read)
@@ -172,9 +169,9 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor [" & $fdi & "] is not registered in the queue!")
-  pkey.ident = 0
+  pkey.ident = InvalidIdent
   pkey.events = {}
   s.pollRemove(fdi.cint)
 
@@ -182,9 +179,9 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
   let fdi = int(ev.rfd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0, "Event is not registered in the queue!")
+  doAssert(pkey.ident != InvalidIdent, "Event is not registered in the queue!")
   doAssert(Event.User in pkey.events)
-  pkey.ident = 0
+  pkey.ident = InvalidIdent
   pkey.events = {}
   s.pollRemove(fdi.cint)
 
@@ -270,7 +267,7 @@ template isEmpty*[T](s: Selector[T]): bool =
   (s.count == 0)
 
 proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
-  return s.fds[fd.int].ident != 0
+  return s.fds[fd.int].ident != InvalidIdent
 
 proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
   let fdi = int(fd)
diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim
index 7ed250307..cd6a72b44 100644
--- a/lib/pure/ioselects/ioselectors_select.nim
+++ b/lib/pure/ioselects/ioselectors_select.nim
@@ -99,6 +99,9 @@ proc newSelector*[T](): Selector[T] =
     result = Selector[T]()
     result.fds = newSeq[SelectorKey[T]](FD_SETSIZE)
 
+  for i in 0 ..< FD_SETSIZE:
+    result.fds[i].ident = InvalidIdent
+
   IOFD_ZERO(addr result.rSet)
   IOFD_ZERO(addr result.wSet)
   IOFD_ZERO(addr result.eSet)
@@ -195,7 +198,7 @@ proc setSelectKey[T](s: Selector[T], fd: SocketHandle, events: set[Event],
   var i = 0
   let fdi = int(fd)
   while i < FD_SETSIZE:
-    if s.fds[i].ident == 0:
+    if s.fds[i].ident == InvalidIdent:
       var pkey = addr(s.fds[i])
       pkey.ident = fdi
       pkey.events = events
@@ -221,7 +224,7 @@ proc delKey[T](s: Selector[T], fd: SocketHandle) =
   var i = 0
   while i < FD_SETSIZE:
     if s.fds[i].ident == fd.int:
-      s.fds[i].ident = 0
+      s.fds[i].ident = InvalidIdent
       s.fds[i].events = {}
       s.fds[i].data = empty
       break
@@ -335,7 +338,7 @@ proc selectInto*[T](s: Selector[T], timeout: int,
     var k = 0
 
     while (i < FD_SETSIZE) and (k < count):
-      if s.fds[i].ident != 0:
+      if s.fds[i].ident != InvalidIdent:
         var flag = false
         var pkey = addr(s.fds[i])
         var rkey = ReadyKey(fd: int(pkey.ident), events: {})
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index 67f92dffe..b9279b18c 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -1004,6 +1004,13 @@ proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType,
       exprColonExpr.add(ifStmt)
 
 proc createConstructor(typeSym, jsonNode: NimNode): NimNode {.compileTime.}
+
+proc detectDistinctType(typeSym: NimNode): NimNode =
+  let
+    typeImpl = getTypeImpl(typeSym)
+    typeInst = getTypeInst(typeSym)
+  result = if typeImpl.typeKind == ntyDistinct: typeImpl else: typeInst
+
 proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
   ## Process a field from a ``RecList``.
   ##
@@ -1022,8 +1029,8 @@ proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
     # Add the field value.
     # -> jsonNode["`field`"]
     let indexedJsonNode = createJsonIndexer(jsonNode, $field)
-    exprColonExpr.add(createConstructor(getTypeInst(field), indexedJsonNode))
-
+    let typeNode = detectDistinctType(field)
+    exprColonExpr.add(createConstructor(typeNode, indexedJsonNode))
   of nnkRecCase:
     # A "case" field that introduces a variant.
     let exprColonExpr = newNimNode(nnkExprColonExpr)
@@ -1248,7 +1255,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
       let seqT = typeSym[1]
       let forLoopI = genSym(nskForVar, "i")
       let indexerNode = createJsonIndexer(jsonNode, forLoopI)
-      let constructorNode = createConstructor(seqT, indexerNode)
+      let constructorNode = createConstructor(detectDistinctType(seqT), indexerNode)
 
       # Create a statement expression containing a for loop.
       result = quote do:
@@ -1284,7 +1291,10 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
 
     # Handle all other types.
     let obj = getType(typeSym)
-    if obj.kind == nnkBracketExpr:
+    let typeNode = getTypeImpl(typeSym)
+    if typeNode.typeKind == ntyDistinct:
+      result = createConstructor(typeNode, jsonNode)
+    elif obj.kind == nnkBracketExpr:
       # When `Sym "Foo"` turns out to be a `ref object`.
       result = createConstructor(obj, jsonNode)
     else:
@@ -1295,6 +1305,21 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
     # TODO: The fact that `jsonNode` here works to give a good line number
     # is weird. Specifying typeSym should work but doesn't.
     error("Use a named tuple instead of: " & $toStrLit(typeSym), jsonNode)
+  of nnkDistinctTy:
+    var baseType = typeSym
+    # solve nested distinct types
+    while baseType.typeKind == ntyDistinct:
+      let impl = getTypeImpl(baseType[0])
+      if impl.typeKind != ntyDistinct:
+        baseType = baseType[0]
+        break
+      baseType = impl
+    let ret = createConstructor(baseType, jsonNode)
+    let typeInst = getTypeInst(typeSym)
+    result = quote do:
+      (
+        `typeInst`(`ret`)
+      )
   else:
     doAssert false, "Unable to create constructor for: " & $typeSym.kind
 
@@ -1418,7 +1443,7 @@ macro to*(node: JsonNode, T: typedesc): untyped =
   ##     doAssert data.person.age == 21
   ##     doAssert data.list == @[1, 2, 3, 4]
 
-  let typeNode = getTypeInst(T)
+  let typeNode = getTypeImpl(T)
   expectKind(typeNode, nnkBracketExpr)
   doAssert(($typeNode[0]).normalize == "typedesc")
 
diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim
index ff69ba61e..8f5f3a183 100644
--- a/lib/pure/mimetypes.nim
+++ b/lib/pure/mimetypes.nim
@@ -231,6 +231,7 @@ const mimes* = {
     "xcf": "application/x-xcf",
     "fig": "application/x-xfig",
     "xpi": "application/x-xpinstall",
+    "wasm": "application/wasm",
     "amr": "audio/amr",
     "awb": "audio/amr-wb",
     "amr": "audio/amr",
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 5d2efebee..771e7de10 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -41,7 +41,7 @@
 ## immediately.
 ##
 ## .. code-block:: Nim
-##   var socket = newSocket()
+##   var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
 ##   socket.sendTo("192.168.0.1", Port(27960), "status\n")
 ##
 ## Creating a server
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 84f492c9d..9cc83c372 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -971,7 +971,7 @@ proc rawCreateDir(dir: string): bool =
     elif errno in {EEXIST, ENOSYS}:
       result = false
     else:
-      raiseOSError(osLastError())
+      raiseOSError(osLastError(), dir)
   elif defined(posix):
     let res = mkdir(dir, 0o777)
     if res == 0'i32:
@@ -980,7 +980,7 @@ proc rawCreateDir(dir: string): bool =
       result = false
     else:
       #echo res
-      raiseOSError(osLastError())
+      raiseOSError(osLastError(), dir)
   else:
     when useWinUnicode:
       wrapUnary(res, createDirectoryW, dir)
@@ -992,7 +992,7 @@ proc rawCreateDir(dir: string): bool =
     elif getLastError() == 183'i32:
       result = false
     else:
-      raiseOSError(osLastError())
+      raiseOSError(osLastError(), dir)
 
 proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1",
   tags: [WriteDirEffect, ReadDirEffect].} =
@@ -1005,7 +1005,7 @@ proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1",
   if result:
     # path already exists - need to check that it is indeed a directory
     if not existsDir(dir):
-      raise newException(IOError, "Failed to create the directory")
+      raise newException(IOError, "Failed to create '" & dir & "'")
 
 proc createDir*(dir: string) {.rtl, extern: "nos$1",
   tags: [WriteDirEffect, ReadDirEffect].} =
diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim
index 4ae5afd6c..a7ebd9d15 100644
--- a/lib/pure/ospaths.nim
+++ b/lib/pure/ospaths.nim
@@ -147,7 +147,7 @@ else: # UNIX-like operating system
     DirSep* = '/'
     AltSep* = DirSep
     PathSep* = ':'
-    FileSystemCaseSensitive* = true
+    FileSystemCaseSensitive* = when defined(macosx): false else: true
     ExeExt* = ""
     ScriptExt* = ""
     DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so"
@@ -410,6 +410,11 @@ proc cmpPaths*(pathA, pathB: string): int {.
   ## | 0 iff pathA == pathB
   ## | < 0 iff pathA < pathB
   ## | > 0 iff pathA > pathB
+  runnableExamples:
+    when defined(macosx):
+      doAssert cmpPaths("foo", "Foo") == 0
+    elif defined(posix):
+      doAssert cmpPaths("foo", "Foo") > 0
   if FileSystemCaseSensitive:
     result = cmp(pathA, pathB)
   else:
diff --git a/lib/pure/random.nim b/lib/pure/random.nim
index 01ea9c845..e565fccf8 100644
--- a/lib/pure/random.nim
+++ b/lib/pure/random.nim
@@ -110,7 +110,7 @@ proc random*[T](a: openArray[T]): T {.deprecated.} =
   ## Use ``rand`` instead.
   result = a[random(a.low..a.len)]
 
-proc rand*(r: var Rand; max: int): int {.benign.} =
+proc rand*(r: var Rand; max: Natural): int {.benign.} =
   ## Returns a random number in the range 0..max. The sequence of
   ## random number is always the same, unless `randomize` is called
   ## which initializes the random number generator with a "random"
@@ -128,7 +128,7 @@ proc rand*(max: int): int {.benign.} =
   ## number, i.e. a tickcount.
   rand(state, max)
 
-proc rand*(r: var Rand; max: float): float {.benign.} =
+proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} =
   ## Returns a random number in the range 0..max. The sequence of
   ## random number is always the same, unless `randomize` is called
   ## which initializes the random number generator with a "random"
@@ -218,4 +218,17 @@ when isMainModule:
     doAssert rand(0) == 0
     doAssert rand("a") == 'a'
 
+    when compileOption("rangeChecks"):
+      try:
+        discard rand(-1)
+        doAssert false
+      except RangeError:
+        discard
+
+      try:
+        discard rand(-1.0)
+        doAssert false
+      except RangeError:
+        discard
+
   main()
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index 935250e08..e4c2b2124 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -261,6 +261,9 @@ else:
       param: int
       data: T
 
+  const
+    InvalidIdent = -1
+
   proc raiseIOSelectorsError[T](message: T) =
     var msg = ""
     when T is string:
@@ -302,6 +305,12 @@ else:
         if posix.sigprocmask(SIG_UNBLOCK, newmask, oldmask) == -1:
           raiseIOSelectorsError(osLastError())
 
+  template clearKey[T](key: ptr SelectorKey[T]) =
+    var empty: T
+    key.ident = InvalidIdent
+    key.events = {}
+    key.data = empty
+
   when defined(linux):
     include ioselects/ioselectors_epoll
   elif bsdPlatform:
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index bc4de7ee4..cdb7a4466 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -181,10 +181,27 @@ elif defined(windows):
 type
   Month* = enum ## Represents a month. Note that the enum starts at ``1``, so ``ord(month)`` will give
                 ## the month number in the range ``[1..12]``.
-    mJan = 1, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
+    mJan = (1, "January")
+    mFeb = "February"
+    mMar = "March"
+    mApr = "April"
+    mMay = "May"
+    mJun = "June"
+    mJul = "July"
+    mAug = "August"
+    mSep = "September"
+    mOct = "October"
+    mNov = "November"
+    mDec = "December"
 
   WeekDay* = enum ## Represents a weekday.
-    dMon, dTue, dWed, dThu, dFri, dSat, dSun
+    dMon = "Monday"
+    dTue = "Tuesday"
+    dWed = "Wednesday"
+    dThu = "Thursday"
+    dFri = "Friday"
+    dSat = "Saturday"
+    dSun = "Sunday"
 
   MonthdayRange* = range[1..31]
   HourRange* = range[0..23]
@@ -1074,20 +1091,6 @@ proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
   result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
     ':' & intToStr(ti.second, 2)
 
-proc `$`*(day: WeekDay): string =
-  ## Stringify operator for ``WeekDay``.
-  const lookup: array[WeekDay, string] = ["Monday", "Tuesday", "Wednesday",
-     "Thursday", "Friday", "Saturday", "Sunday"]
-  return lookup[day]
-
-proc `$`*(m: Month): string =
-  ## Stringify operator for ``Month``.
-  const lookup: array[Month, string] = ["January", "February", "March",
-      "April", "May", "June", "July", "August", "September", "October",
-      "November", "December"]
-  return lookup[m]
-
-
 proc toParts* (ti: TimeInterval): TimeIntervalParts =
   ## Converts a `TimeInterval` into an array consisting of its time units,
   ## starting with nanoseconds and ending with years
diff --git a/lib/system.nim b/lib/system.nim
index 29e357c68..c033a632b 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1479,8 +1479,8 @@ when defined(nimdoc):
     ##
     ## Note that this is a *runtime* call and using ``quit`` inside a macro won't
     ## have any compile time effect. If you need to stop the compiler inside a
-    ## macro, use the `error <manual.html#error-pragma>`_ or `fatal
-    ## <manual.html#fatal-pragma>`_ pragmas.
+    ## macro, use the `error <manual.html#pragmas-error-pragma>`_ or `fatal
+    ## <manual.html#pragmas-fatal-pragma>`_ pragmas.
 
 elif defined(genode):
   include genode/env
@@ -2438,7 +2438,7 @@ iterator fieldPairs*[T: tuple|object](x: T): RootObj {.
   ## When you iterate over objects with different field types you have to use
   ## the compile time ``when`` instead of a runtime ``if`` to select the code
   ## you want to run for each type. To perform the comparison use the `is
-  ## operator <manual.html#is-operator>`_. Example:
+  ## operator <manual.html#generics-is-operator>`_. Example:
   ##
   ## .. code-block:: Nim
   ##
@@ -3788,7 +3788,9 @@ template doAssert*(cond: bool, msg = "") =
   ## same as `assert` but is always turned on and not affected by the
   ## ``--assertions`` command line switch.
   bind instantiationInfo
-  {.line: instantiationInfo().}:
+  # NOTE: `true` is correct here; --excessiveStackTrace:on will control whether
+  # or not to output full paths.
+  {.line: instantiationInfo(-1, true).}:
     if not cond:
       raiseAssert(astToStr(cond) & ' ' &
                   instantiationInfo(-1, false).fileName & '(' &
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index 95becde22..ffb7aaf86 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -116,6 +116,8 @@ type
     nextChunkSize: int
     bottomData: AvlNode
     heapLinks: HeapLinks
+    when defined(nimTypeNames):
+      allocCounter, deallocCounter: int
 
 const
   fsLookupTable: array[byte, int8] = [
@@ -434,8 +436,9 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
         a.nextChunkSize = PageSize*4
       else:
         a.nextChunkSize = min(roundup(usedMem shr 2, PageSize), a.nextChunkSize * 2)
-  var size = size
+        a.nextChunkSize = min(a.nextChunkSize, MaxBigChunkSize)
 
+  var size = size
   if size > a.nextChunkSize:
     result = cast[PBigChunk](osAllocPages(size))
   else:
@@ -737,6 +740,8 @@ when false:
         result = nil
 
 proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
+  when defined(nimTypeNames):
+    inc(a.allocCounter)
   sysAssert(allocInv(a), "rawAlloc: begin")
   sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
   sysAssert(requestedSize >= sizeof(FreeCell), "rawAlloc: requested size too small")
@@ -810,6 +815,8 @@ proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
   zeroMem(result, requestedSize)
 
 proc rawDealloc(a: var MemRegion, p: pointer) =
+  when defined(nimTypeNames):
+    inc(a.deallocCounter)
   #sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer")
   sysAssert(allocInv(a), "rawDealloc: begin")
   var c = pageAddr(p)
@@ -975,6 +982,10 @@ proc getOccupiedMem(a: MemRegion): int {.inline.} =
   result = a.occ
   # a.currMem - a.freeMem
 
+when defined(nimTypeNames):
+  proc getMemCounters(a: MemRegion): (int, int) {.inline.} =
+    (a.allocCounter, a.deallocCounter)
+
 # ---------------------- thread memory region -------------------------------
 
 template instantiateForRegion(allocator: untyped) =
@@ -1018,6 +1029,9 @@ template instantiateForRegion(allocator: untyped) =
   proc getOccupiedMem(): int = return allocator.occ #getTotalMem() - getFreeMem()
   proc getMaxMem*(): int = return getMaxMem(allocator)
 
+  when defined(nimTypeNames):
+    proc getMemCounters*(): (int, int) = getMemCounters(allocator)
+
   # -------------------- shared heap region ----------------------------------
   when hasThreadSupport:
     var sharedHeap: MemRegion
diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim
index 4d21f8747..f593d4c99 100644
--- a/lib/system/ansi_c.nim
+++ b/lib/system/ansi_c.nim
@@ -27,6 +27,9 @@ proc c_strcmp(a, b: cstring): cint {.
   importc: "strcmp", header: "<string.h>", noSideEffect.}
 proc c_strlen(a: cstring): csize {.
   importc: "strlen", header: "<string.h>", noSideEffect.}
+proc c_abort() {.
+  importc: "abort", header: "<stdlib.h>", noSideEffect.}
+
 
 when defined(linux) and defined(amd64):
   type
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index 425963f3f..74ac68eea 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -548,7 +548,10 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
   gcTrace(res, csAllocated)
   track("growObj old", ol, 0)
   track("growObj new", res, newsize)
-  when reallyDealloc:
+  when defined(nimIncrSeqV3):
+    # since we steal the old seq's contents, we set the old length to 0.
+    cast[PGenericSeq](old).len = 0
+  elif reallyDealloc:
     sysAssert(allocInv(gch.region), "growObj before dealloc")
     if ol.refcount shr rcShift <=% 1:
       # free immediately to save space:
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index 711a610bf..dcea0c4cc 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -57,6 +57,9 @@ when defined(nimTypeNames):
     for i in 0 .. n-1:
       c_fprintf(stdout, "[Heap] %s: #%ld; bytes: %ld\n", a[i][0], a[i][1], a[i][2])
     c_fprintf(stdout, "[Heap] total number of bytes: %ld\n", totalAllocated)
+    when defined(nimTypeNames):
+      let (allocs, deallocs) = getMemCounters()
+      c_fprintf(stdout, "[Heap] allocs/deallocs: %ld/%ld\n", allocs, deallocs)
 
   when defined(nimGcRefLeak):
     proc oomhandler() =
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index e6b8ec3a6..3168e4cb2 100644
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -417,12 +417,18 @@ proc setStdIoUnbuffered() =
     discard c_setvbuf(stdin, nil, IONBF, 0)
 
 when declared(stdout):
+  when defined(windows) and compileOption("threads"):
+    var echoLock: SysLock
+    initSysLock echoLock
+
   proc echoBinSafe(args: openArray[string]) {.compilerProc.} =
     # flockfile deadlocks some versions of Android 5.x.x
     when not defined(windows) and not defined(android) and not defined(nintendoswitch):
       proc flockfile(f: File) {.importc, noDecl.}
       proc funlockfile(f: File) {.importc, noDecl.}
       flockfile(stdout)
+    when defined(windows) and compileOption("threads"):
+      acquireSys echoLock
     for s in args:
       discard c_fwrite(s.cstring, s.len, 1, stdout)
     const linefeed = "\n" # can be 1 or more chars
@@ -430,5 +436,7 @@ when declared(stdout):
     discard c_fflush(stdout)
     when not defined(windows) and not defined(android) and not defined(nintendoswitch):
       funlockfile(stdout)
+    when defined(windows) and compileOption("threads"):
+      releaseSys echoLock
 
 {.pop.}
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 9a73d3f9d..951435972 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -144,8 +144,13 @@ proc addChar(s: NimString, c: char): NimString =
     result = s
     if result.len >= result.space:
       let r = resize(result.space)
-      result = cast[NimString](growObj(result,
-        sizeof(TGenericSeq) + r + 1))
+      when defined(nimIncrSeqV3):
+        result = rawNewStringNoInit(r)
+        result.len = s.len
+        copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1)
+      else:
+        result = cast[NimString](growObj(result,
+          sizeof(TGenericSeq) + r + 1))
       result.reserved = r
   result.data[result.len] = c
   result.data[result.len+1] = '\0'
@@ -188,8 +193,13 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
   elif dest.len + addlen <= dest.space:
     result = dest
   else: # slow path:
-    var sp = max(resize(dest.space), dest.len + addlen)
-    result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1))
+    let sp = max(resize(dest.space), dest.len + addlen)
+    when defined(nimIncrSeqV3):
+      result = rawNewStringNoInit(sp)
+      result.len = dest.len
+      copyMem(addr result.data[0], unsafeAddr(dest.data[0]), dest.len+1)
+    else:
+      result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1))
     result.reserved = sp
     #result = rawNewString(sp)
     #copyMem(result, dest, dest.len + sizeof(TGenericSeq))
@@ -212,7 +222,15 @@ proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
   elif n <= s.space:
     result = s
   else:
-    result = resizeString(s, n)
+    let sp = max(resize(s.space), newLen)
+    when defined(nimIncrSeqV3):
+      result = rawNewStringNoInit(sp)
+      result.len = s.len
+      copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1)
+      zeroMem(addr result.data[s.len], newLen - s.len)
+      result.reserved = sp
+    else:
+      result = resizeString(s, n)
   result.len = n
   result.data[n] = '\0'
 
@@ -242,6 +260,9 @@ proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
                                GenericSeqSize))
     result.reserved = r
 
+template `+!`(p: pointer, s: int): pointer =
+  cast[pointer](cast[int](p) +% s)
+
 proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} =
   if s == nil:
     result = cast[PGenericSeq](newSeq(typ, 1))
@@ -250,9 +271,16 @@ proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} =
     result = s
     if result.len >= result.space:
       let r = resize(result.space)
-      result = cast[PGenericSeq](growObj(result, typ.base.size * r +
+      when defined(nimIncrSeqV3):
+        result = cast[PGenericSeq](newSeq(typ, r))
+        result.len = s.len
+        copyMem(result +! GenericSeqSize, s +! GenericSeqSize, s.len * typ.base.size)
+        # since we steal the content from 's', it's crucial to set s's len to 0.
+        s.len = 0
+      else:
+        result = cast[PGenericSeq](growObj(result, typ.base.size * r +
                                 GenericSeqSize))
-      result.reserved = r
+        result.reserved = r
 
 proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
     compilerRtl, inl.} =
@@ -296,7 +324,40 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
 
 proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
     compilerRtl.} =
+  sysAssert typ.kind == tySequence, "setLengthSeqV2: type is not a seq"
   if s == nil:
     result = cast[PGenericSeq](newSeq(typ, newLen))
   else:
-    result = setLengthSeq(s, typ.base.size, newLen)
+    when defined(nimIncrSeqV3):
+      let elemSize = typ.base.size
+      if s.space < newLen:
+        let r = max(resize(s.space), newLen)
+        result = cast[PGenericSeq](newSeq(typ, r))
+        copyMem(result +! GenericSeqSize, s +! GenericSeqSize, s.len * elemSize)
+        # since we steal the content from 's', it's crucial to set s's len to 0.
+        s.len = 0
+      elif newLen < s.len:
+        result = s
+        # we need to decref here, otherwise the GC leaks!
+        when not defined(boehmGC) and not defined(nogc) and
+            not defined(gcMarkAndSweep) and not defined(gogc) and
+            not defined(gcRegions):
+          if ntfNoRefs notin typ.base.flags:
+            for i in newLen..result.len-1:
+              forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
+                                GenericSeqSize +% (i*%elemSize)),
+                                extGetCellType(result).base, waZctDecRef)
+
+        # XXX: zeroing out the memory can still result in crashes if a wiped-out
+        # cell is aliased by another pointer (ie proc parameter or a let variable).
+        # This is a tough problem, because even if we don't zeroMem here, in the
+        # presence of user defined destructors, the user will expect the cell to be
+        # "destroyed" thus creating the same problem. We can destoy the cell in the
+        # finalizer of the sequence, but this makes destruction non-deterministic.
+        zeroMem(cast[pointer](cast[ByteAddress](result) +% GenericSeqSize +%
+              (newLen*%elemSize)), (result.len-%newLen) *% elemSize)
+      else:
+        result = s
+      result.len = newLen
+    else:
+      result = setLengthSeq(s, typ.base.size, newLen)
diff --git a/tests/async/t6100.nim b/tests/async/t6100.nim
deleted file mode 100644
index b4dc0f146..000000000
--- a/tests/async/t6100.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-discard """
-  file: "t6100.nim"
-  exitcode: 0
-  output: "10000000"
-"""
-import asyncdispatch
-
-let done = newFuture[int]()
-done.complete(1)
-
-proc asyncSum: Future[int] {.async.} =
-  for _ in 1..10_000_000:
-    result += await done
-
-echo waitFor asyncSum()
\ No newline at end of file
diff --git a/tests/async/t7985.nim b/tests/async/t7985.nim
deleted file mode 100644
index 0365499d3..000000000
--- a/tests/async/t7985.nim
+++ /dev/null
@@ -1,19 +0,0 @@
-discard """
-  file: "t7985.nim"
-  exitcode: 0
-  output: "(value: 1)"
-"""
-import json, asyncdispatch
-
-proc getData(): Future[JsonNode] {.async.} =
-  result = %*{"value": 1}
-
-type
-  MyData = object
-    value: BiggestInt
-
-proc main() {.async.} =
-  let data = to(await(getData()), MyData)
-  echo data
-
-waitFor(main())
diff --git a/tests/async/tasync_misc.nim b/tests/async/tasync_misc.nim
new file mode 100644
index 000000000..695dcd98a
--- /dev/null
+++ b/tests/async/tasync_misc.nim
@@ -0,0 +1,47 @@
+discard """
+  exitcode: 0
+  output: "ok"
+"""
+
+import json, asyncdispatch
+block: #6100
+  let done = newFuture[int]()
+  done.complete(1)
+
+  proc asyncSum: Future[int] {.async.} =
+    for _ in 1..10_000_000:
+      result += await done
+
+  let res = waitFor asyncSum()
+  doAssert(res == 10000000)
+
+block: #7985
+  proc getData(): Future[JsonNode] {.async.} =
+    result = %*{"value": 1}
+
+  type
+    MyData = object
+      value: BiggestInt
+
+  proc main() {.async.} =
+    let data = to(await(getData()), MyData)
+    doAssert($data == "(value: 1)")
+
+  waitFor(main())
+
+block: #8399
+  proc bar(): Future[string] {.async.} = discard
+
+  proc foo(line: string) {.async.} =
+    var res =
+      case line[0]
+      of '+', '-': @[]
+      of '$': (let x = await bar(); @[""])
+      else:
+        nil
+
+    doAssert(res == @[""])
+
+  waitFor foo("$asd")
+
+echo "ok"
diff --git a/tests/closure/tclosure3.nim b/tests/closure/tclosure3.nim
index d5ffb5ab2..4de07bdb5 100644
--- a/tests/closure/tclosure3.nim
+++ b/tests/closure/tclosure3.nim
@@ -15,7 +15,7 @@ proc main =
       let val = s[i]()
       if val != $(i*i): echo "bug  ", val
 
-    if getOccupiedMem() > 3000_000: quit("still a leak!")
+    if getOccupiedMem() > 5000_000: quit("still a leak!")
   echo "success"
 
 main()
diff --git a/tests/errmsgs/t8434.nim b/tests/errmsgs/t8434.nim
new file mode 100644
index 000000000..60fe2e2df
--- /dev/null
+++ b/tests/errmsgs/t8434.nim
@@ -0,0 +1,16 @@
+discard """
+  errormsg: "type mismatch: got <byte, int literal(0)>"
+  nimout: '''but expected one of:
+proc fun0[T1: int | float |
+    object | array | seq](a1: T1; a2: int)
+  first type mismatch at position: 1
+  required type: T1: int or float or object or array or seq
+  but expression 'byte(1)' is of type: byte
+
+expression: fun0(byte(1), 0)
+'''
+"""
+
+proc fun0[T1:int|float|object|array|seq](a1:T1, a2:int)=discard
+
+fun0(byte(1), 0)
diff --git a/tests/fragmentation/tfragment_alloc.nim b/tests/fragmentation/tfragment_alloc.nim
index 5a44b7434..80341d2dc 100644
--- a/tests/fragmentation/tfragment_alloc.nim
+++ b/tests/fragmentation/tfragment_alloc.nim
@@ -16,9 +16,12 @@ proc main =
     dealloc p
  #   c_fprintf(stdout, "iteration: %ld size: %ld\n", i, size)
   when defined(cpu64):
-    # bug #7120
-    var x = alloc(((1 shl 29) - 4) * 8)
-    dealloc x
+    # see https://github.com/nim-lang/Nim/issues/8509
+    # this often made appveyor (on windows) fail with out of memory
+    when defined(posix):
+      # bug #7120
+      var x = alloc(((1 shl 29) - 4) * 8)
+      dealloc x
 
 main()
 
diff --git a/tests/gc/growobjcrash.nim b/tests/gc/growobjcrash.nim
index a16468c7e..07f92b8f4 100644
--- a/tests/gc/growobjcrash.nim
+++ b/tests/gc/growobjcrash.nim
@@ -14,7 +14,7 @@ proc handleRequest(query: string): StringTableRef =
   let x = foo
   result = x()
 
-const Limit = when compileOption("gc", "markAndSweep"): 5*1024*1024 else: 700_000
+const Limit = 5*1024*1024
 
 proc main =
   var counter = 0
diff --git a/tests/generics/t8403.nim b/tests/generics/t8403.nim
new file mode 100644
index 000000000..47ce9c452
--- /dev/null
+++ b/tests/generics/t8403.nim
@@ -0,0 +1,11 @@
+discard """
+  output: "6.0"
+"""
+
+proc sum*[T](s: seq[T], R: typedesc): R =
+  var sum: R = 0
+  for x in s:
+    sum += R(x)
+  return sum
+
+echo @[1, 2, 3].sum(float)
diff --git a/tests/generics/t8439.nim b/tests/generics/t8439.nim
new file mode 100644
index 000000000..69bd7cfcb
--- /dev/null
+++ b/tests/generics/t8439.nim
@@ -0,0 +1,10 @@
+discard """
+  output: "1"
+"""
+
+type
+  Cardinal = enum
+    north, east, south, west
+
+proc foo[cardinal: static[Cardinal]](): int = 1
+echo(foo[north]())
diff --git a/tests/iter/t338.nim b/tests/iter/t338.nim
new file mode 100644
index 000000000..dbced8b60
--- /dev/null
+++ b/tests/iter/t338.nim
@@ -0,0 +1,20 @@
+discard """
+  output: '''0
+1
+2
+3
+4
+'''
+"""
+
+proc moo(): iterator (): int =
+  iterator fooGen: int {.closure.} =
+    while true:
+      yield result
+      result.inc
+  return fooGen
+
+var foo = moo()
+
+for i in 0 .. 4:
+  echo foo()
diff --git a/tests/macros/tbindsym.nim b/tests/macros/tbindsym.nim
index 6289d3eb2..2abcd98ce 100644
--- a/tests/macros/tbindsym.nim
+++ b/tests/macros/tbindsym.nim
@@ -1,4 +1,9 @@
 discard """
+  msg: '''initApple
+deinitApple
+Coral
+enum
+  redCoral, blackCoral'''
   output: '''TFoo
 TBar'''
 """
@@ -23,3 +28,40 @@ macro test: untyped =
         bindSym("TBar"))
 
 test()
+
+# issue 7827, bindSym power up
+{.experimental: "dynamicBindSym".}
+type
+  Apple = ref object
+    name: string
+    color: int
+    weight: int
+
+proc initApple(name: string): Apple =
+  discard
+
+proc deinitApple(x: Apple) =
+  discard
+
+macro wrapObject(obj: typed, n: varargs[untyped]): untyped =
+  let m = n[0]
+  for x in m:
+    var z = bindSym x
+    echo z.repr
+
+wrapObject(Apple):
+  initApple
+  deinitApple
+
+type
+  Coral = enum
+    redCoral
+    blackCoral
+
+macro mixer(): untyped =
+  let m = "Co" & "ral"
+  let x = bindSym(m)
+  echo x.repr
+  echo getType(x).repr
+
+mixer()
diff --git a/tests/macros/tmacrostmt.nim b/tests/macros/tmacrostmt.nim
index a6e1e66dd..9dbfbce43 100644
--- a/tests/macros/tmacrostmt.nim
+++ b/tests/macros/tmacrostmt.nim
@@ -43,4 +43,35 @@ macro repr_and_parse(fn: typed): typed =
   echo fn_impl.repr
   result = parseStmt(fn_impl.repr)
 
-repr_and_parse(f)
\ No newline at end of file
+repr_and_parse(f)
+
+
+#------------------------------------
+# bugs #8343 and #8344 
+proc one_if_proc(x, y : int): int =
+  if x < y: result = x
+  else: result = y
+
+proc test_block(x, y : int): int =
+  block label:
+    result = x
+    result = y
+
+#------------------------------------
+# bugs #8348
+
+template `>`(x, y: untyped): untyped =
+  ## "is greater" operator. This is the same as ``y < x``.
+  y < x
+
+proc test_cond_stmtlist(x, y: int): int =
+  result = x
+  if x > y:
+    result = x
+
+
+repr_and_parse(one_if_proc)
+repr_and_parse(test_block)
+repr_and_parse(test_cond_stmtlist)
+
+
diff --git a/tests/misc/tsizeof.nim b/tests/misc/tsizeof.nim
index 4afd48472..0597535f9 100644
--- a/tests/misc/tsizeof.nim
+++ b/tests/misc/tsizeof.nim
@@ -1,5 +1,7 @@
-# Test the sizeof proc
-
+discard """
+  file: "tsize.nim"
+  output: "40 3 12 32"
+"""
 type
   TMyRecord {.final.} = object
     x, y: int
@@ -7,4 +9,20 @@ type
     r: float
     s: string
 
+  TMyEnum = enum
+    tmOne, tmTwo, tmThree, tmFour
+
+  TMyArray1 = array[3, uint8]
+  TMyArray2 = array[1..3, int32]
+  TMyArray3 = array[TMyEnum, float64]
+
+const 
+  mysize1 = sizeof(TMyArray1)
+  mysize2 = sizeof(TMyArray2)
+  mysize3 = sizeof(TMyArray3)
+
 write(stdout, sizeof(TMyRecord))
+echo ' ', mysize1, ' ', mysize2, ' ',mysize3
+
+
+
diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim
index a5f86b54d..d7b199a22 100644
--- a/tests/pragmas/tcustom_pragma.nim
+++ b/tests/pragmas/tcustom_pragma.nim
@@ -154,3 +154,23 @@ block:
 let a: proc(x: int) {.defaultValue(5).} = nil
 static:
   doAssert hasCustomPragma(a.type, defaultValue)
+
+# bug #8371
+template thingy {.pragma.}
+
+type
+  Cardinal = enum
+    north, east, south, west
+  Something = object
+    a: float32
+    case cardinal: Cardinal
+    of north:
+      b {.thingy.}: int
+    of east:
+      c: int
+    of south: discard
+    else: discard
+
+var foo: Something
+foo.cardinal = north
+doAssert foo.b.hasCustomPragma(thingy) == true
diff --git a/tests/sets/tsetpop.nim b/tests/sets/tsetpop.nim
new file mode 100644
index 000000000..c37bda57d
--- /dev/null
+++ b/tests/sets/tsetpop.nim
@@ -0,0 +1,22 @@
+discard """
+  targets: "c c++ js"
+  output: '''1000
+0
+set is empty
+'''
+"""
+
+import sets
+
+var a = initSet[int]()
+for i in 1..1000:
+  a.incl(i)
+echo len(a)
+for i in 1..1000:  
+  discard a.pop()
+echo len(a)
+
+try:
+  echo a.pop()
+except KeyError as e:
+  echo e.msg
\ No newline at end of file
diff --git a/tests/sets/tsets.nim b/tests/sets/tsets.nim
index 96d5debc7..13a5f54e6 100644
--- a/tests/sets/tsets.nim
+++ b/tests/sets/tsets.nim
@@ -205,4 +205,12 @@ echo warnUninit in gNotes
 
 # 7555
 doAssert {-1.int8, -2, -2}.card == 2
-doAssert {1, 2, 2, 3..5, 4..6}.card == 6
\ No newline at end of file
+doAssert {1, 2, 2, 3..5, 4..6}.card == 6
+
+type Foo = enum
+  Foo1 = 0
+  Foo2 = 1
+  Foo3 = 3
+
+let x = { Foo1, Foo2 }
+# bug #8425
diff --git a/tests/stdlib/tencoding.nim b/tests/stdlib/tencoding.nim
new file mode 100644
index 000000000..d6ff7ab32
--- /dev/null
+++ b/tests/stdlib/tencoding.nim
@@ -0,0 +1,21 @@
+discard """
+  output: '''OK'''
+"""
+
+#bug #8468
+
+import encodings, strutils
+
+when defined(windows):
+  var utf16to8 = open(destEncoding = "utf-16", srcEncoding = "utf-8")
+  var s = "some string"
+  var c = utf16to8.convert(s)
+
+  var z = newStringOfCap(s.len * 2)
+  for x in s:
+    z.add x
+    z.add chr(0)
+
+  doAssert z == c
+
+echo "OK"
diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim
index f13d2e5cb..c0b4d5f78 100644
--- a/tests/stdlib/tjsonmacro.nim
+++ b/tests/stdlib/tjsonmacro.nim
@@ -337,7 +337,7 @@ when isMainModule:
         n2: Option[int]
         n3: Option[string]
         n4: Option[bool]
-        
+
     var j0 = parseJson("""{"n1": 1, "n2": null, "n3": null, "n4": null}""")
     let j0Deser = j0.to(Obj)
     doAssert j0Deser.n1 == 1
@@ -411,10 +411,109 @@ when isMainModule:
     doAssert dataDeser.a == 1
     doAssert dataDeser.f == 6
     doAssert dataDeser.i == 9.9'f32
-  
+
   # deserialize directly into a table
   block:
     let s = """{"a": 1, "b": 2}"""
     let t = parseJson(s).to(Table[string, int])
     doAssert t["a"] == 1
-    doAssert t["b"] == 2
\ No newline at end of file
+    doAssert t["b"] == 2
+
+  block:
+    # bug #8037
+    type
+      Apple = distinct string
+      String = distinct Apple
+      Email = distinct string
+      MyList = distinct seq[int]
+      MyYear = distinct Option[int]
+      MyTable = distinct Table[string, int]
+      MyArr = distinct array[3, float]
+      MyRef = ref object
+        name: string
+      MyObj = object
+        color: int
+      MyDistRef = distinct MyRef
+      MyDistObj = distinct MyObj
+      Toot = object
+        name*: String
+        email*: Email
+        list: MyList
+        year: MyYear
+        dict: MyTable
+        arr: MyArr
+        person: MyDistRef
+        distfruit: MyDistObj
+        dog: MyRef
+        fruit: MyObj
+        emails: seq[String]
+
+    var tJson = parseJson("""
+      {
+        "name":"Bongo",
+        "email":"bongo@bingo.com",
+        "list": [11,7,15],
+        "year": 1975,
+        "dict": {"a": 1, "b": 2},
+        "arr": [1.0, 2.0, 7.0],
+        "person": {"name": "boney"},
+        "dog": {"name": "honey"},
+        "fruit": {"color": 10},
+        "distfruit": {"color": 11},
+        "emails": ["abc", "123"]
+      }
+    """)
+
+    var t = to(tJson, Toot)
+    doAssert string(t.name) == "Bongo"
+    doAssert string(t.email) == "bongo@bingo.com"
+    doAssert seq[int](t.list) == @[11,7,15]
+    doAssert Option[int](t.year).get() == 1975
+    doAssert Table[string,int](t.dict)["a"] == 1
+    doAssert Table[string,int](t.dict)["b"] == 2
+    doAssert array[3, float](t.arr) == [1.0,2.0,7.0]
+    doAssert MyRef(t.person).name == "boney"
+    doAssert MyObj(t.distFruit).color == 11
+    doAssert t.dog.name == "honey"
+    doAssert t.fruit.color == 10
+    doAssert seq[string](t.emails) == @["abc", "123"]
+
+    block test_table:
+      var y = parseJson("""{"a": 1, "b": 2, "c": 3}""")
+      var u = y.to(MyTable)
+      var v = y.to(Table[string, int])
+      doAssert Table[string, int](u)["a"] == 1
+      doAssert Table[string, int](u)["b"] == 2
+      doAssert Table[string, int](u)["c"] == 3
+      doAssert v["a"] == 1
+
+    block primitive_string:
+      const kApple = "apple"
+      var u = newJString(kApple)
+      var v = u.to(Email)
+      var w = u.to(Apple)
+      var x = u.to(String)
+      doAssert string(v) == kApple
+      doAssert string(w) == kApple
+      doAssert string(x) == kApple
+
+    block test_option:
+      var u = newJInt(1137)
+      var v = u.to(MyYear)
+      var w = u.to(Option[int])
+      doAssert Option[int](v).get() == 1137
+      doAssert w.get() == 1137
+
+    block test_object:
+      var u = parseJson("""{"color": 987}""")
+      var v = u.to(MyObj)
+      var w = u.to(MyDistObj)
+      doAssert v.color == 987
+      doAssert MyObj(w).color == 987
+
+    block test_ref_object:
+      var u = parseJson("""{"name": "smith"}""")
+      var v = u.to(MyRef)
+      var w = u.to(MyDistRef)
+      doAssert v.name == "smith"
+      doAssert MyRef(w).name == "smith"
diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim
index a376c66d9..10f439dfa 100644
--- a/tests/stdlib/tos.nim
+++ b/tests/stdlib/tos.nim
@@ -145,7 +145,7 @@ else:
   echo getLastModificationTime("a") == tm
 removeFile("a")
 
-when defined(Linux) or defined(macosx):
+when defined(posix):
 
   block normalizedPath:
     block relative:
diff --git a/tests/system/t7894.nim b/tests/system/t7894.nim
new file mode 100644
index 000000000..2808e5020
--- /dev/null
+++ b/tests/system/t7894.nim
@@ -0,0 +1,18 @@
+discard """
+"""
+
+import os
+
+const size = 250000000
+var saved = newSeq[seq[int8]]()
+
+for i in 0..22:
+  # one of these is 0.25GB.
+  #echo i
+  var x = newSeq[int8](size)
+  sleep(10)
+  saved.add(x)
+
+for x in saved:
+  #echo x.len
+  doAssert x.len == size
diff --git a/tests/system/talloc.nim b/tests/system/talloc.nim
index 18396041d..bf2cd97a8 100644
--- a/tests/system/talloc.nim
+++ b/tests/system/talloc.nim
@@ -1,54 +1,57 @@
+discard """
+"""
+
 var x: ptr int
 
 x = cast[ptr int](alloc(7))
-assert x != nil
+doAssert x != nil
 x = cast[ptr int](x.realloc(2))
-assert x != nil
+doAssert x != nil
 x.dealloc()
 
 x = createU(int, 3)
-assert x != nil
+doAssert x != nil
 x.dealloc()
 
 x = create(int, 4)
-assert cast[ptr array[4, int]](x)[0] == 0
-assert cast[ptr array[4, int]](x)[1] == 0
-assert cast[ptr array[4, int]](x)[2] == 0
-assert cast[ptr array[4, int]](x)[3] == 0
+doAssert cast[ptr array[4, int]](x)[0] == 0
+doAssert cast[ptr array[4, int]](x)[1] == 0
+doAssert cast[ptr array[4, int]](x)[2] == 0
+doAssert cast[ptr array[4, int]](x)[3] == 0
 
 x = x.resize(4)
-assert x != nil
+doAssert x != nil
 x.dealloc()
 
 x = cast[ptr int](allocShared(100))
-assert x != nil
+doAssert x != nil
 deallocShared(x)
 
 x = createSharedU(int, 3)
-assert x != nil
+doAssert x != nil
 x.deallocShared()
 
 x = createShared(int, 3)
-assert x != nil
-assert cast[ptr array[3, int]](x)[0] == 0
-assert cast[ptr array[3, int]](x)[1] == 0
-assert cast[ptr array[3, int]](x)[2] == 0
+doAssert x != nil
+doAssert cast[ptr array[3, int]](x)[0] == 0
+doAssert cast[ptr array[3, int]](x)[1] == 0
+doAssert cast[ptr array[3, int]](x)[2] == 0
 
-assert x != nil
+doAssert x != nil
 x = cast[ptr int](x.resizeShared(2))
-assert x != nil
+doAssert x != nil
 x.deallocShared()
 
 x = create(int, 10)
-assert x != nil
+doAssert x != nil
 x = x.resize(12)
-assert x != nil
+doAssert x != nil
 x.dealloc()
 
 x = createShared(int, 1)
-assert x != nil
+doAssert x != nil
 x = x.resizeShared(1)
-assert x != nil
+doAssert x != nil
 x.deallocShared()
 
 x = cast[ptr int](alloc0(125 shl 23))
diff --git a/tests/system/talloc2.nim b/tests/system/talloc2.nim
index c8cab78a1..0757c0724 100644
--- a/tests/system/talloc2.nim
+++ b/tests/system/talloc2.nim
@@ -1,3 +1,6 @@
+discard """
+"""
+
 const
   nmax = 2*1024*1024*1024
 
diff --git a/tests/system/tdeepcopy.nim b/tests/system/tdeepcopy.nim
index f7a6e87fa..383d2e8d1 100644
--- a/tests/system/tdeepcopy.nim
+++ b/tests/system/tdeepcopy.nim
@@ -65,7 +65,7 @@ proc main() =
     for val in table.values():
       if myObj2.isNil:
         myObj2 = val
-    assert(myObj == myObj2) # passes
+    doAssert(myObj == myObj2) # passes
 
     var tableCopy: ListTableRef[int, SomeObj]
     deepCopy(tableCopy, table)
@@ -80,7 +80,7 @@ proc main() =
     #echo cast[int](myObjCopy)
     #echo cast[int](myObjCopy2)
 
-    assert(myObjCopy == myObjCopy2) # fails
+    doAssert(myObjCopy == myObjCopy2) # fails
 
 
 type
@@ -88,7 +88,7 @@ type
     counter, max: int
     data: array[0..99, (pointer, pointer)]
 
-assert(sizeof(PtrTable) == 2*sizeof(int)+sizeof(pointer)*2*100)
+doAssert(sizeof(PtrTable) == 2*sizeof(int)+sizeof(pointer)*2*100)
 
 main()
 echo "ok"
diff --git a/tests/system/tio.nim b/tests/system/tio.nim
index 3d4df806b..7e9e18950 100644
--- a/tests/system/tio.nim
+++ b/tests/system/tio.nim
@@ -1,3 +1,6 @@
+discard """
+"""
+
 import
   unittest, osproc, streams, os, strformat
 const STRING_DATA = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
@@ -36,7 +39,7 @@ proc verifyFileSize(sz: int64) =
     discard execProcess(&"dd if=/dev/zero of={fn} bs=1000000 count={size_in_mb}")
 
   doAssert os.getFileSize(fn) == sz # Verify OS filesize by string
-  
+
   var f = open(fn)
   doAssert f.getFileSize() == sz # Verify file handle filesize
   f.close()
diff --git a/tests/system/tparams.nim b/tests/system/tparams.nim
index 1358212f2..dd5511b8f 100644
--- a/tests/system/tparams.nim
+++ b/tests/system/tparams.nim
@@ -1,3 +1,6 @@
+discard """
+"""
+
 import os
 import osproc
 import parseopt2
@@ -7,12 +10,12 @@ let argv = commandLineParams()
 
 if argv == @[]:
   # this won't work with spaces
-  assert execShellCmd(getAppFilename() & " \"foo bar\" --aa:bar=a --a=c:d --ab -c --a[baz]:doo") == 0
+  doAssert execShellCmd(getAppFilename() & " \"foo bar\" --aa:bar=a --a=c:d --ab -c --a[baz]:doo") == 0
 else:
   let f = toSeq(getopt())
   echo f.repr
-  assert f[0].kind == cmdArgument and f[0].key == "foo bar" and f[0].val == ""
-  assert f[1].kind == cmdLongOption and f[1].key == "aa" and f[1].val == "bar=a"
-  assert f[2].kind == cmdLongOption and f[2].key == "a=c" and f[2].val == "d"
-  assert f[3].kind == cmdLongOption and f[3].key == "ab" and f[3].val == ""
-  assert f[4].kind == cmdShortOption and f[4].key == "c" and f[4].val == ""
+  doAssert f[0].kind == cmdArgument and f[0].key == "foo bar" and f[0].val == ""
+  doAssert f[1].kind == cmdLongOption and f[1].key == "aa" and f[1].val == "bar=a"
+  doAssert f[2].kind == cmdLongOption and f[2].key == "a=c" and f[2].val == "d"
+  doAssert f[3].kind == cmdLongOption and f[3].key == "ab" and f[3].val == ""
+  doAssert f[4].kind == cmdShortOption and f[4].key == "c" and f[4].val == ""
diff --git a/tests/vm/tref.nim b/tests/vm/tref.nim
index 27b7bf313..f5cd23da5 100644
--- a/tests/vm/tref.nim
+++ b/tests/vm/tref.nim
@@ -54,4 +54,9 @@ static:
     new(s)
     var ss = s
     s[] = 1
-    doAssert ss[] == 1
\ No newline at end of file
+    doAssert ss[] == 1
+
+static: # bug #8402
+  type R = ref object
+  var empty: R
+  let otherEmpty = empty
\ No newline at end of file
diff --git a/tools/dochack/dochack.nim b/tools/dochack/dochack.nim
index 79a0e7482..8cc27b6eb 100644
--- a/tools/dochack/dochack.nim
+++ b/tools/dochack/dochack.nim
@@ -1,6 +1,5 @@
-
-
 import karax
+import fuzzysearch
 
 proc findNodeWith(x: Element; tag, content: cstring): Element =
   if x.nodeName == tag and x.textContent == content:
@@ -88,11 +87,11 @@ proc toHtml(x: TocEntry; isRoot=false): Element =
   if ul.len != 0: result.add ul
   if result.len == 0: result = nil
 
-proc containsWord(a, b: cstring): bool {.asmNoStackFrame.} =
-  {.emit: """
-     var escaped = `b`.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
-     return new RegExp("\\b" + escaped + "\\b").test(`a`);
-  """.}
+#proc containsWord(a, b: cstring): bool {.asmNoStackFrame.} =
+  #{.emit: """
+     #var escaped = `b`.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
+     #return new RegExp("\\b" + escaped + "\\b").test(`a`);
+  #""".}
 
 proc isWhitespace(text: cstring): bool {.asmNoStackFrame.} =
   {.emit: """
@@ -252,24 +251,29 @@ proc dosearch(value: cstring): Element =
 
     `stuff` = doc.documentElement;
     """.}
-    db = stuff.getElementsByClass"reference external"
+    db = stuff.getElementsByClass"reference"
     contents = @[]
     for ahref in db:
-      contents.add ahref.textContent.normalize
+      contents.add ahref.getAttribute("data-doc-search-tag")
   let ul = tree("UL")
   result = tree("DIV")
   result.setClass"search_results"
   var matches: seq[(Element, int)] = @[]
-  let key = value.normalize
   for i in 0..<db.len:
     let c = contents[i]
-    if c.containsWord(key):
-      matches.add((db[i], -(30_000 - c.len)))
-    elif c.contains(key):
-      matches.add((db[i], c.len))
+    if c == "Examples" or c == "PEG construction":
+    # Some manual exclusions.
+    # Ideally these should be fixed in the index to be more
+    # descriptive of what they are.
+      continue
+    let (score, matched) = fuzzymatch(value, c)
+    if matched:
+      matches.add((db[i], score))
+
   matches.sort do (a, b: auto) -> int:
-    a[1] - b[1]
-  for i in 0..min(<matches.len, 19):
+    b[1] - a[1]
+  for i in 0 ..< min(matches.len, 19):
+    matches[i][0].innerHTML = matches[i][0].getAttribute("data-doc-search-tag")
     ul.add(tree("LI", matches[i][0]))
   if ul.len == 0:
     result.add tree("B", text"no search results")
diff --git a/tools/dochack/fuzzysearch.nim b/tools/dochack/fuzzysearch.nim
new file mode 100644
index 000000000..e6b1ea3cd
--- /dev/null
+++ b/tools/dochack/fuzzysearch.nim
@@ -0,0 +1,139 @@
+# A Fuzzy Match implementation inspired by the sublime text fuzzy match algorithm
+# as described here: https://blog.forrestthewoods.com/reverse-engineering-sublime-text-s-fuzzy-match-4cffeed33fdb
+# Heavily modified to provide more subjectively useful results
+# for on the Nim manual.
+#
+import strutils
+import math
+import macros
+
+
+const
+  MaxUnmatchedLeadingChar = 3
+  ## Maximum number of times the penalty for unmatched leading chars is applied.
+
+  HeadingScaleFactor = 0.5
+  ## The score from before the colon Char is multiplied by this.
+  ## This is to weight function signatures and descriptions over module titles.
+
+
+type 
+  ScoreCard = enum 
+    StartMatch           = -100 ## Start matching.
+    LeadingCharDiff      = -3   ## An unmatched, leading character was found.
+    CharDiff             = -1   ## An unmatched character was found.
+    CharMatch            = 0    ## A matched character was found.
+    ConsecutiveMatch     = 5    ## A consecutive match was found.
+    LeadingCharMatch     = 10   ## The character matches the begining of the
+                                ## string or the first character of a word
+                                ## or camel case boundry.
+    WordBoundryMatch     = 20   ## The last ConsecutiveCharMatch that
+                                ## immediately precedes the end of the string,
+                                ## end of the pattern, or a LeadingCharMatch.
+
+
+proc fuzzyMatch*(pattern, str: cstring) : tuple[score: int, matched: bool] =
+  var
+    scoreState = StartMatch
+    headerMatched = false
+    unmatchedLeadingCharCount = 0
+    consecutiveMatchCount = 0
+    strIndex = 0
+    patIndex = 0
+    score = 0
+
+  template transition(nextState) =
+    scoreState = nextState
+    score += ord(scoreState)
+
+  while (strIndex < str.len) and (patIndex < pattern.len):
+    var
+      patternChar = pattern[patIndex].toLowerAscii
+      strChar     = str[strIndex].toLowerAscii
+
+    # Ignore certain characters
+    if patternChar in {'_', ' ', '.'}:
+      patIndex += 1
+      continue
+    if strChar in {'_', ' ', '.'}:
+      strIndex += 1
+      continue
+    
+    # Since this algorithm will be used to search against Nim documentation,
+    # the below logic prioritizes headers.
+    if not headerMatched and strChar == ':':
+      headerMatched = true
+      scoreState = StartMatch
+      score = toInt(floor(HeadingScaleFactor * toFloat(score)))
+      patIndex = 0
+      strIndex += 1
+      continue
+
+    if strChar == patternChar:
+      case scoreState 
+      of StartMatch, WordBoundryMatch:
+        scoreState = LeadingCharMatch
+
+      of CharMatch:
+        transition(ConsecutiveMatch)
+
+      of LeadingCharMatch, ConsecutiveMatch:
+        consecutiveMatchCount += 1
+        scoreState = ConsecutiveMatch
+        score += ord(ConsecutiveMatch) * consecutiveMatchCount
+
+        if scoreState == LeadingCharMatch:
+          score += ord(LeadingCharMatch)
+          
+        var onBoundary = (patIndex == high(pattern))
+        if not onBoundary:
+          let
+            nextPatternChar = toLowerAscii(pattern[patIndex + 1])
+            nextStrChar     = toLowerAscii(str[strIndex + 1])
+
+          onBoundary = (
+            nextStrChar notin {'a'..'z'} and
+            nextStrChar != nextPatternChar
+          )
+        
+        if onBoundary:
+          transition(WordBoundryMatch)
+
+      of CharDiff, LeadingCharDiff:
+        var isLeadingChar = (
+          str[strIndex - 1] notin Letters or
+          str[strIndex - 1] in {'a'..'z'} and
+          str[strIndex] in {'A'..'Z'}
+        )
+
+        if isLeadingChar:
+          scoreState = LeadingCharMatch
+          #a non alpha or a camel case transition counts as a leading char.
+          # Transition the state, but don't give the bonus yet; wait until we verify a consecutive match.
+        else:
+          transition(CharMatch)
+      patIndex += 1
+
+    else:
+      case scoreState 
+      of StartMatch:
+        transition(LeadingCharDiff)
+
+      of ConsecutiveMatch:
+        transition(CharDiff)
+        consecutiveMatchCount = 0
+
+      of LeadingCharDiff:
+        if unmatchedLeadingCharCount < MaxUnmatchedLeadingChar:
+          transition(LeadingCharDiff)
+        unmatchedLeadingCharCount += 1
+
+      else:
+        transition(CharDiff)
+
+    strIndex += 1
+
+  result = (
+    score:   max(0, score),
+    matched: (score > 0),
+  )
diff --git a/tools/nimweb.nim b/tools/nimweb.nim
index 6e1d9d359..e74b081ea 100644
--- a/tools/nimweb.nim
+++ b/tools/nimweb.nim
@@ -13,16 +13,18 @@ import
 
 from xmltree import escape
 
-const gitRepo = "https://github.com/nim-lang/Nim"
-
 type
   TKeyValPair = tuple[key, id, val: string]
   TConfigData = object of RootObj
     tabs, links: seq[TKeyValPair]
     doc, srcdoc, srcdoc2, webdoc, pdf: seq[string]
-    authors, projectName, projectTitle, logo, infile, outdir, ticker: string
+    authors, projectName, projectTitle, logo, infile, ticker: string
     vars: StringTableRef
+    nimCompiler: string
     nimArgs: string
+    gitURL: string
+    docHTMLOutput: string
+    webUploadOutput: string
     quotations: Table[string, tuple[quote, author: string]]
     numProcessors: int # Set by parallelBuild:n, only works for values > 0.
     gaId: string  # google analytics ID, nil means analytics are disabled
@@ -51,8 +53,10 @@ proc initConfigData(c: var TConfigData) =
   c.webdoc = @[]
   c.pdf = @[]
   c.infile = ""
-  c.outdir = ""
   c.nimArgs = "--hint[Conf]:off --hint[Path]:off --hint[Processing]:off -d:boot "
+  c.gitURL = "https://github.com/nim-lang/Nim"
+  c.docHTMLOutput = "doc/html"
+  c.webUploadOutput = "web/upload"
   c.authors = ""
   c.projectTitle = ""
   c.projectName = ""
@@ -79,14 +83,19 @@ const
 Usage:
   nimweb [options] ini-file[.ini] [compile_options]
 Options:
-  -o, --output:dir    set the output directory (default: same as ini-file)
-  --var:name=value    set the value of a variable
   -h, --help          shows this help
   -v, --version       shows the version
+  -o, --output        overrides output directory instead of default
+                      web/upload and doc/html
+  --nimCompiler       overrides nim compiler; default = bin/nim
+  --var:name=value    set the value of a variable
   --website           only build the website, not the full documentation
   --pdf               build the PDF version of the documentation
   --json2             build JSON of the documentation
   --onlyDocs          build only the documentation
+  --git.url           override base url in generated doc links
+  --git.commit        override commit/branch in generated doc links 'source'
+  --git.devel         override devel branch in generated doc links 'edit'
 Compile_options:
   will be passed to the Nim compiler
 """
@@ -145,7 +154,11 @@ proc parseCmdLine(c: var TConfigData) =
       of "version", "v":
         stdout.write(version & "\n")
         quit(0)
-      of "o", "output": c.outdir = val
+      of "output", "o":
+        c.webUploadOutput = val
+        c.docHTMLOutput = val / "docs"
+      of "nimcompiler":
+        c.nimCompiler = val
       of "parallelbuild":
         try:
           let num = parseInt(val)
@@ -163,8 +176,14 @@ proc parseCmdLine(c: var TConfigData) =
       of "googleanalytics":
         c.gaId = val
         c.nimArgs.add("--doc.googleAnalytics:" & val & " ")
+      of "git.url":
+        c.gitURL = val
+      of "git.commit":
+        c.nimArgs.add("--git.commit:" & val & " ")
+      of "git.devel":
+        c.nimArgs.add("--git.devel:" & val & " ")
       else:
-        echo("Invalid argument $1" % [key])
+        echo("Invalid argument '$1'" % [key])
         quit(usage)
     of cmdEnd: break
   if c.infile.len == 0: quit(usage)
@@ -244,15 +263,14 @@ proc parseIniFile(c: var TConfigData) =
   close(p)
   if c.projectName.len == 0:
     c.projectName = changeFileExt(extractFilename(c.infile), "")
-  if c.outdir.len == 0:
-    c.outdir = splitFile(c.infile).dir
 
 # ------------------- main ----------------------------------------------------
 
 
 proc exe(f: string): string = return addFileExt(f, ExeExt)
 
-proc findNim(): string =
+proc findNim(c: TConfigData): string =
+  if c.nimCompiler.len > 0: return c.nimCompiler
   var nim = "nim".exe
   result = "bin" / nim
   if existsFile(result): return
@@ -289,9 +307,9 @@ proc buildDocSamples(c: var TConfigData, destPath: string) =
   ## it didn't make much sense to integrate into the existing generic
   ## documentation builders.
   const src = "doc"/"docgen_sample.nim"
-  exec(findNim() & " doc $# -o:$# $#" %
+  exec(findNim(c) & " doc $# -o:$# $#" %
     [c.nimArgs, destPath / "docgen_sample.html", src])
-  exec(findNim() & " doc2 $# -o:$# $#" %
+  exec(findNim(c) & " doc2 $# -o:$# $#" %
     [c.nimArgs, destPath / "docgen_sample2.html", src])
 
 proc pathPart(d: string): string = splitFile(d).dir.replace('\\', '/')
@@ -302,23 +320,23 @@ proc buildDoc(c: var TConfigData, destPath: string) =
     commands = newSeq[string](len(c.doc) + len(c.srcdoc) + len(c.srcdoc2))
     i = 0
   for d in items(c.doc):
-    commands[i] = findNim() & " rst2html $# --git.url:$# -o:$# --index:on $#" %
-      [c.nimArgs, gitRepo,
+    commands[i] = findNim(c) & " rst2html $# --git.url:$# -o:$# --index:on $#" %
+      [c.nimArgs, c.gitURL,
       destPath / changeFileExt(splitFile(d).name, "html"), d]
     i.inc
   for d in items(c.srcdoc):
-    commands[i] = findNim() & " doc0 $# --git.url:$# -o:$# --index:on $#" %
-      [c.nimArgs, gitRepo,
+    commands[i] = findNim(c) & " doc0 $# --git.url:$# -o:$# --index:on $#" %
+      [c.nimArgs, c.gitURL,
       destPath / changeFileExt(splitFile(d).name, "html"), d]
     i.inc
   for d in items(c.srcdoc2):
-    commands[i] = findNim() & " doc2 $# --git.url:$# -o:$# --index:on $#" %
-      [c.nimArgs, gitRepo,
+    commands[i] = findNim(c) & " doc2 $# --git.url:$# -o:$# --index:on $#" %
+      [c.nimArgs, c.gitURL,
       destPath / changeFileExt(splitFile(d).name, "html"), d]
     i.inc
 
   mexec(commands, c.numProcessors)
-  exec(findNim() & " buildIndex -o:$1/theindex.html $1" % [destPath])
+  exec(findNim(c) & " buildIndex -o:$1/theindex.html $1" % [destPath])
 
 proc buildPdfDoc(c: var TConfigData, destPath: string) =
   createDir(destPath)
@@ -327,7 +345,7 @@ proc buildPdfDoc(c: var TConfigData, destPath: string) =
   else:
     const pdflatexcmd = "pdflatex -interaction=nonstopmode "
     for d in items(c.pdf):
-      exec(findNim() & " rst2tex $# $#" % [c.nimArgs, d])
+      exec(findNim(c) & " rst2tex $# $#" % [c.nimArgs, d])
       # call LaTeX twice to get cross references right:
       exec(pdflatexcmd & changeFileExt(d, "tex"))
       exec(pdflatexcmd & changeFileExt(d, "tex"))
@@ -347,8 +365,8 @@ proc buildAddDoc(c: var TConfigData, destPath: string) =
   # build additional documentation (without the index):
   var commands = newSeq[string](c.webdoc.len)
   for i, doc in pairs(c.webdoc):
-    commands[i] = findNim() & " doc2 $# --git.url:$# -o:$# $#" %
-      [c.nimArgs, gitRepo,
+    commands[i] = findNim(c) & " doc2 $# --git.url:$# -o:$# $#" %
+      [c.nimArgs, c.gitURL,
       destPath / changeFileExt(splitFile(doc).name, "html"), doc]
   mexec(commands, c.numProcessors)
 
@@ -431,9 +449,9 @@ proc buildNewsRss(c: var TConfigData, destPath: string) =
 
   generateRss(destFilename, parseNewsTitles(srcFilename))
 
-proc buildJS(destPath: string) =
-  exec(findNim() & " js -d:release --out:$1 web/nimblepkglist.nim" %
-      [destPath / "nimblepkglist.js"])
+proc buildJS(c: TConfigData) =
+  exec(findNim(c) & " js -d:release --out:$1 web/nimblepkglist.nim" %
+      [c.webUploadOutput / "nimblepkglist.js"])
 
 proc readSponsors(sponsorsFile: string): seq[Sponsor] =
   result = @[]
@@ -464,7 +482,7 @@ const
   cmdRst2Html = " rst2html --compileonly $1 -o:web/$2.temp web/$2.rst"
 
 proc buildPage(c: var TConfigData, file, title, rss: string, assetDir = "") =
-  exec(findNim() & cmdRst2Html % [c.nimArgs, file])
+  exec(findNim(c) & cmdRst2Html % [c.nimArgs, file])
   var temp = "web" / changeFileExt(file, "temp")
   var content: string
   try:
@@ -472,7 +490,7 @@ proc buildPage(c: var TConfigData, file, title, rss: string, assetDir = "") =
   except IOError:
     quit("[Error] cannot open: " & temp)
   var f: File
-  var outfile = "web/upload/$#.html" % file
+  var outfile = c.webUploadOutput / "$#.html" % file
   if not existsDir(outfile.splitFile.dir):
     createDir(outfile.splitFile.dir)
   if open(f, outfile, fmWrite):
@@ -502,27 +520,25 @@ proc buildWebsite(c: var TConfigData) =
     let rss = if file in ["news", "index"]: extractFilename(rssUrl) else: ""
     if '.' in file: continue
     buildPage(c, file, if file == "question": "FAQ" else: file, rss)
-  copyDir("web/assets", "web/upload/assets")
-  buildNewsRss(c, "web/upload")
-  buildSponsors(c, "web/upload")
-  buildNews(c, "web/news", "web/upload/news")
+  copyDir("web/assets", c.webUploadOutput / "assets")
+  buildNewsRss(c, c.webUploadOutput)
+  buildSponsors(c, c.webUploadOutput)
+  buildNews(c, "web/news", c.webUploadOutput / "news")
+
+proc onlyDocs(c: var TConfigData) =
+  createDir(c.docHTMLOutput)
+  buildDocSamples(c, c.docHTMLOutput)
+  buildDoc(c, c.docHTMLOutput)
 
 proc main(c: var TConfigData) =
   buildWebsite(c)
-  buildJS("web/upload")
-  const docup = "web/upload/" & NimVersion
+  buildJS(c)
+  let docup = c.webUploadOutput / NimVersion
   createDir(docup)
   buildAddDoc(c, docup)
   buildDocSamples(c, docup)
   buildDoc(c, docup)
-  createDir("doc/html")
-  buildDocSamples(c, "doc/html")
-  buildDoc(c, "doc/html")
-
-proc onlyDocs(c: var TConfigData) =
-  createDir("doc/html")
-  buildDocSamples(c, "doc/html")
-  buildDoc(c, "doc/html")
+  onlyDocs(c)
 
 proc json2(c: var TConfigData) =
   const destPath = "web/json2"
@@ -530,8 +546,8 @@ proc json2(c: var TConfigData) =
   var i = 0
   for d in items(c.srcdoc2):
     createDir(destPath / splitFile(d).dir)
-    commands[i] = findNim() & " jsondoc2 $# --git.url:$# -o:$# --index:on $#" %
-      [c.nimArgs, gitRepo,
+    commands[i] = findNim(c) & " jsondoc2 $# --git.url:$# -o:$# --index:on $#" %
+      [c.nimArgs, c.gitURL,
       destPath / changeFileExt(d, "json"), d]
     i.inc