summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--appveyor.yml1
-rw-r--r--build_all.sh31
-rw-r--r--changelog.md63
-rw-r--r--compiler/ast.nim26
-rw-r--r--compiler/astalgo.nim2
-rw-r--r--compiler/ccgcalls.nim4
-rw-r--r--compiler/ccgexprs.nim37
-rw-r--r--compiler/ccgmerge.nim2
-rw-r--r--compiler/ccgstmts.nim12
-rw-r--r--compiler/cgen.nim34
-rw-r--r--compiler/cgendata.nim1
-rw-r--r--compiler/commands.nim2
-rw-r--r--compiler/condsyms.nim9
-rw-r--r--compiler/docgen.nim15
-rw-r--r--compiler/idents.nim37
-rw-r--r--compiler/jsgen.nim5
-rw-r--r--compiler/lineinfos.nim16
-rw-r--r--compiler/lookups.nim4
-rw-r--r--compiler/modulegraphs.nim54
-rw-r--r--compiler/msgs.nim8
-rw-r--r--compiler/nimconf.nim6
-rw-r--r--compiler/options.nim53
-rw-r--r--compiler/parampatterns.nim2
-rw-r--r--compiler/parser.nim3
-rw-r--r--compiler/patterns.nim13
-rw-r--r--compiler/pragmas.nim2
-rw-r--r--compiler/renderer.nim16
-rw-r--r--compiler/rodutils.nim21
-rw-r--r--compiler/ropes.nim26
-rw-r--r--compiler/scriptconfig.nim9
-rw-r--r--compiler/sem.nim9
-rw-r--r--compiler/semcall.nim6
-rw-r--r--compiler/semdata.nim7
-rw-r--r--compiler/semfold.nim8
-rw-r--r--compiler/seminst.nim7
-rw-r--r--compiler/semmacrosanity.nim2
-rw-r--r--compiler/semobjconstr.nim4
-rw-r--r--compiler/semstmts.nim191
-rw-r--r--compiler/semtypes.nim2
-rw-r--r--compiler/semtypinst.nim4
-rw-r--r--compiler/sigmatch.nim24
-rw-r--r--compiler/transf.nim11
-rw-r--r--compiler/types.nim11
-rw-r--r--compiler/vm.nim30
-rw-r--r--compiler/vmdef.nim7
-rw-r--r--compiler/vmgen.nim17
-rw-r--r--config/nim.cfg33
-rw-r--r--doc/advopt.txt2
-rw-r--r--doc/backends.txt13
-rw-r--r--doc/docgen.rst21
-rw-r--r--doc/exception_hierarchy_fragment.txt28
-rw-r--r--doc/manual.rst144
-rw-r--r--doc/nimc.rst78
-rw-r--r--doc/spawn.txt6
-rw-r--r--examples/tunit.nim2
-rw-r--r--koch.nim7
-rw-r--r--lib/core/macros.nim3
-rw-r--r--lib/deprecated/pure/actors.nim3
-rw-r--r--lib/deprecated/pure/sockets.nim2
-rw-r--r--lib/impure/db_mysql.nim35
-rw-r--r--lib/impure/db_postgres.nim7
-rw-r--r--lib/impure/db_sqlite.nim1
-rw-r--r--lib/impure/nre.nim12
-rw-r--r--lib/impure/nre/private/util.nim6
-rw-r--r--lib/impure/re.nim10
-rw-r--r--lib/js/jscore.nim113
-rw-r--r--lib/nimbase.h4
-rw-r--r--lib/packages/docutils/rstast.nim4
-rw-r--r--lib/packages/docutils/rstgen.nim16
-rw-r--r--lib/posix/posix_other.nim12
-rw-r--r--lib/pure/asyncfutures.nim4
-rw-r--r--lib/pure/asyncnet.nim6
-rw-r--r--lib/pure/collections/intsets.nim24
-rw-r--r--lib/pure/collections/sequtils.nim130
-rw-r--r--lib/pure/collections/tables.nim2
-rw-r--r--lib/pure/concurrency/cpuinfo.nim12
-rw-r--r--lib/pure/concurrency/threadpool.nim54
-rw-r--r--lib/pure/coro.nim18
-rw-r--r--lib/pure/distros.nim6
-rw-r--r--lib/pure/encodings.nim4
-rw-r--r--lib/pure/fenv.nim2
-rw-r--r--lib/pure/htmlparser.nim1
-rw-r--r--lib/pure/httpclient.nim31
-rw-r--r--lib/pure/ioselects/ioselectors_kqueue.nim5
-rw-r--r--lib/pure/ioselects/ioselectors_select.nim6
-rw-r--r--lib/pure/json.nim2
-rw-r--r--lib/pure/math.nim88
-rw-r--r--lib/pure/nativesockets.nim5
-rw-r--r--lib/pure/net.nim48
-rw-r--r--lib/pure/options.nim17
-rw-r--r--lib/pure/os.nim23
-rw-r--r--lib/pure/ospaths.nim44
-rw-r--r--lib/pure/parseopt2.nim6
-rw-r--r--lib/pure/parsexml.nim10
-rw-r--r--lib/pure/pegs.nim4
-rw-r--r--lib/pure/ropes.nim69
-rw-r--r--lib/pure/smtp.nim6
-rw-r--r--lib/pure/streams.nim5
-rw-r--r--lib/pure/strutils.nim46
-rw-r--r--lib/pure/sugar.nim38
-rw-r--r--lib/pure/times.nim204
-rw-r--r--lib/pure/unittest.nim19
-rw-r--r--lib/pure/xmldom.nim24
-rw-r--r--lib/pure/xmldomparser.nim4
-rw-r--r--lib/pure/xmltree.nim1
-rw-r--r--lib/system.nim185
-rw-r--r--lib/system/alloc.nim10
-rw-r--r--lib/system/ansi_c.nim13
-rw-r--r--lib/system/channels.nim2
-rw-r--r--lib/system/excpt.nim6
-rw-r--r--lib/system/gc_common.nim22
-rw-r--r--lib/system/jssys.nim2
-rw-r--r--lib/system/memtracker.nim12
-rw-r--r--lib/system/osalloc.nim3
-rw-r--r--lib/system/sysstr.nim2
-rw-r--r--lib/system/threads.nim16
-rw-r--r--lib/windows/winlean.nim2
-rw-r--r--nimsuggest/nimsuggest.nim6
-rw-r--r--nimsuggest/tester.nim12
-rw-r--r--nimsuggest/tests/tdot4.nim2
-rw-r--r--nimsuggest/tests/tinclude.nim8
-rw-r--r--readme.md25
-rw-r--r--tests/arithm/tashr.nim46
-rw-r--r--tests/async/tasync_misc.nim3
-rw-r--r--tests/async/tasyncssl.nim4
-rw-r--r--tests/async/tioselectors.nim4
-rw-r--r--tests/casestmt/tcasestm.nim18
-rw-r--r--tests/ccgbugs/pkg8616/rtarray.nim2
-rw-r--r--tests/ccgbugs/pkg8616/scheduler.nim10
-rw-r--r--tests/ccgbugs/t8616.nim4
-rw-r--r--tests/ccgbugs/topenarraycast.nim8
-rw-r--r--tests/closure/t8550.nim12
-rw-r--r--tests/compiles/t8630.nim13
-rw-r--r--tests/concepts/t3330.nim12
-rw-r--r--tests/converter/tconverter_with_constraint.nim20
-rw-r--r--tests/cpp/tasync_cpp.nim1
-rw-r--r--tests/distinct/tnil.nim15
-rw-r--r--tests/exception/tdont_overwrite_typename.nim4
-rw-r--r--tests/exprs/tstmtexprs.nim2
-rw-r--r--tests/generics/t6137.nim29
-rw-r--r--tests/generics/t7141.nim10
-rw-r--r--tests/generics/tgeneric3.nim4
-rw-r--r--tests/generics/tobjecttyperel.nim12
-rw-r--r--tests/js/tclosures.nim2
-rw-r--r--tests/js/testobjs.nim2
-rw-r--r--tests/js/tjsffi.nim2
-rw-r--r--tests/js/ttimes.nim10
-rw-r--r--tests/macros/tforloop_macro1.nim2
-rw-r--r--tests/macros/tlineinfo.nim14
-rw-r--r--tests/manyloc/argument_parser/argument_parser.nim10
-rw-r--r--tests/manyloc/keineschweine/lib/sg_assets.nim4
-rw-r--r--tests/manyloc/keineschweine/lib/sg_gui.nim4
-rw-r--r--tests/metatype/ttypeselectors.nim12
-rw-r--r--tests/notnil/tmust_compile.nim20
-rw-r--r--tests/notnil/tnotnil.nim12
-rw-r--r--tests/notnil/tnotnil1.nim15
-rw-r--r--tests/notnil/tnotnil_in_objconstr.nim8
-rw-r--r--tests/osproc/texitsignal.nim5
-rw-r--r--tests/osproc/tworkingdir.nim2
-rw-r--r--tests/parallel/tptr_to_ref.nim4
-rw-r--r--tests/parallel/tsendtwice.nim12
-rw-r--r--tests/parallel/twaitany.nim4
-rw-r--r--tests/pragmas/custom_pragma.nim2
-rw-r--r--tests/stdlib/nre/captures.nim6
-rw-r--r--tests/stdlib/nre/replace.nim4
-rw-r--r--tests/stdlib/tjsonmacro.nim4
-rw-r--r--tests/stdlib/tnilecho.nim8
-rw-r--r--tests/stdlib/tstrutil.nim9
-rw-r--r--tests/stdlib/tsugar.nim29
-rw-r--r--tests/stdlib/ttimes.nim30
-rw-r--r--tests/system/tnilconcats.nim2
-rw-r--r--tests/system/tostring.nim12
-rw-r--r--tests/template/mgensym_generic_cross_module.nim4
-rw-r--r--tests/template/tdefault_nil.nim2
-rw-r--r--tests/testament/categories.nim26
-rw-r--r--tests/testament/specs.nim4
-rw-r--r--tests/testament/tester.nim6
-rw-r--r--tests/threads/t8535.nim16
-rw-r--r--tests/threads/tactors.nim4
-rw-r--r--tests/tuples/tuple_with_nil.nim24
-rw-r--r--tests/tuples/tuple_with_seq.nim4
-rw-r--r--tests/types/temptyseqs.nim2
-rw-r--r--tests/vm/meta.nim22
-rw-r--r--tests/vm/tcomponent.nim2
-rw-r--r--tests/vm/tseq_badinit.nim4
-rw-r--r--tests/vm/tstringnil.nim8
-rw-r--r--tools/dochack/dochack.nim28
-rw-r--r--tools/dochack/karax.nim5
-rw-r--r--tools/niminst/deinstall.tmpl4
-rw-r--r--tools/niminst/install.tmpl5
-rw-r--r--tools/nimweb.nim10
-rw-r--r--tools/vccenv/vccenv.nim2
-rw-r--r--tools/vccenv/vccexe.nim12
-rw-r--r--tools/website.tmpl2
195 files changed, 2049 insertions, 1344 deletions
diff --git a/.travis.yml b/.travis.yml
index e9b76bf6a..b07de1df1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -51,5 +51,3 @@ script:
   - ./koch csource
   - ./koch nimsuggest
 #  - nim c -r nimsuggest/tester
-  - ( ! grep -F '.. code-block' -l -r --include '*.html' --exclude contributing.html --exclude docgen.html --exclude tut2.html )
-  - ( ! grep -F '..code-block' -l -r --include '*.html' --exclude contributing.html --exclude docgen.html --exclude tut2.html )
diff --git a/appveyor.yml b/appveyor.yml
index 5aa3798b1..cb4ac9e00 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -55,6 +55,7 @@ build_script:
   - nimble install jester@#head -y
   - nimble install niminst
   - nim c --taintMode:on -d:nimCoroutines tests/testament/tester
+  - nim c --taintMode:on -d:nimCoroutines --os:genode -d:posix --compileOnly tests/testament/tester
 
 test_script:
   - tests\testament\tester --pedantic all -d:nimCoroutines
diff --git a/build_all.sh b/build_all.sh
new file mode 100644
index 000000000..701d7d204
--- /dev/null
+++ b/build_all.sh
@@ -0,0 +1,31 @@
+#! /bin/sh
+
+# build development version of the compiler; can be rerun safely
+
+set -u # error on undefined variables
+set -e # exit on first error
+
+echo_run(){
+  echo "\n$@"
+  "$@"
+}
+
+[ -d csources ] || echo_run git clone --depth 1 https://github.com/nim-lang/csources.git
+
+nim_csources=bin/nim_csources
+build_nim_csources(){
+  ## avoid changing dir in case of failure
+  (
+    echo_run cd csources
+    echo_run sh build.sh
+  )
+  # keep $nim_csources in case needed to investigate bootstrap issues
+  # without having to rebuild from csources
+  echo_run cp bin/nim $nim_csources
+}
+
+[ -f $nim_csources ] || echo_run build_nim_csources
+
+echo_run bin/nim c koch
+echo_run ./koch boot -d:release
+echo_run ./koch tools # Compile Nimble and other tools.
diff --git a/changelog.md b/changelog.md
index 045286ef9..45bdb495d 100644
--- a/changelog.md
+++ b/changelog.md
@@ -20,7 +20,7 @@
 - The parser now warns about inconsistent spacing around binary operators as
   these can easily be confused with unary operators. This warning will likely
   become an error in the future.
-- The ``'c`` and ``'C'`` prefix for octal literals is now deprecated to
+- The ``'c`` and ``'C'`` suffix 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.
@@ -37,9 +37,18 @@
   strings anymore for its ``unit`` parameter. Instead the space is controlled
   by a new parameter ``useUnitSpace``.
 
+- The ``times.parse`` and ``times.format`` procs have been rewritten.
+  The proc signatures are the same so it should generally not break anything.
+  However, the new implementation is a bit stricter, which is a breaking change.
+  For example ``parse("2017-01-01 foo", "yyyy-MM-dd")`` will now raise an error.
+
 - ``proc `-`*(a, b: Time): int64`` in the ``times`` module has changed return type
   to ``times.Duration`` in order to support higher time resolutions.
   The proc is no longer deprecated.
+
+- The ``times.Timezone`` is now an immutable ref-type that must be initialized
+  with an explicit constructor (``newTimezone``).
+
 - ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``.
 
 - ``math.`mod` `` for floats now behaves the same as ``mod`` for integers
@@ -63,10 +72,17 @@
 - ``lineInfoObj`` now returns absolute path instead of project path.
   It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc.
 
+- `threadpool`'s `await` and derivatives have been renamed to `blockUntil`
+  to avoid confusions with `await` from the `async` macro.
+
+
 #### Breaking changes in the compiler
 
 - The undocumented ``#? braces`` parsing mode was removed.
 - The undocumented PHP backend was removed.
+- The default location of ``nimcache`` for the native code targets was
+  changed. Read [the compiler user guide](https://nim-lang.org/docs/nimc.html#generated-c-code-directory)
+  for more information.
 
 ### Library additions
 
@@ -88,9 +104,11 @@
 - Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals.
 - Added the proc ``math.prod`` for product of elements in openArray.
 - Added the proc ``parseBinInt`` to parse a binary integer from a string, which returns the value.
-- ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt``
+- ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt``.
 - Added the proc ``flush`` for memory mapped files.
 - Added the ``MemMapFileStream``.
+- Added ``macros.copyLineInfo`` to copy lineInfo from other node.
+- Added ``system.ashr`` an arithmetic right shift for integers.
 
 ### Library changes
 
@@ -129,7 +147,13 @@
 - ``func`` is now an alias for ``proc {.noSideEffect.}``.
 - In order to make ``for`` loops and iterators more flexible to use Nim now
   supports so called "for-loop macros". See
-  the `manual <manual.html#macros-for-loop-macros>`_ for more details.
+  the [manual](manual.html#macros-for-loop-macros) for more details.
+  This feature enables a Python-like generic ``enumerate`` implementation.
+
+- Case statements can now be rewritten via macros. See the [manual](manual.html#macros-case-statement-macros) for more information.
+  This feature enables custom pattern matchers.
+
+
 - the `typedesc` special type has been renamed to just `type`.
 - `static` and `type` are now also modifiers similar to `ref` and `ptr`.
   They denote the special types `static[T]` and `type[T]`.
@@ -150,7 +174,7 @@
   More details in language manual.
 
 - ``nil`` for strings/seqs is finally gone. Instead the default value for
-  these is ``"" / @[]``.
+  these is ``"" / @[]``. Use ``--nilseqs:on`` for a transition period.
 
 - Accessing the binary zero terminator in Nim's native strings
   is now invalid. Internally a Nim string still has the trailing zero for
@@ -160,11 +184,22 @@
 - The command syntax now supports keyword arguments after the first comma.
 
 - Thread-local variables can now be declared inside procs. This implies all
-  the effects of the `global` pragma.
+  the effects of the ``global`` pragma.
+
+- Nim now supports the ``except`` clause in the export statement.
+
+- Range float types, example ``range[0.0 .. Inf]``. More details in language manual.
+- The ``{.this.}`` pragma has been deprecated. It never worked within generics and
+  we found the resulting code harder to read than the more explicit ``obj.field``
+  syntax.
+- "Memory regions" for pointer types have been deprecated, they were hardly used
+  anywhere. Note that this has **nothing** to do with the ``--gc:regions`` switch
+  of managing memory.
 
-- Nim now supports `except` clause in the export statement.
+- The exception hierarchy was slightly reworked, ``SystemError`` was renamed to
+  ``CatchableError`` and is the new base class for any exception that is guaranteed to
+  be catchable. This change should have minimal impact on most existing Nim code.
 
-- Range float types, example `range[0.0 .. Inf]`. More details in language manual.
 
 ### Tool changes
 
@@ -173,7 +208,7 @@
 
 ### Compiler changes
 
-- The VM's instruction count limit was raised to 1 billion instructions in
+- The VM's instruction count limit was raised to 3 million instructions in
   order to support more complex computations at compile-time.
 
 - Support for hot code reloading has been implemented for the JavaScript
@@ -199,6 +234,16 @@
 - 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
+  This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch.
+
+- On Posix systems the global system wide configuration is now put under ``/etc/nim/nim.cfg``,
+  it used to be ``/etc/nim.cfg``. Usually it does not exist, however.
+
+- On Posix systems the user configuration is now looked under ``$XDG_CONFIG_HOME/nim/nim.cfg``
+  (if ``XDG_CONFIG_HOME`` is not defined, then under ``~/.config/nim/nim.cfg``). It used to be
+  ``$XDG_CONFIG_DIR/nim.cfg`` (and ``~/.config/nim.cfg``).
+
+  Similarly, on Windows, the user configuration is now looked under ``%APPDATA%/nim/nim.cfg``.
+  This used to be ``%APPDATA%/nim.cfg``.
 
 ### Bugfixes
diff --git a/compiler/ast.nim b/compiler/ast.nim
index a61ac055e..a722f63f6 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -591,7 +591,7 @@ type
     mAddI, mSubI, mMulI, mDivI, mModI,
     mSucc, mPred,
     mAddF64, mSubF64, mMulF64, mDivF64,
-    mShrI, mShlI, mBitandI, mBitorI, mBitxorI,
+    mShrI, mShlI, mAshrI, mBitandI, mBitorI, mBitxorI,
     mMinI, mMaxI,
     mMinF64, mMaxF64,
     mAddU, mSubU, mMulU, mDivU, mModU,
@@ -972,8 +972,8 @@ const
     tyFloat..tyFloat128, tyUInt..tyUInt64}
   ConstantDataTypes*: TTypeKinds = {tyArray, tySet,
                                     tyTuple, tySequence}
-  NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
-    tyProc, tyString, tyError}
+  NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr,
+    tyProc, tyError}
   ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType,
     skIterator,
     skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
@@ -1151,7 +1151,10 @@ proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
   for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
 
 proc discardSons*(father: PNode) =
-  father.sons = nil
+  when defined(nimNoNilSeqs):
+    father.sons = @[]
+  else:
+    father.sons = nil
 
 proc withInfo*(n: PNode, info: TLineInfo): PNode =
   n.info = info
@@ -1368,7 +1371,7 @@ proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo;
   result.loc = s.loc
   result.annex = s.annex
   # XXX once usedGenerics is used, ensure module aliases keep working!
-  assert s.usedGenerics == nil
+  assert s.usedGenerics.len == 0
 
 proc initStrTable*(x: var TStrTable) =
   x.counter = 0
@@ -1593,7 +1596,10 @@ proc getStr*(a: PNode): string =
   of nkStrLit..nkTripleStrLit: result = a.strVal
   of nkNilLit:
     # let's hope this fixes more problems than it creates:
-    result = nil
+    when defined(nimNoNilSeqs):
+      result = ""
+    else:
+      result = nil
   else:
     doAssert false, "getStr"
     #internalError(a.info, "getStr")
@@ -1675,6 +1681,14 @@ proc skipStmtList*(n: PNode): PNode =
   else:
     result = n
 
+proc toVar*(typ: PType): PType =
+  ## If ``typ`` is not a tyVar then it is converted into a `var <typ>` and
+  ## returned. Otherwise ``typ`` is simply returned as-is.
+  result = typ
+  if typ.kind != tyVar:
+    result = newType(tyVar, typ.owner)
+    rawAddSon(result, typ)
+
 proc toRef*(typ: PType): PType =
   ## If ``typ`` is a tyObject then it is converted into a `ref <typ>` and
   ## returned. Otherwise ``typ`` is simply returned as-is.
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 333376f6a..152802ba1 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -412,6 +412,8 @@ proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int;
         else:
           addf(result, ",$N$1\"ident\": null", [istr])
       else:
+        if renderType and n.typ != nil:
+          addf(result, ",$N$1\"typ\": $2", [istr, debugType(conf, n.typ, 2)])
         if sonsLen(n) > 0:
           addf(result, ",$N$1\"sons\": [", [istr])
           for i in countup(0, sonsLen(n) - 1):
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index ab15b9f2f..83461350b 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -435,7 +435,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
   assert(sonsLen(typ) == sonsLen(typ.n))
   # don't call '$' here for efficiency:
   let pat = ri.sons[0].sym.loc.r.data
-  internalAssert p.config, pat != nil
+  internalAssert p.config, pat.len > 0
   if pat.contains({'#', '(', '@', '\''}):
     var pl = genPatternCall(p, ri, pat, typ)
     # simpler version of 'fixupCall' that works with the pl+params combination:
@@ -484,7 +484,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
 
   # don't call '$' here for efficiency:
   let pat = ri.sons[0].sym.loc.r.data
-  internalAssert p.config, pat != nil
+  internalAssert p.config, pat.len > 0
   var start = 3
   if ' ' in pat:
     start = 1
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 5af0fe4e0..65cae8866 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -65,9 +65,10 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
     of tyString:
       # with the new semantics for 'nil' strings, we can map "" to nil and
       # save tons of allocations:
-      #if n.strVal.len == 0: result = genNilStringLiteral(p.module, n.info)
-      #else:
-      result = genStringLiteral(p.module, n)
+      if n.strVal.len == 0 and optNilSeqs notin p.options:
+        result = genNilStringLiteral(p.module, n.info)
+      else:
+        result = genStringLiteral(p.module, n)
     else:
       if n.strVal.isNil: result = rope("NIM_NIL")
       else: result = makeCString(n.strVal)
@@ -567,9 +568,9 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       "(($4)($1) - ($4)($2))", # SubF64
       "(($4)($1) * ($4)($2))", # MulF64
       "(($4)($1) / ($4)($2))", # DivF64
-
       "($4)((NU$5)($1) >> (NU$3)($2))", # ShrI
       "($4)((NU$3)($1) << (NU$3)($2))", # ShlI
+      "($4)((NI$3)($1) >> (NU$3)($2))", # AshrI
       "($4)($1 & $2)",      # BitandI
       "($4)($1 | $2)",      # BitorI
       "($4)($1 ^ $2)",      # BitxorI
@@ -996,7 +997,7 @@ proc genEcho(p: BProc, n: PNode) =
   # is threadsafe.
   internalAssert p.config, n.kind == nkBracket
   if p.config.target.targetOS == osGenode:
-    # bypass libc and print directly to the Genode LOG session
+    # echo directly to the Genode LOG session
     var args: Rope = nil
     var a: TLoc
     for it in n.sons:
@@ -1004,8 +1005,9 @@ proc genEcho(p: BProc, n: PNode) =
         add(args, ", \"\"")
       else:
         initLocExpr(p, it, a)
-        add(args, ropecg(p.module, ", #nimToCStringConv($1)", [rdLoc(a)]))
+        add(args, ropecg(p.module, ", Genode::Cstring($1->data, $1->len)", [rdLoc(a)]))
     p.module.includeHeader("<base/log.h>")
+    p.module.includeHeader("<util/string.h>")
     linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args)
   else:
     if n.len == 0:
@@ -1112,8 +1114,8 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
     initLoc(call, locCall, e, OnHeap)
     call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, rope(L)])
     genAssignment(p, dest, call, {})
+    gcUsage(p.config, e)
   add(p.s(cpsStmts), appends)
-  gcUsage(p.config, e)
 
 proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
   # seq &= x  -->
@@ -1201,7 +1203,7 @@ proc genNew(p: BProc, e: PNode) =
     rawGenNew(p, a, nil)
   gcUsage(p.config, e)
 
-proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
+proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) =
   let seqtype = skipTypes(dest.t, abstractVarRange)
   let args = [getTypeDesc(p.module, seqtype),
               genTypeInfo(p.module, seqtype, dest.lode.info), length]
@@ -1212,10 +1214,14 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
       linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc)
     else:
       linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", dest.rdLoc)
-    call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", args)
-    linefmt(p, cpsStmts, "$1 = $2;$n", dest.rdLoc, call.rdLoc)
+    if not lenIsZero:
+      call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", args)
+      linefmt(p, cpsStmts, "$1 = $2;$n", dest.rdLoc, call.rdLoc)
   else:
-    call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args)
+    if lenIsZero:
+      call.r = rope"NIM_NIL"
+    else:
+      call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args)
     genAssignment(p, dest, call, {})
 
 proc genNewSeq(p: BProc, e: PNode) =
@@ -1228,7 +1234,9 @@ proc genNewSeq(p: BProc, e: PNode) =
       a.rdLoc, b.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
       getSeqPayloadType(p.module, seqtype))
   else:
-    genNewSeqAux(p, a, b.rdLoc)
+    let lenIsZero = optNilSeqs notin p.options and
+      e[2].kind == nkIntLit and e[2].intVal == 0
+    genNewSeqAux(p, a, b.rdLoc, lenIsZero)
     gcUsage(p.config, e)
 
 proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
@@ -1328,7 +1336,8 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
   elif d.k == locNone:
     getTemp(p, n.typ, d)
   # generate call to newSeq before adding the elements per hand:
-  genNewSeqAux(p, dest[], intLiteral(sonsLen(n)))
+  genNewSeqAux(p, dest[], intLiteral(sonsLen(n)),
+    optNilSeqs notin p.options and n.len == 0)
   for i in countup(0, sonsLen(n) - 1):
     initLoc(arr, locExpr, n[i], OnHeap)
     arr.r = ropecg(p.module, "$1$3[$2]", rdLoc(dest[]), intLiteral(i), dataField(p))
@@ -1351,7 +1360,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
     getTemp(p, n.typ, d)
   # generate call to newSeq before adding the elements per hand:
   let L = int(lengthOrd(p.config, n.sons[1].typ))
-  genNewSeqAux(p, d, intLiteral(L))
+  genNewSeqAux(p, d, intLiteral(L), optNilSeqs notin p.options and L == 0)
   initLocExpr(p, n.sons[1], a)
   # bug #5007; do not produce excessive C source code:
   if L < 10:
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
index 664f89b73..067a60c57 100644
--- a/compiler/ccgmerge.nim
+++ b/compiler/ccgmerge.nim
@@ -58,7 +58,7 @@ proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
 
 proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
   if compilationCachePresent(conf):
-    result = rope(nil)
+    result = rope("")
     add(result, "\n/*\t")
     add(result, CProcSectionNames[ps])
     add(result, ":*/\n")
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 9a8d3bcd3..69e6558bb 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -256,7 +256,15 @@ proc genSingleVar(p: BProc, a: PNode) =
     # That's why we are doing the construction inside the preInitProc.
     # genObjectInit relies on the C runtime's guarantees that
     # global variables will be initialized to zero.
-    genObjectInit(p.module.preInitProc, cpsInit, v.typ, v.loc, true)
+    var loc = v.loc
+
+    # When the native TLS is unavailable, a global thread-local variable needs
+    # one more layer of indirection in order to access the TLS block.
+    # Only do this for complex types that may need a call to `objectInit`
+    if sfThread in v.flags and emulatedThreadVars(p.config) and
+      isComplexValueType(v.typ):
+      initLocExprSingleUse(p.module.preInitProc, vn, loc)
+    genObjectInit(p.module.preInitProc, cpsInit, v.typ, loc, true)
     # Alternative construction using default constructor (which may zeromem):
     # if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc)
     if sfExportc in v.flags and p.module.g.generatedHeader != nil:
@@ -1130,8 +1138,8 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
       patchAsgnStmtListExpr(patchedTree, e, ri)
       genStmts(p, patchedTree)
       return
-
     var a: TLoc
+    discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs))
     if le.kind in {nkDerefExpr, nkHiddenDeref}:
       genDeref(p, le, a, enforceDeref=true)
     else:
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 80c03f9e4..2cb431ff9 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -1147,12 +1147,29 @@ proc genInitCode(m: BModule) =
     appcg(m, m.s[cfsTypeInit1], "static #TNimType $1[$2];$n",
           [m.nimTypesName, rope(m.nimTypes)])
 
+  # Give this small function its own scope
+  addf(prc, "{$N", [])
+  block:
+    # Keep a bogus frame in case the code needs one
+    add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
+
+    add(prc, genSectionStart(cpsLocals, m.config))
+    add(prc, m.preInitProc.s(cpsLocals))
+    add(prc, genSectionEnd(cpsLocals, m.config))
+
+    add(prc, genSectionStart(cpsInit, m.config))
+    add(prc, m.preInitProc.s(cpsInit))
+    add(prc, genSectionEnd(cpsInit, m.config))
+
+    add(prc, genSectionStart(cpsStmts, m.config))
+    add(prc, m.preInitProc.s(cpsStmts))
+    add(prc, genSectionEnd(cpsStmts, m.config))
+  addf(prc, "}$N", [])
+
   add(prc, initGCFrame(m.initProc))
 
   add(prc, genSectionStart(cpsLocals, m.config))
-  add(prc, m.preInitProc.s(cpsLocals))
   add(prc, m.initProc.s(cpsLocals))
-  add(prc, m.postInitProc.s(cpsLocals))
   add(prc, genSectionEnd(cpsLocals, m.config))
 
   if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
@@ -1166,16 +1183,13 @@ proc genInitCode(m: BModule) =
       add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
 
   add(prc, genSectionStart(cpsInit, m.config))
-  add(prc, m.preInitProc.s(cpsInit))
   add(prc, m.initProc.s(cpsInit))
-  add(prc, m.postInitProc.s(cpsInit))
   add(prc, genSectionEnd(cpsInit, m.config))
 
   add(prc, genSectionStart(cpsStmts, m.config))
-  add(prc, m.preInitProc.s(cpsStmts))
   add(prc, m.initProc.s(cpsStmts))
-  add(prc, m.postInitProc.s(cpsStmts))
   add(prc, genSectionEnd(cpsStmts, m.config))
+
   if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
     add(prc, deinitFrame(m.initProc))
   add(prc, deinitGCFrame(m.initProc))
@@ -1221,11 +1235,6 @@ proc newPreInitProc(m: BModule): BProc =
   # little hack so that unique temporaries are generated:
   result.labels = 100_000
 
-proc newPostInitProc(m: BModule): BProc =
-  result = newProc(nil, m)
-  # little hack so that unique temporaries are generated:
-  result.labels = 200_000
-
 proc initProcOptions(m: BModule): TOptions =
   let opts = m.config.options
   if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts
@@ -1247,7 +1256,6 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
   result.initProc = newProc(nil, result)
   result.initProc.options = initProcOptions(result)
   result.preInitProc = newPreInitProc(result)
-  result.postInitProc = newPostInitProc(result)
   initNodeTable(result.dataCache)
   result.typeStack = @[]
   result.forwardedProcs = @[]
@@ -1258,7 +1266,6 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
   if sfSystemModule in module.flags:
     incl result.flags, preventStackTrace
     excl(result.preInitProc.options, optStackTrace)
-    excl(result.postInitProc.options, optStackTrace)
   let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi")
                 else: ""
   open(result.ndi, ndiName, g.config)
@@ -1276,7 +1283,6 @@ proc resetModule*(m: BModule) =
   m.initProc = newProc(nil, m)
   m.initProc.options = initProcOptions(m)
   m.preInitProc = newPreInitProc(m)
-  m.postInitProc = newPostInitProc(m)
   initNodeTable(m.dataCache)
   m.typeStack = @[]
   m.forwardedProcs = @[]
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index a526a0f00..56dbd65a2 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -147,7 +147,6 @@ type
     headerFiles*: seq[string] # needed headers to include
     typeInfoMarker*: TypeCache # needed for generating type information
     initProc*: BProc          # code for init procedure
-    postInitProc*: BProc      # code to be executed after the init proc
     preInitProc*: BProc       # code executed before the init proc
     typeStack*: TTypeSeq      # used for type generation
     dataCache*: TNodeTable
diff --git a/compiler/commands.nim b/compiler/commands.nim
index ef5a2a40f..f7c8cf9f2 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -279,6 +279,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
   of "implicitstatic": result = contains(conf.options, optImplicitStatic)
   of "patterns": result = contains(conf.options, optPatterns)
   of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
+  of "nilseqs": result = contains(conf.options, optNilSeqs)
   else: invalidCmdLineOption(conf, passCmd1, switch, info)
 
 proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
@@ -497,6 +498,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     else:
       localError(conf, info, errOnOrOffExpectedButXFound % arg)
   of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info)
+  of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info)
   of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
   of "floatchecks":
     processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info)
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index d853ce969..0cf264ac3 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -12,6 +12,8 @@
 import
   strtabs, platform, strutils, idents
 
+from options import Feature
+
 const
   catNone = "false"
 
@@ -74,3 +76,10 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimVmExportFixed")
   defineSymbol("nimNewRuntime")
   defineSymbol("nimIncrSeqV3")
+  defineSymbol("nimAshr")
+  defineSymbol("nimNoNilSeqs")
+  defineSymbol("nimNoNilSeqs2")
+
+  defineSymbol("nimHasNilSeqs")
+  for f in low(Feature)..high(Feature):
+    defineSymbol("nimHas" & $f)
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 8d233566c..b35452365 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -191,7 +191,7 @@ proc ropeFormatNamedVars(conf: ConfigRef; frmt: FormatStr,
 proc genComment(d: PDoc, n: PNode): string =
   result = ""
   var dummyHasToc: bool
-  if n.comment != nil:
+  if n.comment.len > 0:
     renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info),
                                toLinenumber(n.info), toColumn(n.info),
                                dummyHasToc, d.options, d.conf), result)
@@ -205,7 +205,8 @@ proc genRecComment(d: PDoc, n: PNode): Rope =
         result = genRecComment(d, n.sons[i])
         if result != nil: return
   else:
-    n.comment = nil
+    when defined(nimNoNilSeqs): n.comment = ""
+    else: n.comment = nil
 
 proc getPlainDocstring(n: PNode): string =
   ## Gets the plain text docstring of a node non destructively.
@@ -215,7 +216,7 @@ proc getPlainDocstring(n: PNode): string =
   ## the concatenated ``##`` comments of the node.
   result = ""
   if n == nil: return
-  if n.comment != nil and startsWith(n.comment, "##"):
+  if startsWith(n.comment, "##"):
     result = n.comment
   if result.len < 1:
     for i in countup(0, safeLen(n)-1):
@@ -564,9 +565,9 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
 
   result = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
                  "col": %n.info.col}
-  if comm != nil and comm != "":
+  if comm.len > 0:
     result["description"] = %comm
-  if r.buf != nil:
+  if r.buf.len > 0:
     result["code"] = %r.buf
 
 proc checkForFalse(n: PNode): bool =
@@ -634,7 +635,7 @@ proc add(d: PDoc; j: JsonNode) =
 proc generateJson*(d: PDoc, n: PNode) =
   case n.kind
   of nkCommentStmt:
-    if n.comment != nil and startsWith(n.comment, "##"):
+    if startsWith(n.comment, "##"):
       let stripped = n.comment.substr(2).strip
       d.add %{ "comment": %stripped, "line": %n.info.line.int,
                "col": %n.info.col }
@@ -678,7 +679,7 @@ proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string =
 proc generateTags*(d: PDoc, n: PNode, r: var Rope) =
   case n.kind
   of nkCommentStmt:
-    if n.comment != nil and startsWith(n.comment, "##"):
+    if startsWith(n.comment, "##"):
       let stripped = n.comment.substr(2).strip
       r.add stripped
   of nkProcDef:
diff --git a/compiler/idents.nim b/compiler/idents.nim
index 0a2f2d5cf..58800b73d 100644
--- a/compiler/idents.nim
+++ b/compiler/idents.nim
@@ -30,14 +30,7 @@ type
     wordCounter: int
     idAnon*, idDelegator*, emptyIdent*: PIdent
 
-when false:
-  var
-    legacy: IdentCache
-
-proc resetIdentCache*() =
-  when false:
-    for i in low(legacy.buckets)..high(legacy.buckets):
-      legacy.buckets[i] = nil
+proc resetIdentCache*() = discard
 
 proc cmpIgnoreStyle*(a, b: cstring, blen: int): int =
   if a[0] != b[0]: return 1
@@ -73,11 +66,9 @@ proc cmpExact(a, b: cstring, blen: int): int =
   if result == 0:
     if a[i] != '\0': result = 1
 
-{.this: self.}
-
-proc getIdent*(self: IdentCache; identifier: cstring, length: int, h: Hash): PIdent =
-  var idx = h and high(buckets)
-  result = buckets[idx]
+proc getIdent*(ic: IdentCache; identifier: cstring, length: int, h: Hash): PIdent =
+  var idx = h and high(ic.buckets)
+  result = ic.buckets[idx]
   var last: PIdent = nil
   var id = 0
   while result != nil:
@@ -85,8 +76,8 @@ proc getIdent*(self: IdentCache; identifier: cstring, length: int, h: Hash): PId
       if last != nil:
         # make access to last looked up identifier faster:
         last.next = result.next
-        result.next = buckets[idx]
-        buckets[idx] = result
+        result.next = ic.buckets[idx]
+        ic.buckets[idx] = result
       return
     elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0:
       assert((id == 0) or (id == result.id))
@@ -97,20 +88,20 @@ proc getIdent*(self: IdentCache; identifier: cstring, length: int, h: Hash): PId
   result.h = h
   result.s = newString(length)
   for i in countup(0, length - 1): result.s[i] = identifier[i]
-  result.next = buckets[idx]
-  buckets[idx] = result
+  result.next = ic.buckets[idx]
+  ic.buckets[idx] = result
   if id == 0:
-    inc(wordCounter)
-    result.id = -wordCounter
+    inc(ic.wordCounter)
+    result.id = -ic.wordCounter
   else:
     result.id = id
 
-proc getIdent*(self: IdentCache; identifier: string): PIdent =
-  result = getIdent(cstring(identifier), len(identifier),
+proc getIdent*(ic: IdentCache; identifier: string): PIdent =
+  result = getIdent(ic, cstring(identifier), len(identifier),
                     hashIgnoreStyle(identifier))
 
-proc getIdent*(self: IdentCache; identifier: string, h: Hash): PIdent =
-  result = getIdent(cstring(identifier), len(identifier), h)
+proc getIdent*(ic: IdentCache; identifier: string, h: Hash): PIdent =
+  result = getIdent(ic, cstring(identifier), len(identifier), h)
 
 proc newIdentCache*(): IdentCache =
   result = IdentCache()
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index ef54841ae..462c622aa 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -379,6 +379,7 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
     ["", "", "($1 / $2)", "($1 / $2)"], # DivF64
     ["", "", "", ""], # ShrI
     ["", "", "($1 << $2)", "($1 << $2)"], # ShlI
+    ["", "", "($1 >> $2)", "($1 >> $2)"], # AshrI
     ["", "", "($1 & $2)", "($1 & $2)"], # BitandI
     ["", "", "($1 | $2)", "($1 | $2)"], # BitorI
     ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
@@ -1266,7 +1267,7 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
   if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
   if sfInfixCall in f.flags:
     let pat = n.sons[0].sym.loc.r.data
-    internalAssert p.config, pat != nil
+    internalAssert p.config, pat.len > 0
     if pat.contains({'#', '(', '@'}):
       var typ = skipTypes(n.sons[0].typ, abstractInst)
       assert(typ.kind == tyProc)
@@ -1349,7 +1350,7 @@ proc arrayTypeForElemType(typ: PType): string =
   of tyUint8: "Uint8Array"
   of tyFloat32: "Float32Array"
   of tyFloat64, tyFloat: "Float64Array"
-  else: nil
+  else: ""
 
 proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
   var t = skipTypes(typ, abstractInst)
diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim
index 261dcb44e..c5a641713 100644
--- a/compiler/lineinfos.nim
+++ b/compiler/lineinfos.nim
@@ -92,7 +92,7 @@ const
     warnResultShadowed: "Special variable 'result' is shadowed.",
     warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
     warnUser: "$1",
-    hintSuccess: "operation successful",
+    hintSuccess: "operation successful: $#",
     hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
     hintCC: "CC: \'$1\'", # unused
     hintLineTooLong: "line too long",
@@ -164,13 +164,13 @@ type
   TNoteKinds* = set[TNoteKind]
 
 proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
-    result[3] = {low(TNoteKind)..high(TNoteKind)} - {}
-    result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext}
-    result[1] = result[2] - {warnShadowIdent, warnProveField, warnProveIndex,
-      warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
-      hintSource, hintGlobalVar, hintGCStats}
-    result[0] = result[1] - {hintSuccessX, hintConf, hintProcessing,
-      hintPattern, hintExecuting, hintLinking}
+  result[3] = {low(TNoteKind)..high(TNoteKind)} - {}
+  result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext}
+  result[1] = result[2] - {warnShadowIdent, warnProveField, warnProveIndex,
+    warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
+    hintSource, hintGlobalVar, hintGCStats}
+  result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
+    hintProcessing, hintPattern, hintExecuting, hintLinking}
 
 const
   NotesVerbosity* = computeNotesVerbosity()
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 87694988a..1e9d963fa 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -11,7 +11,7 @@
 
 import
   intsets, ast, astalgo, idents, semdata, types, msgs, options,
-  renderer, wordrecg, idgen, nimfix.prettybase, lineinfos, strutils
+  renderer, wordrecg, idgen, nimfix/prettybase, lineinfos, strutils
 
 proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
 
@@ -262,7 +262,7 @@ proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) =
     err.add "\nThis might be caused by a recursive module dependency: "
     err.add c.recursiveDep
     # prevent excessive errors for 'nim check'
-    c.recursiveDep = nil
+    c.recursiveDep = ""
   localError(c.config, info, errGenerated, err)
 
 proc lookUp*(c: PContext, n: PNode): PSym =
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 334cd1ae6..1eecc4176 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -65,10 +65,8 @@ type
 
 proc hash*(x: FileIndex): Hash {.borrow.}
 
-{.this: g.}
-
 proc stopCompile*(g: ModuleGraph): bool {.inline.} =
-  result = doStopCompile != nil and doStopCompile()
+  result = g.doStopCompile != nil and g.doStopCompile()
 
 proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
   result = newSym(skProc, getIdent(g.cache, name), nil, unknownLineInfo(), {})
@@ -98,44 +96,44 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
   result.cacheTables = initTable[string, BTree[string, PNode]]()
 
 proc resetAllModules*(g: ModuleGraph) =
-  initStrTable(packageSyms)
-  deps = initIntSet()
-  modules = @[]
-  importStack = @[]
-  inclToMod = initTable[FileIndex, FileIndex]()
-  usageSym = nil
-  owners = @[]
-  methods = @[]
-  initStrTable(compilerprocs)
-  initStrTable(exposed)
+  initStrTable(g.packageSyms)
+  g.deps = initIntSet()
+  g.modules = @[]
+  g.importStack = @[]
+  g.inclToMod = initTable[FileIndex, FileIndex]()
+  g.usageSym = nil
+  g.owners = @[]
+  g.methods = @[]
+  initStrTable(g.compilerprocs)
+  initStrTable(g.exposed)
 
 proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
-  if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len:
-    result = modules[fileIdx.int32]
+  if fileIdx.int32 >= 0 and fileIdx.int32 < g.modules.len:
+    result = g.modules[fileIdx.int32]
 
 proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
 
 proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
   assert m.position == m.info.fileIndex.int32
   addModuleDep(g.incr, g.config, m.info.fileIndex, dep, isIncludeFile = false)
-  if suggestMode:
-    deps.incl m.position.dependsOn(dep.int)
+  if g.suggestMode:
+    g.deps.incl m.position.dependsOn(dep.int)
     # we compute the transitive closure later when quering the graph lazily.
     # this improves efficiency quite a lot:
     #invalidTransitiveClosure = true
 
 proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) =
   addModuleDep(g.incr, g.config, module, includeFile, isIncludeFile = true)
-  discard hasKeyOrPut(inclToMod, includeFile, module)
+  discard hasKeyOrPut(g.inclToMod, includeFile, module)
 
 proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex =
   ## returns 'fileIdx' if the file belonging to this index is
   ## directly used as a module or else the module that first
   ## references this include file.
-  if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len and modules[fileIdx.int32] != nil:
+  if fileIdx.int32 >= 0 and fileIdx.int32 < g.modules.len and g.modules[fileIdx.int32] != nil:
     result = fileIdx
   else:
-    result = inclToMod.getOrDefault(fileIdx)
+    result = g.inclToMod.getOrDefault(fileIdx)
 
 proc transitiveClosure(g: var IntSet; n: int) =
   # warshall's algorithm
@@ -147,22 +145,22 @@ proc transitiveClosure(g: var IntSet; n: int) =
             g.incl i.dependsOn(j)
 
 proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
-  let m = getModule fileIdx
+  let m = g.getModule fileIdx
   if m != nil: incl m.flags, sfDirty
 
 proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
   # we need to mark its dependent modules D as dirty right away because after
   # nimsuggest is done with this module, the module's dirty flag will be
   # cleared but D still needs to be remembered as 'dirty'.
-  if invalidTransitiveClosure:
-    invalidTransitiveClosure = false
-    transitiveClosure(deps, modules.len)
+  if g.invalidTransitiveClosure:
+    g.invalidTransitiveClosure = false
+    transitiveClosure(g.deps, g.modules.len)
 
   # every module that *depends* on this file is also dirty:
-  for i in 0i32..<modules.len.int32:
-    let m = modules[i]
-    if m != nil and deps.contains(i.dependsOn(fileIdx.int)):
+  for i in 0i32..<g.modules.len.int32:
+    let m = g.modules[i]
+    if m != nil and g.deps.contains(i.dependsOn(fileIdx.int)):
       incl m.flags, sfDirty
 
 proc isDirty*(g: ModuleGraph; m: PSym): bool =
-  result = suggestMode and sfDirty in m.flags
+  result = g.suggestMode and sfDirty in m.flags
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index be2ece911..1d7939142 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -391,10 +391,10 @@ proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
 
   if conf.structuredErrorHook != nil:
     conf.structuredErrorHook(conf, unknownLineInfo(),
-      s & (if kind != nil: KindFormat % kind else: ""), sev)
+      s & (if kind.len > 0: KindFormat % kind else: ""), sev)
 
   if not ignoreMsgBecauseOfIdeTools(conf, msg):
-    if kind != nil:
+    if kind.len > 0:
       styledMsgWriteln(color, title, resetStyle, s,
                        KindColor, `%`(KindFormat, kind))
     else:
@@ -483,9 +483,9 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
 
   if not ignoreMsg:
     if conf.structuredErrorHook != nil:
-      conf.structuredErrorHook(conf, info, s & (if kind != nil: KindFormat % kind else: ""), sev)
+      conf.structuredErrorHook(conf, info, s & (if kind.len > 0: KindFormat % kind else: ""), sev)
     if not ignoreMsgBecauseOfIdeTools(conf, msg):
-      if kind != nil:
+      if kind.len > 0:
         styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s,
                          KindColor, `%`(KindFormat, kind))
       else:
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index d3b4645dc..1a8a0acb5 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -220,7 +220,7 @@ proc readConfigFile(
     return true
 
 proc getUserConfigPath(filename: string): string =
-  result = joinPath(getConfigDir(), filename)
+  result = joinPath([getConfigDir(), "nim", filename])
 
 proc getSystemConfigPath(conf: ConfigRef; filename: string): string =
   # try standard configuration file (installation did not distribute files
@@ -228,8 +228,8 @@ proc getSystemConfigPath(conf: ConfigRef; filename: string): string =
   let p = getPrefixDir(conf)
   result = joinPath([p, "config", filename])
   when defined(unix):
-    if not existsFile(result): result = joinPath([p, "etc", filename])
-    if not existsFile(result): result = "/etc/" & filename
+    if not existsFile(result): result = joinPath([p, "etc/nim", filename])
+    if not existsFile(result): result = "/etc/nim/" & filename
 
 proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) =
   setDefaultLibpath(conf)
diff --git a/compiler/options.nim b/compiler/options.nim
index a776961fc..04b14c65f 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -38,7 +38,8 @@ type                          # please make sure we have under 32 options
     optPatterns,              # en/disable pattern matching
     optMemTracker,
     optHotCodeReloading,
-    optLaxStrings
+    optLaxStrings,
+    optNilSeqs
 
   TOptions* = set[TOption]
   TGlobalOption* = enum       # **keep binary compatible**
@@ -110,14 +111,16 @@ type
     ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
     ideHighlight, ideOutline, ideKnown, ideMsg
 
-  Feature* = enum  ## experimental features
+  Feature* = enum  ## experimental features; DO NOT RENAME THESE!
     implicitDeref,
     dotOperators,
     callOperator,
     parallel,
     destructor,
     notnil,
-    dynamicBindSym
+    dynamicBindSym,
+    forLoopMacros,
+    caseStmtMacros
 
   SymbolFilesOption* = enum
     disabledSf, writeOnlySf, readOnlySf, v2Sf
@@ -354,6 +357,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool =
     of "msdos": result = conf.target.targetOS == osDos
     of "mswindows", "win32": result = conf.target.targetOS == osWindows
     of "macintosh": result = conf.target.targetOS in {osMacos, osMacosx}
+    of "osx": result = conf.target.targetOS == osMacosx
     of "sunos": result = conf.target.targetOS == osSolaris
     of "nintendoswitch":
       result = conf.target.targetOS == osNintendoSwitch
@@ -479,9 +483,20 @@ proc disableNimblePath*(conf: ConfigRef) =
 
 include packagehandling
 
+proc getOsCacheDir(): string =
+  when defined(posix):
+    result = getEnv("XDG_CACHE_HOME", getHomeDir() / ".cache") / "nim"
+  else:
+    result = getHomeDir() / genSubDir
+
 proc getNimcacheDir*(conf: ConfigRef): string =
-  result = if conf.nimcacheDir.len > 0: conf.nimcacheDir
-           else: shortenDir(conf, conf.projectPath) / genSubDir
+  # XXX projectName should always be without a file extension!
+  result = if conf.nimcacheDir.len > 0:
+             conf.nimcacheDir
+           elif conf.cmd == cmdCompileToJS:
+             shortenDir(conf, conf.projectPath) / genSubDir
+           else: getOsCacheDir() / splitFile(conf.projectName).name &
+             (if isDefined(conf, "release"): "_r" else: "_d")
 
 proc pathSubs*(conf: ConfigRef; p, config: string): string =
   let home = removeTrailingDirSep(os.getHomeDir())
@@ -588,18 +603,22 @@ proc findModule*(conf: ConfigRef; modulename, currentModule: string): string =
 proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
   const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
   var candidates: seq[string] = @[]
-  for k, f in os.walkDir(pkg, relative=true):
-    if k == pcFile and f != "config.nims":
-      let (_, name, ext) = splitFile(f)
-      if ext in extensions:
-        let x = changeFileExt(pkg / name, ".nim")
-        if fileExists(x):
-          candidates.add x
-  for c in candidates:
-    # nim-foo foo  or  foo  nfoo
-    if (pkg in c) or (c in pkg): return c
-  if candidates.len >= 1:
-    return candidates[0]
+  var dir = pkg
+  while true:
+    for k, f in os.walkDir(dir, relative=true):
+      if k == pcFile and f != "config.nims":
+        let (_, name, ext) = splitFile(f)
+        if ext in extensions:
+          let x = changeFileExt(dir / name, ".nim")
+          if fileExists(x):
+            candidates.add x
+    for c in candidates:
+      # nim-foo foo  or  foo  nfoo
+      if (pkg in c) or (c in pkg): return c
+    if candidates.len >= 1:
+      return candidates[0]
+    dir = parentDir(dir)
+    if dir == "": break
   return ""
 
 proc canonDynlibName(s: string): string =
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index 944aec048..bbaf7a069 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -217,7 +217,7 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
     if n.typ != nil and n.typ.kind == tyVar:
       result = arLValue
   of nkSym:
-    let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet}
+    let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet, skForVar}
                 else: {skVar, skResult, skTemp}
     if n.sym.kind in kinds:
       if owner != nil and owner == n.sym.owner and
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 5664a9f67..98ccb05b9 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -150,7 +150,8 @@ template sameOrNoInd(p): bool = p.tok.indent == p.currInd or p.tok.indent < 0
 proc rawSkipComment(p: var TParser, node: PNode) =
   if p.tok.tokType == tkComment:
     if node != nil:
-      if node.comment == nil: node.comment = ""
+      when not defined(nimNoNilSeqs):
+        if node.comment == nil: node.comment = ""
       when defined(nimpretty):
         if p.tok.commentOffsetB > p.tok.commentOffsetA:
           add node.comment, fileSection(p.lex.config, p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB)
diff --git a/compiler/patterns.nim b/compiler/patterns.nim
index 2d2aeba76..ebb3a7c1d 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -21,14 +21,17 @@ type
     formals: int
     c: PContext
     subMatch: bool       # subnode matches are special
+    mappingIsFull: bool
   PPatternContext = var TPatternContext
 
 proc getLazy(c: PPatternContext, sym: PSym): PNode =
-  if not isNil(c.mapping):
+  if c.mappingIsFull:
     result = c.mapping[sym.position]
 
 proc putLazy(c: PPatternContext, sym: PSym, n: PNode) =
-  if isNil(c.mapping): newSeq(c.mapping, c.formals)
+  if not c.mappingIsFull:
+    newSeq(c.mapping, c.formals)
+    c.mappingIsFull = true
   c.mapping[sym.position] = n
 
 proc matches(c: PPatternContext, p, n: PNode): bool
@@ -209,7 +212,11 @@ proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
     for j in 0 ..< p.len:
       if not matches(c, p.sons[j], n.sons[i+j]):
         # we need to undo any bindings:
-        if not isNil(c.mapping): c.mapping = nil
+        when defined(nimNoNilSeqs):
+          c.mapping = @[]
+          c.mappingIsFull = false
+        else:
+          if not isNil(c.mapping): c.mapping = nil
         return false
     result = true
 
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index afe60e9dd..a067f2074 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -1066,8 +1066,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wThis:
         if it.kind in nkPragmaCallKinds and it.len == 2:
           c.selfName = considerQuotedIdent(c, it[1])
+          message(c.config, n.info, warnDeprecated, "the '.this' pragma")
         elif it.kind == nkIdent or it.len == 1:
           c.selfName = getIdent(c.cache, "self")
+          message(c.config, n.info, warnDeprecated, "the '.this' pragma")
         else:
           localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments")
       of wNoRewrite:
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index ce27e1cd9..c3e151f5a 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -281,7 +281,7 @@ const
 
 proc shouldRenderComment(g: var TSrcGen, n: PNode): bool =
   result = false
-  if n.comment != nil:
+  if n.comment.len > 0:
     result = (renderNoComments notin g.flags) or
         (renderDocComments in g.flags)
 
@@ -402,7 +402,7 @@ proc lsons(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
 proc lsub(g: TSrcGen; n: PNode): int =
   # computes the length of a tree
   if isNil(n): return 0
-  if n.comment != nil: return MaxLineLen + 1
+  if n.comment.len > 0: return MaxLineLen + 1
   case n.kind
   of nkEmpty: result = 0
   of nkTripleStrLit:
@@ -500,7 +500,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
   of nkBreakStmt: result = lsub(g, n.sons[0]) + len("break_")
   of nkContinueStmt: result = lsub(g, n.sons[0]) + len("continue_")
   of nkPragma: result = lcomma(g, n) + 4
-  of nkCommentStmt: result = if n.comment.isNil: 0 else: len(n.comment)
+  of nkCommentStmt: result = len(n.comment)
   of nkOfBranch: result = lcomma(g, n, 0, - 2) + lsub(g, lastSon(n)) + len("of_:_")
   of nkImportAs: result = lsub(g, n.sons[0]) + len("_as_") + lsub(g, n.sons[1])
   of nkElifBranch: result = lsons(g, n) + len("elif_:_")
@@ -539,7 +539,7 @@ proc gsub(g: var TSrcGen, n: PNode) =
 proc hasCom(n: PNode): bool =
   result = false
   if n.isNil: return false
-  if n.comment != nil: return true
+  if n.comment.len > 0: return true
   case n.kind
   of nkEmpty..nkNilLit: discard
   else:
@@ -602,7 +602,7 @@ proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TTokType,
   dedent(g)
 
 proc longMode(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): bool =
-  result = n.comment != nil
+  result = n.comment.len > 0
   if not result:
     # check further
     for i in countup(start, sonsLen(n) + theEnd):
@@ -637,7 +637,7 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
 proc gcond(g: var TSrcGen, n: PNode) =
   if n.kind == nkStmtListExpr:
     put(g, tkParLe, "(")
-  gsub(g, n)  
+  gsub(g, n)
   if n.kind == nkStmtListExpr:
     put(g, tkParRi, ")")
 
@@ -864,7 +864,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   if isNil(n): return
   var
     a: TContext
-  if n.comment != nil: pushCom(g, n)
+  if n.comment.len > 0: pushCom(g, n)
   case n.kind                 # atoms:
   of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n))
   of nkEmpty: discard
@@ -1079,7 +1079,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
                 elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
                 else: nil
       var n_next = n[1]
-      while n_next.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, 
+      while n_next.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref,
                   nkStringToCString, nkCStringToString} and n_next.len > 0:
         n_next = n_next[0]
       if n_next.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim
index 66d7f63c2..a774cdba7 100644
--- a/compiler/rodutils.nim
+++ b/compiler/rodutils.nim
@@ -10,6 +10,27 @@
 ## Serialization utilities for the compiler.
 import strutils, math
 
+# MSVC prior to 2013 doesn't have C99 functions
+when defined(windows) and (defined(vcc) or defined(bcc)):
+  {.emit: """#if defined(_MSC_VER) && _MSC_VER < 1900
+  #include <stdarg.h>
+  static int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) {
+    int count = -1;
+    if (size != 0) count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
+    if (count == -1) count = _vscprintf(format, ap);
+    return count;
+  }
+  int snprintf(char *outBuf, size_t size, const char *format, ...) {
+    int count;
+    va_list ap;
+    va_start(ap, format);
+    count = c99_vsnprintf(outBuf, size, format, ap);
+    va_end(ap);
+    return count;
+  }
+  #endif
+  """.}
+
 proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
 
 proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index 973f16916..81ee01dbf 100644
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -66,29 +66,19 @@ type
   Rope* = ref RopeObj
   RopeObj*{.acyclic.} = object of RootObj # the empty rope is represented
                                           # by nil to safe space
-    left*, right*: Rope
-    length*: int
-    data*: string             # != nil if a leaf
+    left, right: Rope
+    L: int                    # <= 0 if a leaf
+    data*: string
 
 proc len*(a: Rope): int =
   ## the rope's length
   if a == nil: result = 0
-  else: result = a.length
+  else: result = abs a.L
 
-proc newRope(data: string = nil): Rope =
+proc newRope(data: string = ""): Rope =
   new(result)
-  if data != nil:
-    result.length = len(data)
-    result.data = data
-
-proc newMutableRope*(capacity = 30): Rope =
-  ## creates a new rope that supports direct modifications of the rope's
-  ## 'data' and 'length' fields.
-  new(result)
-  result.data = newStringOfCap(capacity)
-
-proc freezeMutableRope*(r: Rope) {.inline.} =
-  r.length = r.data.len
+  result.L = -len(data)
+  result.data = data
 
 var
   cache: array[0..2048*2 - 1, Rope] # XXX Global here!
@@ -147,7 +137,7 @@ proc `&`*(a, b: Rope): Rope =
     result = a
   else:
     result = newRope()
-    result.length = a.length + b.length
+    result.L = abs(a.L) + abs(b.L)
     result.left = a
     result.right = b
 
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index ae7e030b8..659206a40 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -45,7 +45,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
   template cbos(name, body) {.dirty.} =
     result.registerCallback "stdlib.system." & astToStr(name),
       proc (a: VmArgs) =
-        errorMsg = nil
+        errorMsg = ""
         try:
           body
         except OSError:
@@ -159,8 +159,11 @@ proc runNimScript*(cache: IdentCache; scriptName: string;
 
   defineSymbol(conf.symbols, "nimscript")
   defineSymbol(conf.symbols, "nimconfig")
-  registerPass(graph, semPass)
-  registerPass(graph, evalPass)
+  var registeredPasses {.global.} = false
+  if not registeredPasses:
+    registerPass(graph, semPass)
+    registerPass(graph, evalPass)
+    registeredPasses = true
 
   conf.searchPaths.add(conf.libpath)
 
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 2c3fab5a5..6128c02d1 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -16,12 +16,12 @@ import
   procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch,
   intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
   evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
-  semparallel, lowerings, pluginsupport, plugins.active, rod, lineinfos
+  semparallel, lowerings, pluginsupport, plugins/active, rod, lineinfos
 
 from modulegraphs import ModuleGraph
 
 when defined(nimfix):
-  import nimfix.prettybase
+  import nimfix/prettybase
 
 # implementation
 
@@ -119,7 +119,7 @@ proc commonType*(x, y: PType): PType =
   elif b.kind == tyStmt: result = b
   elif a.kind == tyTypeDesc:
     # turn any concrete typedesc into the abstract typedesc type
-    if a.sons == nil: result = a
+    if a.len == 0: result = a
     else:
       result = newType(tyTypeDesc, a.owner)
       rawAddSon(result, newType(tyNone, a.owner))
@@ -621,7 +621,8 @@ proc testExamples(c: PContext) =
   if os.execShellCmd(os.getAppFilename() & " " & backend & " --nimcache:" & nimcache & " -r " & outp) != 0:
     quit "[Examples] failed: see " & outp
   else:
-    removeFile(outp)
+    # keep generated source file `outp` to allow inspection.
+    rawMessage(c.config, hintSuccess, ["runnableExamples: " & outp])
     removeFile(outp.changeFileExt(ExeExt))
     try:
       removeDir(nimcache)
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index aa5394a71..ef452fcdc 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -89,7 +89,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
       continue
     determineType(c, sym)
     initCandidate(c, z, sym, initialBinding, scope, diagnosticsFlag)
-    if c.currentScope.symbols.counter == counterInitial or syms != nil:
+    if c.currentScope.symbols.counter == counterInitial or syms.len != 0:
       matches(c, n, orig, z)
       if z.state == csMatch:
         #if sym.name.s == "==" and (n.info ?? "temp3"):
@@ -237,7 +237,7 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
     if symx.kind in routineKinds:
       errors.add(CandidateError(sym: symx,
                                 unmatchedVarParam: 0, firstMismatch: 0,
-                                diagnostics: nil,
+                                diagnostics: @[],
                                 enabled: false))
     symx = nextOverloadIter(o, c, headSymbol)
   if errors.len == 0:
@@ -455,7 +455,7 @@ proc tryDeref(n: PNode): PNode =
 
 proc semOverloadedCall(c: PContext, n, nOrig: PNode,
                        filter: TSymKinds, flags: TExprFlags): PNode =
-  var errors: CandidateErrors = if efExplain in flags: @[] else: nil
+  var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil
   var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
   if r.state == csMatch:
     # this may be triggered, when the explain pragma is used
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index aa0cb6e8e..4189a5214 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -37,6 +37,7 @@ type
                               # in standalone ``except`` and ``finally``
     next*: PProcCon           # used for stacking procedure contexts
     wasForwarded*: bool       # whether the current proc has a separate header
+    mappingExists*: bool
     mapping*: TIdTable
 
   TMatchedConcept* = object
@@ -176,12 +177,14 @@ proc lastOptionEntry*(c: PContext): POptionEntry =
 proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
 
 proc put*(p: PProcCon; key, val: PSym) =
-  if p.mapping.data == nil: initIdTable(p.mapping)
+  if not p.mappingExists:
+    initIdTable(p.mapping)
+    p.mappingExists = true
   #echo "put into table ", key.info
   p.mapping.idTablePut(key, val)
 
 proc get*(p: PProcCon; key: PSym): PSym =
-  if p.mapping.data == nil: return nil
+  if not p.mappingExists: return nil
   result = PSym(p.mapping.idTableGet(key))
 
 proc getGenSym*(c: PContext; s: PSym): PSym =
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index d2abfac13..a6c185fdc 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -268,6 +268,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
     of tyInt64, tyInt, tyUInt..tyUInt64:
       result = newIntNodeT(`shr`(getInt(a), getInt(b)), n, g)
     else: internalError(g.config, n.info, "constant folding for shr")
+  of mAshrI:
+    case skipTypes(n.typ, abstractRange).kind
+    of tyInt8: result = newIntNodeT(ashr(int8(getInt(a)), int8(getInt(b))), n, g)
+    of tyInt16: result = newIntNodeT(ashr(int16(getInt(a)), int16(getInt(b))), n, g)
+    of tyInt32: result = newIntNodeT(ashr(int32(getInt(a)), int32(getInt(b))), n, g)
+    of tyInt64, tyInt:
+      result = newIntNodeT(ashr(getInt(a), getInt(b)), n, g)
+    else: internalError(g.config, n.info, "constant folding for ashr")
   of mDivI: result = foldDiv(getInt(a), getInt(b), n, g)
   of mModI: result = foldMod(getInt(a), getInt(b), n, g)
   of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n, g)
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index fac04e3a0..f9d7c3754 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -97,10 +97,9 @@ proc sameInstantiation(a, b: TInstantiation): bool =
 
 proc genericCacheGet(genericSym: PSym, entry: TInstantiation;
                      id: CompilesId): PSym =
-  if genericSym.procInstCache != nil:
-    for inst in genericSym.procInstCache:
-      if inst.compilesId == id and sameInstantiation(entry, inst[]):
-        return inst.sym
+  for inst in genericSym.procInstCache:
+    if inst.compilesId == id and sameInstantiation(entry, inst[]):
+      return inst.sym
 
 when false:
   proc `$`(x: PSym): string =
diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim
index 02c56c035..3056f5d72 100644
--- a/compiler/semmacrosanity.nim
+++ b/compiler/semmacrosanity.nim
@@ -88,7 +88,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
     else:
       globalError(conf, n.info, "string literal must be of some string type")
   of nkNilLit:
-    if x.kind in NilableTypes:
+    if x.kind in NilableTypes+{tyString, tySequence}:
       n.typ = t
     else:
       globalError(conf, n.info, "nil literal must be of some pointer type")
diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim
index 8b639806d..90ab2c57a 100644
--- a/compiler/semobjconstr.nim
+++ b/compiler/semobjconstr.nim
@@ -121,7 +121,7 @@ proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string
     if {tfNotNil, tfNeedsInit} * r.sym.typ.flags != {}:
       let assignment = locateFieldInInitExpr(c, r.sym, initExpr)
       if assignment == nil:
-        if result == nil:
+        if result.len == 0:
           result = r.sym.name.s
         else:
           result.add ", "
@@ -129,7 +129,7 @@ proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string
 
 proc checkForMissingFields(c: PContext, recList, initExpr: PNode) =
   let missing = missingMandatoryFields(c, recList, initExpr)
-  if missing != nil:
+  if missing.len > 0:
     localError(c.config, initExpr.info, "fields not initialized: $1.", [missing])
 
 proc semConstructFields(c: PContext, recNode: PNode,
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index f7d8b6b7b..3a1278137 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -178,71 +178,6 @@ proc semIf(c: PContext, n: PNode): PNode =
     result.kind = nkIfExpr
     result.typ = typ
 
-proc semCase(c: PContext, n: PNode): PNode =
-  result = n
-  checkMinSonsLen(n, 2, c.config)
-  openScope(c)
-  n.sons[0] = semExprWithType(c, n.sons[0])
-  var chckCovered = false
-  var covered: BiggestInt = 0
-  var typ = commonTypeBegin
-  var hasElse = false
-  let caseTyp = skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc})
-  case caseTyp.kind
-  of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool:
-    chckCovered = true
-  of tyFloat..tyFloat128, tyString, tyError:
-    discard
-  else:
-    localError(c.config, n.info, errSelectorMustBeOfCertainTypes)
-    return
-  for i in countup(1, sonsLen(n) - 1):
-    var x = n.sons[i]
-    when defined(nimsuggest):
-      if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, x.info) and caseTyp.kind == tyEnum:
-        suggestEnum(c, x, caseTyp)
-    case x.kind
-    of nkOfBranch:
-      checkMinSonsLen(x, 2, c.config)
-      semCaseBranch(c, n, x, i, covered)
-      var last = sonsLen(x)-1
-      x.sons[last] = semExprBranchScope(c, x.sons[last])
-      typ = commonType(typ, x.sons[last])
-    of nkElifBranch:
-      chckCovered = false
-      checkSonsLen(x, 2, c.config)
-      openScope(c)
-      x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
-      x.sons[1] = semExprBranch(c, x.sons[1])
-      typ = commonType(typ, x.sons[1])
-      closeScope(c)
-    of nkElse:
-      chckCovered = false
-      checkSonsLen(x, 1, c.config)
-      x.sons[0] = semExprBranchScope(c, x.sons[0])
-      typ = commonType(typ, x.sons[0])
-      hasElse = true
-    else:
-      illFormedAst(x, c.config)
-  if chckCovered:
-    if covered == toCover(c, n.sons[0].typ):
-      hasElse = true
-    else:
-      localError(c.config, n.info, "not all cases are covered")
-  closeScope(c)
-  if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
-    for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
-    # propagate any enforced VoidContext:
-    if typ == c.enforceVoidContext:
-      result.typ = c.enforceVoidContext
-  else:
-    for i in 1..n.len-1:
-      var it = n.sons[i]
-      let j = it.len-1
-      if not endsInNoReturn(it.sons[j]):
-        it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
-    result.typ = typ
-
 proc semTry(c: PContext, n: PNode): PNode =
 
   var check = initIntSet()
@@ -683,29 +618,28 @@ proc isTrivalStmtExpr(n: PNode): bool =
       return false
   result = true
 
-proc handleForLoopMacro(c: PContext; n: PNode): PNode =
-  let iterExpr = n[^2]
-  if iterExpr.kind in nkCallKinds:
+proc handleStmtMacro(c: PContext; n, selector: PNode; magicType: string): PNode =
+  if selector.kind in nkCallKinds:
     # we transform
     # n := for a, b, c in m(x, y, z): Y
     # to
     # m(n)
-    let forLoopStmt = magicsys.getCompilerProc(c.graph, "ForLoopStmt")
-    if forLoopStmt == nil: return
+    let maType = magicsys.getCompilerProc(c.graph, magicType)
+    if maType == nil: return
 
-    let headSymbol = iterExpr[0]
+    let headSymbol = selector[0]
     var o: TOverloadIter
     var match: PSym = nil
     var symx = initOverloadIter(o, c, headSymbol)
     while symx != nil:
       if symx.kind in {skTemplate, skMacro}:
-        if symx.typ.len == 2 and symx.typ[1] == forLoopStmt.typ:
+        if symx.typ.len == 2 and symx.typ[1] == maType.typ:
           if match == nil:
             match = symx
           else:
             localError(c.config, n.info, errAmbiguousCallXYZ % [
               getProcHeader(c.config, match),
-              getProcHeader(c.config, symx), $iterExpr])
+              getProcHeader(c.config, symx), $selector])
       symx = nextOverloadIter(o, c, headSymbol)
 
     if match == nil: return
@@ -717,11 +651,44 @@ proc handleForLoopMacro(c: PContext; n: PNode): PNode =
     of skTemplate: result = semTemplateExpr(c, callExpr, match, {})
     else: result = nil
 
+proc handleForLoopMacro(c: PContext; n: PNode): PNode =
+  result = handleStmtMacro(c, n, n[^2], "ForLoopStmt")
+
+proc handleCaseStmtMacro(c: PContext; n: PNode): PNode =
+  # n[0] has been sem'checked and has a type. We use this to resolve
+  # 'match(n[0])' but then we pass 'n' to the 'match' macro. This seems to
+  # be the best solution.
+  var toResolve = newNodeI(nkCall, n.info)
+  toResolve.add newIdentNode(getIdent(c.cache, "match"), n.info)
+  toResolve.add n[0]
+
+  var errors: CandidateErrors
+  var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {},
+                           errors, false)
+  if r.state == csMatch:
+    var match = r.calleeSym
+    markUsed(c.config, n[0].info, match, c.graph.usageSym)
+    styleCheckUse(n[0].info, match)
+
+    # but pass 'n' to the 'match' macro, not 'n[0]':
+    r.call.sons[1] = n
+    let toExpand = semResolvedCall(c, r, r.call, {})
+    case match.kind
+    of skMacro: result = semMacroExpr(c, toExpand, toExpand, match, {})
+    of skTemplate: result = semTemplateExpr(c, toExpand, match, {})
+    else: result = nil
+  # this would be the perfectly consistent solution with 'for loop macros',
+  # but it kinda sucks for pattern matching as the matcher is not attached to
+  # a type then:
+  when false:
+    result = handleStmtMacro(c, n, n[0], "CaseStmt")
+
 proc semFor(c: PContext, n: PNode): PNode =
   checkMinSonsLen(n, 3, c.config)
   var length = sonsLen(n)
-  result = handleForLoopMacro(c, n)
-  if result != nil: return result
+  if forLoopMacros in c.features:
+    result = handleForLoopMacro(c, n)
+    if result != nil: return result
   openScope(c)
   result = n
   n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
@@ -757,6 +724,75 @@ proc semFor(c: PContext, n: PNode): PNode =
     result.typ = c.enforceVoidContext
   closeScope(c)
 
+proc semCase(c: PContext, n: PNode): PNode =
+  result = n
+  checkMinSonsLen(n, 2, c.config)
+  openScope(c)
+  n.sons[0] = semExprWithType(c, n.sons[0])
+  var chckCovered = false
+  var covered: BiggestInt = 0
+  var typ = commonTypeBegin
+  var hasElse = false
+  let caseTyp = skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc})
+  case caseTyp.kind
+  of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool:
+    chckCovered = true
+  of tyFloat..tyFloat128, tyString, tyError:
+    discard
+  else:
+    if caseStmtMacros in c.features:
+      result = handleCaseStmtMacro(c, n)
+      if result != nil: return result
+
+    localError(c.config, n.info, errSelectorMustBeOfCertainTypes)
+    return
+  for i in countup(1, sonsLen(n) - 1):
+    var x = n.sons[i]
+    when defined(nimsuggest):
+      if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, x.info) and caseTyp.kind == tyEnum:
+        suggestEnum(c, x, caseTyp)
+    case x.kind
+    of nkOfBranch:
+      checkMinSonsLen(x, 2, c.config)
+      semCaseBranch(c, n, x, i, covered)
+      var last = sonsLen(x)-1
+      x.sons[last] = semExprBranchScope(c, x.sons[last])
+      typ = commonType(typ, x.sons[last])
+    of nkElifBranch:
+      chckCovered = false
+      checkSonsLen(x, 2, c.config)
+      openScope(c)
+      x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
+      x.sons[1] = semExprBranch(c, x.sons[1])
+      typ = commonType(typ, x.sons[1])
+      closeScope(c)
+    of nkElse:
+      chckCovered = false
+      checkSonsLen(x, 1, c.config)
+      x.sons[0] = semExprBranchScope(c, x.sons[0])
+      typ = commonType(typ, x.sons[0])
+      hasElse = true
+    else:
+      illFormedAst(x, c.config)
+  if chckCovered:
+    if covered == toCover(c, n.sons[0].typ):
+      hasElse = true
+    else:
+      localError(c.config, n.info, "not all cases are covered")
+  closeScope(c)
+  if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
+    for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
+    # propagate any enforced VoidContext:
+    if typ == c.enforceVoidContext:
+      result.typ = c.enforceVoidContext
+  else:
+    for i in 1..n.len-1:
+      var it = n.sons[i]
+      let j = it.len-1
+      if not endsInNoReturn(it.sons[j]):
+        it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
+    result.typ = typ
+
 proc semRaise(c: PContext, n: PNode): PNode =
   result = n
   checkSonsLen(n, 1, c.config)
@@ -970,7 +1006,10 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
             var body = s.typ.lastSon
             if body.kind == tyObject:
               # erases all declared fields
-              body.n.sons = nil
+              when defined(nimNoNilSeqs):
+                body.n.sons = @[]
+              else:
+                body.n.sons = nil
 
       popOwner(c)
       closeScope(c)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 28dd15209..1669a7707 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -196,6 +196,8 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
         if region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin {
               tyError, tyObject}:
           message c.config, n[i].info, errGenerated, "region needs to be an object type"
+        else:
+          message(c.config, n.info, warnDeprecated, "region for pointer types")
         addSonSkipIntLit(result, region)
     addSonSkipIntLit(result, t)
     if tfPartial in result.flags:
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index ff7cb0bb0..c315cbebb 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -40,8 +40,8 @@ proc searchInstTypes*(key: PType): PType =
   if not (genericTyp.kind == tyGenericBody and
       key.sons[0] == genericTyp and genericTyp.sym != nil): return
 
-  if genericTyp.sym.typeInstCache == nil:
-    return
+  when not defined(nimNoNilSeqs):
+    if genericTyp.sym.typeInstCache == nil: return
 
   for inst in genericTyp.sym.typeInstCache:
     if inst.id == key.id: return inst
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 29f16b808..f206119ec 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -143,7 +143,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
       c.calleeScope = 1
   else:
     c.calleeScope = calleeScope
-  c.diagnostics = if diagnosticsEnabled: @[] else: nil
+  c.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil
   c.diagnosticsEnabled = diagnosticsEnabled
   c.magic = c.calleeSym.magic
   initIdTable(c.bindings)
@@ -535,6 +535,12 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
 proc allowsNil(f: PType): TTypeRelation {.inline.} =
   result = if tfNotNil notin f.flags: isSubtype else: isNone
 
+proc allowsNilDeprecated(c: TCandidate, f: PType): TTypeRelation =
+  if optNilSeqs in c.c.config.options:
+    result = allowsNil(f)
+  else:
+    result = isNone
+
 proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
   result = f.kind != a.kind and (f.kind in {tyVar, tyLent} or a.kind in {tyVar, tyLent})
 
@@ -741,7 +747,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
     diagnostics = @[]
     flags = {efExplain}
     m.c.config.writelnHook = proc (s: string) =
-      if errorPrefix == nil: errorPrefix = typeClass.sym.name.s & ":"
+      if errorPrefix.len == 0: errorPrefix = typeClass.sym.name.s & ":"
       let msg = s.replace("Error:", errorPrefix)
       if oldWriteHook != nil: oldWriteHook msg
       diagnostics.add msg
@@ -858,6 +864,10 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
       if lhs[2].kind == nkIntLit:
         return inferStaticParam(c, lhs[1], rhs shl lhs[2].intVal)
 
+    of mAshrI:
+      if lhs[2].kind == nkIntLit:
+        return inferStaticParam(c, lhs[1], ashr(rhs, lhs[2].intVal))
+
     of mUnaryMinusI:
       return inferStaticParam(c, lhs[1], -rhs)
 
@@ -1249,7 +1259,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
             result = isNone
         elif tfNotNil in f.flags and tfNotNil notin a.flags:
           result = isNilConversion
-    of tyNil: result = f.allowsNil
+    of tyNil: result = allowsNilDeprecated(c, f)
     else: discard
   of tyOrdinal:
     if isOrdinalType(a):
@@ -1334,7 +1344,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
         result = isNilConversion
       else:
         result = isEqual
-    of tyNil: result = f.allowsNil
+    of tyNil: result = allowsNilDeprecated(c, f)
     else: discard
   of tyCString:
     # conversion from string to cstring is automatic:
@@ -1608,7 +1618,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
           if f.sonsLen == 0:
             result = isGeneric
           else:
-            internalAssert c.c.graph.config, a.sons != nil and a.sons.len > 0
+            internalAssert c.c.graph.config, a.len > 0
             c.typedescMatched = true
             var aa = a
             while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0:
@@ -1806,6 +1816,10 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
     let srca = typeRel(m, src, a)
     if srca notin {isEqual, isGeneric, isSubtype}: continue
 
+    let constraint = c.converters[i].typ.n[1].sym.constraint
+    if not constraint.isNil and not matchNodeKinds(constraint, arg):
+      continue
+
     let destIsGeneric = containsGenericType(dest)
     if destIsGeneric:
       dest = generateTypeInstance(c, m.bindings, arg, dest)
diff --git a/compiler/transf.nim b/compiler/transf.nim
index acfccb5ff..84297aa6a 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -369,6 +369,8 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
       result = PTransNode(n.sons[0])
       if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
         PNode(result).typ = n.typ
+      elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
+        PNode(result).typ = toVar(PNode(result).typ)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
     var m = n.sons[0].sons[1]
     if m.kind == a or m.kind == b:
@@ -377,6 +379,8 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
       result = PTransNode(n.sons[0])
       if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
         PNode(result).typ = n.typ
+      elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
+        PNode(result).typ = toVar(PNode(result).typ)
   else:
     if n.sons[0].kind == a or n.sons[0].kind == b:
       # addr ( deref ( x )) --> x
@@ -539,11 +543,8 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
   if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
       call.sons[0].typ.callConv == ccClosure:
     n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
-    if not c.tooEarly:
-      n.sons[length-2] = transform(c, n.sons[length-2]).PNode
-      result[1] = lambdalifting.liftForLoop(c.graph, n, getCurrOwner(c)).PTransNode
-    else:
-      result[1] = newNode(nkEmpty).PTransNode
+    n.sons[length-2] = transform(c, n.sons[length-2]).PNode
+    result[1] = lambdalifting.liftForLoop(c.graph, n, getCurrOwner(c)).PTransNode
     discard c.breakSyms.pop
     return result
 
diff --git a/compiler/types.nim b/compiler/types.nim
index 514f5cee5..674819bc5 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -430,7 +430,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
       result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
     elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil:
       result = t.sym.name.s
-      if t.kind == tyGenericParam and t.sons != nil and t.sonsLen > 0:
+      if t.kind == tyGenericParam and t.sonsLen > 0:
         result.add ": "
         var first = true
         for son in t.sons:
@@ -1202,7 +1202,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
      tyNone, tyForward, tyFromExpr:
     result = t
   of tyNil:
-    if kind != skConst: result = t
+    if kind != skConst and kind != skParam: result = t
   of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, tyPointer:
     result = nil
   of tyOrdinal:
@@ -1539,10 +1539,9 @@ proc isCompileTimeOnly*(t: PType): bool {.inline.} =
 
 proc containsCompileTimeOnly*(t: PType): bool =
   if isCompileTimeOnly(t): return true
-  if t.sons != nil:
-    for i in 0 ..< t.sonsLen:
-      if t.sons[i] != nil and isCompileTimeOnly(t.sons[i]):
-        return true
+  for i in 0 ..< t.sonsLen:
+    if t.sons[i] != nil and isCompileTimeOnly(t.sons[i]):
+      return true
   return false
 
 type
diff --git a/compiler/vm.nim b/compiler/vm.nim
index a6ec4788b..c49b66b82 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -752,6 +752,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcShlInt:
       decodeBC(rkInt)
       regs[ra].intVal = regs[rb].intVal shl regs[rc].intVal
+    of opcAshrInt:
+      decodeBC(rkInt)
+      regs[ra].intVal = ashr(regs[rb].intVal, regs[rc].intVal)
     of opcBitandInt:
       decodeBC(rkInt)
       regs[ra].intVal = regs[rb].intVal and regs[rc].intVal
@@ -1422,24 +1425,23 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       ensureKind(rkNode)
       if c.callsite != nil: regs[ra].node = c.callsite
       else: stackTrace(c, tos, pc, errFieldXNotFound & "callsite")
-    of opcNGetFile:
-      decodeB(rkNode)
-      let n = regs[rb].node
-      regs[ra].node = newStrNode(nkStrLit, toFullPath(c.config, n.info))
-      regs[ra].node.info = n.info
-      regs[ra].node.typ = n.typ
-    of opcNGetLine:
-      decodeB(rkNode)
+    of opcNGetLineInfo:
+      decodeBImm(rkNode)
       let n = regs[rb].node
-      regs[ra].node = newIntNode(nkIntLit, n.info.line.int)
+      case imm
+      of 0: # getFile
+        regs[ra].node = newStrNode(nkStrLit, toFullPath(c.config, n.info))
+      of 1: # getLine
+        regs[ra].node = newIntNode(nkIntLit, n.info.line.int)
+      of 2: # getColumn
+        regs[ra].node = newIntNode(nkIntLit, n.info.col)
+      else:
+        internalAssert c.config, false
       regs[ra].node.info = n.info
       regs[ra].node.typ = n.typ
-    of opcNGetColumn:
+    of opcNSetLineInfo:
       decodeB(rkNode)
-      let n = regs[rb].node
-      regs[ra].node = newIntNode(nkIntLit, n.info.col)
-      regs[ra].node.info = n.info
-      regs[ra].node.typ = n.typ
+      regs[ra].node.info = regs[rb].node.info
     of opcEqIdent:
       decodeBC(rkInt)
       # aliases for shorter and easier to understand code below
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 866b79568..1abd9ae4a 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -17,7 +17,7 @@ const
   byteExcess* = 128 # we use excess-K for immediates
   wordExcess* = 32768
 
-  MaxLoopIterations* = 1_000_000_000 # max iterations of all loops
+  MaxLoopIterations* = 3_000_000 # max iterations of all loops
 
 
 type
@@ -57,7 +57,8 @@ type
     opcLenStr,
 
     opcIncl, opcInclRange, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt,
-    opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, opcShrInt, opcShlInt,
+    opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat,
+    opcShrInt, opcShlInt, opcAshrInt,
     opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu,
     opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat,
     opcLeFloat, opcLtFloat, opcLeu, opcLtu,
@@ -102,7 +103,7 @@ type
     opcNError,
     opcNWarning,
     opcNHint,
-    opcNGetLine, opcNGetColumn, opcNGetFile,
+    opcNGetLineInfo, opcNSetLineInfo,
     opcEqIdent,
     opcStrToIdent,
     opcGetImpl,
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 3b5ea4beb..a36f559ca 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -939,6 +939,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.freeTemp(tmp2)
 
   of mShlI: genBinaryABCnarrowU(c, n, dest, opcShlInt)
+  of mAshrI: genBinaryABCnarrow(c, n, dest, opcAshrInt)
   of mBitandI: genBinaryABCnarrowU(c, n, dest, opcBitandInt)
   of mBitorI: genBinaryABCnarrowU(c, n, dest, opcBitorInt)
   of mBitxorI: genBinaryABCnarrowU(c, n, dest, opcBitxorInt)
@@ -1181,14 +1182,14 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mSameNodeType: genBinaryABC(c, n, dest, opcSameNodeType)
   of mNLineInfo:
     case n[0].sym.name.s
-    of "getFile":
-      genUnaryABC(c, n, dest, opcNGetFile)
-    of "getLine":
-      genUnaryABC(c, n, dest, opcNGetLine)
-    of "getColumn":
-      genUnaryABC(c, n, dest, opcNGetColumn)
-    else:
-      internalAssert c.config, false
+    of "getFile": genUnaryABI(c, n, dest, opcNGetLineInfo, 0)
+    of "getLine": genUnaryABI(c, n, dest, opcNGetLineInfo, 1)
+    of "getColumn": genUnaryABI(c, n, dest, opcNGetLineInfo, 2)
+    of "copyLineInfo":
+      internalAssert c.config, n.len == 3
+      unused(c, n, dest)
+      genBinaryStmt(c, n, opcNSetLineInfo)
+    else: internalAssert c.config, false
   of mNHint:
     unused(c, n, dest)
     genUnaryStmt(c, n, opcNHint)
diff --git a/config/nim.cfg b/config/nim.cfg
index c30190a18..9626a3197 100644
--- a/config/nim.cfg
+++ b/config/nim.cfg
@@ -82,19 +82,21 @@ path="$lib/pure"
     clang.cpp.options.linker = "-ldl"
     tcc.options.linker = "-ldl"
   @end
-  @if bsd or haiku:
+  @if bsd:
     # BSD got posix_spawn only recently, so we deactivate it for osproc:
     define:useFork
     # at least NetBSD has problems with thread local storage:
     tlsEmulation:on
   @end
   @if haiku:
-    # -fopenmp
-    gcc.options.linker = "-lroot -lnetwork"
-    gcc.cpp.options.linker = "-lroot -lnetwork"
-    clang.options.linker = "-lroot -lnetwork"
-    clang.cpp.options.linker = "-lroot -lnetwork"
-    tcc.options.linker = "-lroot -lnetwork"
+    # Haiku currently have problems with TLS
+    # https://dev.haiku-os.org/ticket/14342
+    tlsEmulation:on
+    gcc.options.linker = "-Wl,--as-needed -lnetwork"
+    gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork"
+    clang.options.linker = "-Wl,--as-needed -lnetwork"
+    clang.cpp.options.linker = "-Wl,--as-needed -lnetwork"
+    tcc.options.linker = "-Wl,--as-needed -lnetwork"
   @end
 @end
 
@@ -253,9 +255,14 @@ vcc.cpp.options.size = "/O1"
 tcc.options.always = "-w"
 
 # Configuration for the Genode toolchain
-amd64.genode.gcc.cpp.exe = "genode-x86-g++"
-amd64.genode.gcc.exe = "genode-x86-gcc"
-amd64.genode.gcc.path = "/usr/local/genode-gcc/bin"
-arm.genode.gcc.cpp.exe = "genode-arm-g++"
-arm.genode.gcc.exe = "genode-arm-gcc"
-arm.genode.gcc.path = "/usr/local/genode-gcc/bin"
+@if genode:
+  gcc.path = "/usr/local/genode-gcc/bin"
+  gcc.cpp.options.always = "-D__GENODE__ -fno-stack-protector"
+  @if i386 or amd64:
+    gcc.exe = "genode-x86-gcc"
+    gcc.cpp.exe = "genode-x86-g++"
+  @elif arm:
+    gcc.exe = "genode-arm-gcc"
+    gcc.cpp.exe = "genode-arm-g++"
+  @end
+@end
diff --git a/doc/advopt.txt b/doc/advopt.txt
index 685c8127d..150025509 100644
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -68,6 +68,8 @@ Advanced options:
   --oldNewlines:on|off      turn on|off the old behaviour of "\n"
   --laxStrings:on|off       when turned on, accessing the zero terminator in
                             strings is allowed; only for backwards compatibility
+  --nilseqs:on|off          allow 'nil' for strings/seqs for
+                            backwards compatibility
   --skipCfg                 do not read the general configuration file
   --skipUserCfg             do not read the user's configuration file
   --skipParentCfg           do not read the parent dirs' configuration files
diff --git a/doc/backends.txt b/doc/backends.txt
index b7f5308ab..13ef7bf4d 100644
--- a/doc/backends.txt
+++ b/doc/backends.txt
@@ -65,7 +65,6 @@ The JavaScript target
 ---------------------
 
 Nim can also generate `JavaScript`:idx: code through the ``js`` command.
-However, the JavaScript code generator is experimental!
 
 Nim targets JavaScript 1.5 which is supported by any widely used browser.
 Since JavaScript does not have a portable means to include another module,
@@ -77,7 +76,7 @@ available. This includes:
 * manual memory management (``alloc``, etc.)
 * casting and other unsafe operations (``cast`` operator, ``zeroMem``, etc.)
 * file management
-* most modules of the Standard library
+* most modules of the standard library
 * proper 64 bit integer arithmetic
 * unsigned integer arithmetic
 
@@ -87,9 +86,8 @@ However, the modules `strutils <strutils.html>`_, `math <math.html>`_, and
 
 To compile a Nim module into a ``.js`` file use the ``js`` command; the
 default is a ``.js`` file that is supposed to be referenced in an ``.html``
-file. However, you can also run the code with `nodejs`:idx:, a `software
-platform for easily building fast, scalable network applications
-<http://nodejs.org>`_::
+file. However, you can also run the code with `nodejs`:idx:
+(`<http://nodejs.org>`_)::
 
   nim js -d:nodejs -r examples/hallo.nim
 
@@ -330,8 +328,9 @@ Nimcache naming logic
 
 The `nimcache`:idx: directory is generated during compilation and will hold
 either temporary or final files depending on your backend target. The default
-name for the directory is ``nimcache`` but you can use the ``--nimcache``
-`compiler switch <nimc.html#command-line-switches>`_ to change it.
+name for the directory depends on the used backend and on your OS but you can
+use the ``--nimcache`` `compiler switch <nimc.html#command-line-switches>`_ to
+change it.
 
 Nimcache and C like targets
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/doc/docgen.rst b/doc/docgen.rst
index d196b3a18..fa23bdc79 100644
--- a/doc/docgen.rst
+++ b/doc/docgen.rst
@@ -97,24 +97,9 @@ Partial Output::
   proc helloWorld(times: int) {.raises: [], tags: [].}
   ...
 
-The full output can be seen here: `docgen_sample2.html <docgen_sample2.html>`_.
-
-The older version of the ``doc`` command, now renamed ``doc0`` runs before
-semantic checking which means it lacks some of the things ``doc`` will output.
-
-The ``doc0`` command::
-  nim doc0 sample
-
-Partial Output::
-  ...
-  proc helloWorld*(times: int)
-  ...
-
-Output can be viewed in full here: `docgen_sample.html <docgen_sample.html>`_.
-As you can see, the tool has extracted less information than what the ``doc``
-command provides, such as pragmas attached implicitly by the compiler. This type
-of information is not available from looking at the AST (Abstract Syntax Tree)
-prior to semantic checking, which is why ``doc0`` doesn't show it.
+The full output can be seen here: `docgen_sample.html <docgen_sample.html>`_.
+It runs after semantic checking, and includes pragmas attached implicitly by the
+compiler.
 
 
 JSON
diff --git a/doc/exception_hierarchy_fragment.txt b/doc/exception_hierarchy_fragment.txt
deleted file mode 100644
index a02d9ccef..000000000
--- a/doc/exception_hierarchy_fragment.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-* `Exception <system.html#Exception>`_
-  * `AccessViolationError <system.html#AccessViolationError>`_
-  * `ArithmeticError <system.html#ArithmeticError>`_
-    * `DivByZeroError <system.html#DivByZeroError>`_
-    * `OverflowError <system.html#OverflowError>`_
-  * `AssertionError <system.html#AssertionError>`_
-  * `DeadThreadError <system.html#DeadThreadError>`_
-  * `FloatingPointError <system.html#FloatingPointError>`_
-    * `FloatDivByZeroError <system.html#FloatDivByZeroError>`_
-    * `FloatInexactError <system.html#FloatInexactError>`_
-    * `FloatInvalidOpError <system.html#FloatInvalidOpError>`_
-    * `FloatOverflowError <system.html#FloatOverflowError>`_
-    * `FloatUnderflowError <system.html#FloatUnderflowError>`_
-  * `FieldError <system.html#FieldError>`_
-  * `IndexError <system.html#IndexError>`_
-  * `ObjectAssignmentError <system.html#ObjectAssignmentError>`_
-  * `ObjectConversionError <system.html#ObjectConversionError>`_
-  * `ValueError <system.html#ValueError>`_
-    * `KeyError <system.html#KeyError>`_
-  * `ReraiseError <system.html#ReraiseError>`_
-  * `RangeError <system.html#RangeError>`_
-  * `OutOfMemoryError <system.html#OutOfMemoryError>`_
-  * `ResourceExhaustedError <system.html#ResourceExhaustedError>`_
-  * `StackOverflowError <system.html#StackOverflowError>`_
-  * `SystemError <system.html#SystemError>`_
-    * `IOError <system.html#IOError>`_
-    * `OSError <system.html#OSError>`_
-      * `LibraryError <system.html#LibraryError>`_
diff --git a/doc/manual.rst b/doc/manual.rst
index abdc4ce69..bb2650799 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -769,8 +769,8 @@ Pre-defined floating point types
 The following floating point types are pre-defined:
 
 ``float``
-  the generic floating point type; its size is platform dependent
-  (the compiler chooses the processor's fastest floating point type).
+  the generic floating point type; its size used to be platform dependent,
+  but now it is always mapped to ``float64``.
   This type should be used in general.
 
 floatXX
@@ -1507,68 +1507,6 @@ non nilable pointers. The details of this analysis are still to be specified
 here.
 
 
-Memory regions
---------------
-
-The types ``ref`` and ``ptr`` can get an optional ``region`` annotation.
-A region has to be an object type.
-
-Regions are very useful to separate user space and kernel memory in the
-development of OS kernels:
-
-.. code-block:: nim
-  type
-    Kernel = object
-    Userspace = object
-
-  var a: Kernel ptr Stat
-  var b: Userspace ptr Stat
-
-  # the following does not compile as the pointer types are incompatible:
-  a = b
-
-As the example shows ``ptr`` can also be used as a binary
-operator, ``region ptr T`` is a shortcut for ``ptr[region, T]``.
-
-In order to make generic code easier to write ``ptr T`` is a subtype
-of ``ptr[R, T]`` for any ``R``.
-
-Furthermore the subtype relation of the region object types is lifted to
-the pointer types: If ``A <: B`` then ``ptr[A, T] <: ptr[B, T]``. This can be
-used to model subregions of memory. As a special typing rule ``ptr[R, T]`` is
-not compatible to ``pointer`` to prevent the following from compiling:
-
-.. code-block:: nim
-  # from system
-  proc dealloc(p: pointer)
-
-  # wrap some scripting language
-  type
-    PythonsHeap = object
-    PyObjectHeader = object
-      rc: int
-      typ: pointer
-    PyObject = ptr[PythonsHeap, PyObjectHeader]
-
-  proc createPyObject(): PyObject {.importc: "...".}
-  proc destroyPyObject(x: PyObject) {.importc: "...".}
-
-  var foo = createPyObject()
-  # type error here, how convenient:
-  dealloc(foo)
-
-
-Future directions:
-
-* Memory regions might become available for  ``string`` and ``seq`` too.
-* Builtin regions like ``private``, ``global`` and ``local`` might be
-  useful for an OpenCL target.
-* Builtin "regions" can model ``lent`` and ``unique`` pointers.
-* An assignment operator can be attached to a region so that proper write
-  barriers can be generated. This would imply that the GC can be implemented
-  completely in user-space.
-
-
 Procedural type
 ---------------
 A procedural type is internally a pointer to a procedure. ``nil`` is
@@ -1673,7 +1611,8 @@ A ``distinct`` type is new type derived from a `base type`:idx: that is
 incompatible with its base type. In particular, it is an essential property
 of a distinct type that it **does not** imply a subtype relation between it
 and its base type. Explicit type conversions from a distinct type to its
-base type and vice versa are allowed.
+base type and vice versa are allowed. See also ``distinctBase`` to get the
+reverse operation.
 
 
 Modelling currencies
@@ -2336,6 +2275,8 @@ pointer type and overloading resolution is tried with ``a[]`` instead.
 Automatic self insertions
 -------------------------
 
+**Note**: The ``.this`` pragma is deprecated and should not be used anymore.
+
 Starting with version 0.14 of the language, Nim supports ``field`` as a
 shortcut for ``self.field`` comparable to the `this`:idx: keyword in Java
 or C++. This feature has to be explicitly enabled via a ``{.this: self.}``
@@ -4028,9 +3969,13 @@ exception.
 Exception hierarchy
 -------------------
 
-The exception tree is defined in the `system <system.html>`_ module:
-
-.. include:: exception_hierarchy_fragment.txt
+The exception tree is defined in the `system <system.html>`_ module.
+Every exception inherits from ``system.Exception``. Exceptions that indicate
+programming bugs inherit from ``system.Defect`` (which is a subtype of ``Exception``)
+and are stricly speaking not catchable as they can also be mapped to an operation
+that terminates the whole process. Exceptions that indicate any other runtime error
+that can be caught inherit from ``system.CatchableError``
+(which is a subtype of ``Exception``).
 
 
 Imported exceptions
@@ -5435,6 +5380,7 @@ type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop:
     :test: "nim c $1"
 
   import macros
+  {.experimental: "forLoopMacros".}
 
   macro enumerate(x: ForLoopStmt): untyped =
     expectKind x, nnkForStmt
@@ -5461,6 +5407,62 @@ type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop:
     echo a2, " ", b2
 
 
+Currently for loop macros must be enabled explicitly
+via ``{.experimental: "forLoopMacros".}``.
+
+
+Case statement macros
+---------------------
+
+A macro that needs to be called `match`:idx: can be used to
+rewrite ``case`` statements in order to
+implement `pattern matching`:idx: for certain types. The following
+example implements a simplistic form of pattern matching for tuples,
+leveraging the existing equality operator for tuples (as provided in
+ ``system.==``):
+
+.. code-block:: nim
+    :test: "nim c $1"
+
+  {.experimental: "caseStmtMacros".}
+
+  import macros
+
+  macro match(n: tuple): untyped =
+    result = newTree(nnkIfStmt)
+    let selector = n[0]
+    for i in 1 ..< n.len:
+      let it = n[i]
+      case it.kind
+      of nnkElse, nnkElifBranch, nnkElifExpr, nnkElseExpr:
+        result.add it
+      of nnkOfBranch:
+        for j in 0..it.len-2:
+          let cond = newCall("==", selector, it[j])
+          result.add newTree(nnkElifBranch, cond, it[^1])
+      else:
+        error "'match' cannot handle this node", it
+    echo repr result
+
+  case ("foo", 78)
+  of ("foo", 78): echo "yes"
+  of ("bar", 88): echo "no"
+  else: discard
+
+
+Currently case statement macros must be enabled explicitly
+via ``{.experimental: "caseStmtMacros".}``.
+
+``match`` macros are subject to overload resolution. First the
+``case``'s selector expression is used to determine which ``match``
+macro to call. To this macro is then the complete ``case`` statement
+body is passed and the macro is evaluated.
+
+In other words, the macro needs to transform the full ``case`` statement
+but only the statement's selector expression is used to determine which
+``macro`` to call.
+
+
 Special Types
 =============
 
@@ -7932,7 +7934,7 @@ that ``spawn`` takes is restricted:
 
 ``spawn`` executes the passed expression on the thread pool and returns
 a `data flow variable`:idx: ``FlowVar[T]`` that can be read from. The reading
-with the ``^`` operator is **blocking**. However, one can use ``awaitAny`` to
+with the ``^`` operator is **blocking**. However, one can use ``blockUntilAny`` to
 wait on multiple flow variables at the same time:
 
 .. code-block:: nim
@@ -7943,10 +7945,10 @@ wait on multiple flow variables at the same time:
     var responses = newSeq[FlowVarBase](3)
     for i in 0..2:
       responses[i] = spawn tellServer(Update, "key", "value")
-    var index = awaitAny(responses)
+    var index = blockUntilAny(responses)
     assert index >= 0
     responses.del(index)
-    discard awaitAny(responses)
+    discard blockUntilAny(responses)
 
 Data flow variables ensure that no data races
 are possible. Due to technical limitations not every type ``T`` is possible in
diff --git a/doc/nimc.rst b/doc/nimc.rst
index 0939f67e8..0682fac03 100644
--- a/doc/nimc.rst
+++ b/doc/nimc.rst
@@ -68,6 +68,46 @@ User                             Some user defined warning.
 ==========================       ============================================
 
 
+List of hints
+-------------
+
+Each hint can be activated individually with ``--hint[NAME]:on|off`` or in a
+``push`` pragma.
+
+==========================       ============================================
+Name                             Description
+==========================       ============================================
+CC                               Shows when the C compiler is called.
+CodeBegin
+CodeEnd
+CondTrue
+Conf                             A config file was loaded.
+ConvToBaseNotNeeded
+ConvFromXtoItselfNotNeeded
+Dependency
+Exec                             Program is executed.
+ExprAlwaysX
+ExtendedContext
+GCStats                          Dumps statistics about the Garbage Collector.
+GlobalVar                        Shows global variables declarations.
+LineTooLong                      Line exceeds the maximum length.
+Link                             Linking phase.
+Name
+Path                             Search paths modifications.
+Pattern
+Performance
+Processing                       Artifact being compiled.
+QuitCalled
+Source                           The source line that triggered a diagnostic
+                                 message.
+StackTrace
+Success, SuccessX                Successful compilation of a library or a binary.
+User
+UserRaw
+XDeclaredButNotUsed              Unused symbols in the code.
+==========================       ============================================
+
+
 Verbosity levels
 ----------------
 
@@ -114,7 +154,7 @@ passed as a command line argument to the compiler.
 The ``nim`` executable processes configuration files in the following
 directories (in this order; later files overwrite previous settings):
 
-1) ``$nim/config/nim.cfg``, ``/etc/nim.cfg`` (UNIX) or ``%NIMROD%/config/nim.cfg`` (Windows). This file can be skipped with the ``--skipCfg`` command line option.
+1) ``$nim/config/nim.cfg``, ``/etc/nim/nim.cfg`` (UNIX) or ``%NIM%/config/nim.cfg`` (Windows). This file can be skipped with the ``--skipCfg`` command line option.
 2) ``$HOME/.config/nim.cfg`` (POSIX) or  ``%APPDATA%/nim.cfg`` (Windows). This file can be skipped with the ``--skipUserCfg`` command line option.
 3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent  directory of the project file's path. These files can be skipped with the ``--skipParentCfg`` command line option.
 4) ``$projectDir/nim.cfg`` where ``$projectDir`` stands for the project  file's path. This file can be skipped with the ``--skipProjCfg`` command line option.
@@ -157,38 +197,28 @@ the first matching file is used.
 Generated C code directory
 --------------------------
 The generated files that Nim produces all go into a subdirectory called
-``nimcache`` in your project directory. This makes it easy to delete all
+``nimcache``. Its full path is
+
+- ``$XDG_CACHE_HOME/nim/$projectname(_r|_d)`` or ``~/.cache/nim/$projectname(_r|_d)``
+  on Posix
+- ``$HOME/nimcache/$projectname(_r|_d)`` on Windows.
+
+The ``_r`` suffix is used for release builds, ``_d`` is for debug builds.
+
+This makes it easy to delete all
 generated files. Files generated in this directory follow a naming logic which
 you can read about in the `Nim Backend Integration document
 <backends.html#nimcache-naming-logic>`_.
 
+The ``--nimcache``
+`compiler switch <nimc.html#command-line-switches>`_ can be used to
+to change the ``nimcache`` directory.
+
 However, the generated C code is not platform independent. C code generated for
 Linux does not compile on Windows, for instance. The comment on top of the
 C file lists the OS, CPU and CC the file has been compiled for.
 
 
-Compilation cache
-=================
-
-**Warning**: The compilation cache is still highly experimental!
-
-The ``nimcache`` directory may also contain so called `rod`:idx:
-or `symbol files`:idx:. These files are pre-compiled modules that are used by
-the compiler to perform `incremental compilation`:idx:. This means that only
-modules that have changed since the last compilation (or the modules depending
-on them etc.) are re-compiled. However, per default no symbol files are
-generated; use the ``--symbolFiles:on`` command line switch to activate them.
-
-Unfortunately due to technical reasons the ``--symbolFiles:on`` needs
-to *aggregate* some generated C code. This means that the resulting executable
-might contain some cruft even with dead code elimination. So
-the final release build should be done with ``--symbolFiles:off``.
-
-Due to the aggregation of C code it is also recommended that each project
-resides in its own directory so that the generated ``nimcache`` directory
-is not shared between different projects.
-
-
 Compiler Selection
 ==================
 
diff --git a/doc/spawn.txt b/doc/spawn.txt
index 522c94464..ab667ff48 100644
--- a/doc/spawn.txt
+++ b/doc/spawn.txt
@@ -25,7 +25,7 @@ Spawn statement
 A standalone ``spawn`` statement is a simple construct. It executes
 the passed expression on the thread pool and returns a `data flow variable`:idx:
 ``FlowVar[T]`` that can be read from. The reading with the ``^`` operator is
-**blocking**. However, one can use ``awaitAny`` to wait on multiple flow
+**blocking**. However, one can use ``blockUntilAny`` to wait on multiple flow
 variables at the same time:
 
 .. code-block:: nim
@@ -36,10 +36,10 @@ variables at the same time:
     var responses = newSeq[FlowVarBase](3)
     for i in 0..2:
       responses[i] = spawn tellServer(Update, "key", "value")
-    var index = awaitAny(responses)
+    var index = blockUntilAny(responses)
     assert index >= 0
     responses.del(index)
-    discard awaitAny(responses)
+    discard blockUntilAny(responses)
 
 Data flow variables ensure that no data races
 are possible. Due to technical limitations not every type ``T`` is possible in
diff --git a/examples/tunit.nim b/examples/tunit.nim
index 26bcafda1..785b9aa5e 100644
--- a/examples/tunit.nim
+++ b/examples/tunit.nim
@@ -42,6 +42,6 @@ test "arithmetic failure":
   expect(ArithmeticError):
     err()
 
-  expect(ArithmeticError, SystemError):
+  expect(ArithmeticError, CatchableError):
     discard foo()
 
diff --git a/koch.nim b/koch.nim
index ad6a774e8..c302be4ca 100644
--- a/koch.nim
+++ b/koch.nim
@@ -57,7 +57,8 @@ Commands for core developers:
   zip                      builds the installation zip package
   xz                       builds the installation tar.xz package
   testinstall              test tar.xz package; Unix only!
-  tests [options]          run the testsuite
+  tests [options]          run the testsuite (run a subset of tests by
+                           specifying a category, e.g. `tests cat async`)
   temp options             creates a temporary compiler for testing
   winrelease               creates a Windows release
   pushcsource              push generated C sources to its repo
@@ -182,7 +183,7 @@ proc bundleNimbleExe() =
   bundleNimbleSrc()
   # now compile Nimble and copy it to $nim/bin for the installer.ini
   # to pick it up:
-  nimexec("c -d:release dist/nimble/src/nimble.nim")
+  nimexec("c -d:release --nilseqs:on dist/nimble/src/nimble.nim")
   copyExe("dist/nimble/src/nimble".exe, "bin/nimble".exe)
 
 proc buildNimble(latest: bool) =
@@ -209,7 +210,7 @@ proc buildNimble(latest: bool) =
       else:
         exec("git checkout -f stable")
       exec("git pull")
-  nimexec("c --noNimblePath -p:compiler -d:release " & installDir / "src/nimble.nim")
+  nimexec("c --noNimblePath -p:compiler --nilseqs:on -d:release " & installDir / "src/nimble.nim")
   copyExe(installDir / "src/nimble".exe, "bin/nimble".exe)
 
 proc bundleNimsuggest(buildExe: bool) =
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index d4e8ada0a..90fea440e 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -432,6 +432,9 @@ proc getLine(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
 proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
 proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.}
 
+proc copyLineInfo*(arg: NimNode, info: NimNode) {.magic: "NLineInfo", noSideEffect.}
+  ## copy lineinfo from info node
+
 proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} =
   ## returns ``LineInfo`` of ``n``, using absolute path for ``filename``
   result.filename = n.getFile
diff --git a/lib/deprecated/pure/actors.nim b/lib/deprecated/pure/actors.nim
index 17321cc0e..451668825 100644
--- a/lib/deprecated/pure/actors.nim
+++ b/lib/deprecated/pure/actors.nim
@@ -43,7 +43,6 @@ type
     t: Thread[ptr Actor[In, Out]]
 
   PActor*[In, Out] = ptr Actor[In, Out] ## an actor
-{.deprecated: [TTask: Task, TActor: Actor].}
 
 proc spawn*[In, Out](action: proc(
     self: PActor[In, Out]){.thread.}): PActor[In, Out] =
@@ -168,7 +167,7 @@ proc terminate*[In, Out](a: var ActorPool[In, Out]) =
   for i in 0..<a.actors.len: join(a.actors[i])
   when Out isnot void:
     close(a.outputs)
-  a.actors = nil
+  a.actors = @[]
 
 proc join*[In, Out](a: var ActorPool[In, Out]) =
   ## short-cut for `sync` and then `terminate`.
diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim
index 05aebef76..76a9044d8 100644
--- a/lib/deprecated/pure/sockets.nim
+++ b/lib/deprecated/pure/sockets.nim
@@ -36,6 +36,8 @@ include "system/inclrtl"
 
 when hostOS == "solaris":
   {.passl: "-lsocket -lnsl".}
+elif hostOS == "haiku":
+  {.passl: "-lnetwork".}
 
 import os, parseutils
 from times import epochTime
diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim
index ca0e29d11..26bc7d0ad 100644
--- a/lib/impure/db_mysql.nim
+++ b/lib/impure/db_mysql.nim
@@ -128,10 +128,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
   var a = 0
   for c in items(string(formatstr)):
     if c == '?':
-      if args[a].isNil:
-        add(result, "NULL")
-      else:
-        add(result, dbQuote(args[a]))
+      add(result, dbQuote(args[a]))
       inc(a)
     else:
       add(result, c)
@@ -183,24 +180,8 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
       row = mysql.fetchRow(sqlres)
       if row == nil: break
       for i in 0..L-1:
-        if row[i] == nil:
-          if backup == nil:
-            newSeq(backup, L)
-          if backup[i] == nil and result[i] != nil:
-            shallowCopy(backup[i], result[i])
-          result[i] = nil
-        else:
-          if result[i] == nil:
-            if backup != nil:
-              if backup[i] == nil:
-                backup[i] = ""
-              shallowCopy(result[i], backup[i])
-              setLen(result[i], 0)
-            else:
-              result[i] = ""
-          else:
-            setLen(result[i], 0)
-          add(result[i], row[i])
+        setLen(result[i], 0)
+        result[i].add row[i]
       yield result
     properFreeResult(sqlres, row)
 
@@ -323,10 +304,7 @@ proc getRow*(db: DbConn, query: SqlQuery,
     if row != nil:
       for i in 0..L-1:
         setLen(result[i], 0)
-        if row[i] == nil:
-          result[i] = nil
-        else:
-          add(result[i], row[i])
+        add(result[i], row[i])
     properFreeResult(sqlres, row)
 
 proc getAllRows*(db: DbConn, query: SqlQuery,
@@ -345,10 +323,7 @@ proc getAllRows*(db: DbConn, query: SqlQuery,
       setLen(result, j+1)
       newSeq(result[j], L)
       for i in 0..L-1:
-        if row[i] == nil:
-          result[j][i] = nil
-        else:
-          result[j][i] = $row[i]
+        result[j][i] = $row[i]
       inc(j)
     mysql.freeResult(sqlres)
 
diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim
index 1459f0d7e..e765cc197 100644
--- a/lib/impure/db_postgres.nim
+++ b/lib/impure/db_postgres.nim
@@ -103,10 +103,7 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
   else:
     for c in items(string(formatstr)):
       if c == '?':
-        if args[a] == nil:
-          add(result, "NULL")
-        else:
-          add(result, dbQuote(args[a]))
+        add(result, dbQuote(args[a]))
         inc(a)
       else:
         add(result, c)
@@ -179,7 +176,7 @@ proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
     setLen(r[col], 0)
     let x = pqgetvalue(res, line, col)
     if x.isNil:
-      r[col] = nil
+      r[col] = ""
     else:
       add(r[col], x)
 
diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim
index fd25b2b94..a40c88a11 100644
--- a/lib/impure/db_sqlite.nim
+++ b/lib/impure/db_sqlite.nim
@@ -105,7 +105,6 @@ proc dbError*(db: DbConn) {.noreturn.} =
 
 proc dbQuote*(s: string): string =
   ## DB quotes the string.
-  if s.isNil: return "NULL"
   result = "'"
   for c in items(s):
     if c == '\'': add(result, "''")
diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim
index 889210f62..32b1d0255 100644
--- a/lib/impure/nre.nim
+++ b/lib/impure/nre.nim
@@ -267,7 +267,7 @@ proc `[]`*(pattern: Captures, i: int): string =
     let bounds = bounds.get
     return pattern.str.substr(bounds.a, bounds.b)
   else:
-    return nil
+    return ""
 
 proc match*(pattern: RegexMatch): string =
   return pattern.captures[-1]
@@ -291,9 +291,9 @@ template toTableImpl(cond: untyped) {.dirty.} =
     else:
       result[key] = nextVal
 
-proc toTable*(pattern: Captures, default: string = nil): Table[string, string] =
+proc toTable*(pattern: Captures, default: string = ""): Table[string, string] =
   result = initTable[string, string]()
-  toTableImpl(nextVal == nil)
+  toTableImpl(nextVal.len == 0)
 
 proc toTable*(pattern: CaptureBounds, default = none(HSlice[int, int])):
     Table[string, Option[HSlice[int, int]]] =
@@ -312,13 +312,13 @@ template itemsImpl(cond: untyped) {.dirty.} =
 iterator items*(pattern: CaptureBounds, default = none(HSlice[int, int])): Option[HSlice[int, int]] =
   itemsImpl(nextVal.isNone)
 
-iterator items*(pattern: Captures, default: string = nil): string =
-  itemsImpl(nextVal == nil)
+iterator items*(pattern: Captures, default: string = ""): string =
+  itemsImpl(nextVal.len == 0)
 
 proc toSeq*(pattern: CaptureBounds, default = none(HSlice[int, int])): seq[Option[HSlice[int, int]]] =
   accumulateResult(pattern.items(default))
 
-proc toSeq*(pattern: Captures, default: string = nil): seq[string] =
+proc toSeq*(pattern: Captures, default: string = ""): seq[string] =
   accumulateResult(pattern.items(default))
 
 proc `$`*(pattern: RegexMatch): string =
diff --git a/lib/impure/nre/private/util.nim b/lib/impure/nre/private/util.nim
index 12d2506ea..a3ae84007 100644
--- a/lib/impure/nre/private/util.nim
+++ b/lib/impure/nre/private/util.nim
@@ -10,11 +10,7 @@ proc fget*[K, V](self: Table[K, V], key: K): V =
 const Ident = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
 const StartIdent = Ident - {'0'..'9'}
 
-proc checkNil(arg: string): string =
-  if arg == nil:
-    raise newException(ValueError, "Cannot use nil capture")
-  else:
-    return arg
+template checkNil(arg: string): string = arg
 
 template formatStr*(howExpr, namegetter, idgetter): untyped =
   let how = howExpr
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index 201c490f3..a60f70828 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -113,7 +113,7 @@ proc matchOrFind(buf: cstring, pattern: Regex, matches: var openArray[string],
     var b = rawMatches[i * 2 + 1]
     if a >= 0'i32:
       matches[i-1] = bufSubstr(buf, int(a), int(b))
-    else: matches[i-1] = nil
+    else: matches[i-1] = ""
   return rawMatches[1] - rawMatches[0]
 
 proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string],
@@ -133,7 +133,7 @@ proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string],
     var a = rawMatches[i * 2]
     var b = rawMatches[i * 2 + 1]
     if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b))
-    else: matches[i-1] = nil
+    else: matches[i-1] = ""
   return (rawMatches[0].int, rawMatches[1].int - 1)
 
 proc findBounds*(s: string, pattern: Regex, matches: var openArray[string],
@@ -287,7 +287,7 @@ proc find*(buf: cstring, pattern: Regex, matches: var openArray[string],
     var a = rawMatches[i * 2]
     var b = rawMatches[i * 2 + 1]
     if a >= 0'i32: matches[i-1] = bufSubstr(buf, int(a), int(b))
-    else: matches[i-1] = nil
+    else: matches[i-1] = ""
   return rawMatches[0]
 
 proc find*(s: string, pattern: Regex, matches: var openArray[string],
@@ -456,8 +456,6 @@ proc replacef*(s: string, sub: Regex, by: string): string =
   while true:
     var match = findBounds(s, sub, caps, prev)
     if match.first < 0: break
-    assert result != nil
-    assert s != nil
     add(result, substr(s, prev, match.first-1))
     addf(result, by, caps)
     prev = match.last + 1
@@ -615,7 +613,7 @@ when isMainModule:
     doAssert false
 
   if "abc" =~ re"(cba)?.*":
-    doAssert matches[0] == nil
+    doAssert matches[0] == ""
   else: doAssert false
 
   if "abc" =~ re"().*":
diff --git a/lib/js/jscore.nim b/lib/js/jscore.nim
index 91f3ff8bb..bf64b0794 100644
--- a/lib/js/jscore.nim
+++ b/lib/js/jscore.nim
@@ -1,3 +1,12 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
 ## This module wraps core JavaScript functions.
 ##
 ## Unless your application has very
@@ -19,49 +28,47 @@ var
   Date* {.importc, nodecl.}: DateLib
   JSON* {.importc, nodecl.}: JsonLib
 
-{.push importcpp.}
-
 # Math library
-proc abs*(m: MathLib, a: SomeNumber): SomeNumber
-proc acos*(m: MathLib, a: SomeNumber): float
-proc acosh*(m: MathLib, a: SomeNumber): float
-proc asin*(m: MathLib, a: SomeNumber): float
-proc asinh*(m: MathLib, a: SomeNumber): float
-proc atan*(m: MathLib, a: SomeNumber): float
-proc atan2*(m: MathLib, a: SomeNumber): float
-proc atanh*(m: MathLib, a: SomeNumber): float
-proc cbrt*(m: MathLib, f: SomeFloat): SomeFloat
-proc ceil*(m: MathLib, f: SomeFloat): SomeFloat
-proc clz32*(m: MathLib, f: SomeInteger): int
-proc cos*(m: MathLib, a: SomeNumber): float
-proc cosh*(m: MathLib, a: SomeNumber): float
-proc exp*(m: MathLib, a: SomeNumber): float
-proc expm1*(m: MathLib, a: SomeNumber): float
-proc floor*(m: MathLib, f: SomeFloat): int
-proc fround*(m: MathLib, f: SomeFloat): float32
-proc hypot*(m: MathLib, args: varargs[distinct SomeNumber]): float
-proc imul*(m: MathLib, a, b: int32): int32
-proc log*(m: MathLib, a: SomeNumber): float
-proc log10*(m: MathLib, a: SomeNumber): float
-proc log1p*(m: MathLib, a: SomeNumber): float
-proc log2*(m: MathLib, a: SomeNumber): float
-proc max*(m: MathLib, a, b: SomeNumber): SomeNumber
-proc min*[T: SomeNumber | JsRoot](m: MathLib, a, b: T): T
-proc pow*(m: MathLib, a, b: distinct SomeNumber): float
-proc random*(m: MathLib): float
-proc round*(m: MathLib, f: SomeFloat): int
-proc sign*(m: MathLib, f: SomeNumber): int
-proc sin*(m: MathLib, a: SomeNumber): float
-proc sinh*(m: MathLib, a: SomeNumber): float
-proc sqrt*(m: MathLib, f: SomeFloat): SomeFloat
-proc tan*(m: MathLib, a: SomeNumber): float
-proc tanh*(m: MathLib, a: SomeNumber): float
-proc trunc*(m: MathLib, f: SomeFloat): int
+proc abs*(m: MathLib, a: SomeNumber): SomeNumber {.importcpp.}
+proc acos*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc acosh*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc asin*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc asinh*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc atan*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc atan2*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc atanh*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc cbrt*(m: MathLib, f: SomeFloat): SomeFloat {.importcpp.}
+proc ceil*(m: MathLib, f: SomeFloat): SomeFloat {.importcpp.}
+proc clz32*(m: MathLib, f: SomeInteger): int {.importcpp.}
+proc cos*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc cosh*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc exp*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc expm1*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc floor*(m: MathLib, f: SomeFloat): int {.importcpp.}
+proc fround*(m: MathLib, f: SomeFloat): float32 {.importcpp.}
+proc hypot*(m: MathLib, args: varargs[distinct SomeNumber]): float {.importcpp.}
+proc imul*(m: MathLib, a, b: int32): int32 {.importcpp.}
+proc log*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc log10*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc log1p*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc log2*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc max*(m: MathLib, a, b: SomeNumber): SomeNumber {.importcpp.}
+proc min*[T: SomeNumber | JsRoot](m: MathLib, a, b: T): T {.importcpp.}
+proc pow*(m: MathLib, a, b: distinct SomeNumber): float {.importcpp.}
+proc random*(m: MathLib): float {.importcpp.}
+proc round*(m: MathLib, f: SomeFloat): int {.importcpp.}
+proc sign*(m: MathLib, f: SomeNumber): int {.importcpp.}
+proc sin*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc sinh*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc sqrt*(m: MathLib, f: SomeFloat): SomeFloat {.importcpp.}
+proc tan*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc tanh*(m: MathLib, a: SomeNumber): float {.importcpp.}
+proc trunc*(m: MathLib, f: SomeFloat): int {.importcpp.}
 
 # Date library
-proc now*(d: DateLib): int
-proc UTC*(d: DateLib): int
-proc parse*(d: DateLib, s: cstring): int
+proc now*(d: DateLib): int {.importcpp.}
+proc UTC*(d: DateLib): int {.importcpp.}
+proc parse*(d: DateLib, s: cstring): int {.importcpp.}
 
 proc newDate*(): DateTime {.
   importcpp: "new Date()".}
@@ -73,19 +80,17 @@ proc newDate*(year, month, day, hours, minutes,
              seconds, milliseconds: int): DateTime {.
   importcpp: "new Date(#,#,#,#,#,#,#)".}
 
-proc getDay*(d: DateTime): int
-proc getFullYear*(d: DateTime): int
-proc getHours*(d: DateTime): int
-proc getMilliseconds*(d: DateTime): int
-proc getMinutes*(d: DateTime): int
-proc getMonth*(d: DateTime): int
-proc getSeconds*(d: DateTime): int
-proc getYear*(d: DateTime): int
-proc getTime*(d: DateTime): int
-proc toString*(d: DateTime): cstring
+proc getDay*(d: DateTime): int {.importcpp.}
+proc getFullYear*(d: DateTime): int {.importcpp.}
+proc getHours*(d: DateTime): int {.importcpp.}
+proc getMilliseconds*(d: DateTime): int {.importcpp.}
+proc getMinutes*(d: DateTime): int {.importcpp.}
+proc getMonth*(d: DateTime): int {.importcpp.}
+proc getSeconds*(d: DateTime): int {.importcpp.}
+proc getYear*(d: DateTime): int {.importcpp.}
+proc getTime*(d: DateTime): int {.importcpp.}
+proc toString*(d: DateTime): cstring {.importcpp.}
 
 #JSON library
-proc stringify*(l: JsonLib, s: JsRoot): cstring
-proc parse*(l: JsonLib, s: cstring): JsRoot
-
-{.pop.}
+proc stringify*(l: JsonLib, s: JsRoot): cstring {.importcpp.}
+proc parse*(l: JsonLib, s: cstring): JsRoot {.importcpp.}
diff --git a/lib/nimbase.h b/lib/nimbase.h
index 6dc742910..84972fcb0 100644
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -129,13 +129,13 @@ __clang__
        defined __DMC__ || \
        defined __BORLANDC__ )
 #  define NIM_THREADVAR __declspec(thread)
+#elif defined(__TINYC__) || defined(__GENODE__)
+#  define NIM_THREADVAR
 /* note that ICC (linux) and Clang are covered by __GNUC__ */
 #elif defined __GNUC__ || \
        defined __SUNPRO_C || \
        defined __xlC__
 #  define NIM_THREADVAR __thread
-#elif defined __TINYC__
-#  define NIM_THREADVAR
 #else
 #  error "Cannot define NIM_THREADVAR"
 #endif
diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim
index f3596b571..4a77b4f34 100644
--- a/lib/packages/docutils/rstast.nim
+++ b/lib/packages/docutils/rstast.nim
@@ -293,9 +293,9 @@ proc renderRstToJsonNode(node: PRstNode): JsonNode =
       (key: "kind", val: %($node.kind)),
       (key: "level", val: %BiggestInt(node.level))
      ]
-  if node.text != nil:
+  if node.text.len > 0:
     result.add("text", %node.text)
-  if node.sons != nil and len(node.sons) > 0:
+  if len(node.sons) > 0:
     var accm = newSeq[JsonNode](len(node.sons))
     for i, son in node.sons:
       accm[i] = renderRstToJsonNode(son)
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index 43a429a17..5b0b6c6ee 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -312,7 +312,6 @@ proc setIndexTerm*(d: var RstGenerator, id, term: string,
   ## The index won't be written to disk unless you call `writeIndexFile()
   ## <#writeIndexFile>`_. The purpose of the index is documented in the `docgen
   ## tools guide <docgen.html#index-switch>`_.
-  assert(not d.theIndex.isNil)
   var
     entry = term
     isTitle = false
@@ -337,7 +336,7 @@ proc hash(n: PRstNode): int =
     result = hash(n.text)
   elif n.len > 0:
     result = hash(n.sons[0])
-    for i in 1 .. <len(n):
+    for i in 1 ..< len(n):
       result = result !& hash(n.sons[i])
     result = !$result
 
@@ -398,9 +397,9 @@ proc hash(x: IndexEntry): Hash =
 proc `<-`(a: var IndexEntry, b: IndexEntry) =
   shallowCopy a.keyword, b.keyword
   shallowCopy a.link, b.link
-  if b.linkTitle.isNil: a.linkTitle = nil
+  if b.linkTitle.isNil: a.linkTitle = ""
   else: shallowCopy a.linkTitle, b.linkTitle
-  if b.linkDesc.isNil: a.linkDesc = nil
+  if b.linkDesc.isNil: a.linkDesc = ""
   else: shallowCopy a.linkDesc, b.linkDesc
 
 proc sortIndex(a: var openArray[IndexEntry]) =
@@ -452,7 +451,7 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
           title="$3" data-doc-search-tag="$2" href="$1">$2</a></li>
           """, [url, text, desc])
       else:
-        result.addf("""<li><a class="reference external" 
+        result.addf("""<li><a class="reference external"
           data-doc-search-tag="$2" href="$1">$2</a></li>
           """, [url, text])
       inc j
@@ -524,7 +523,7 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
       titleTag = levels[L].text
     else:
       result.add(level.indentToLevel(levels[L].level))
-      result.addf("""<li><a class="reference" data-doc-search-tag="$1" href="$2"> 
+      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
@@ -608,8 +607,8 @@ proc readIndexDir(dir: string):
           fileEntries[F].linkTitle = extraCols[1].unquoteIndexColumn
           fileEntries[F].linkDesc = extraCols[2].unquoteIndexColumn
         else:
-          fileEntries[F].linkTitle = nil
-          fileEntries[F].linkDesc = nil
+          fileEntries[F].linkTitle = ""
+          fileEntries[F].linkDesc = ""
         inc F
       # Depending on type add this to the list of symbols or table of APIs.
       if title.keyword.isNil:
@@ -657,7 +656,6 @@ proc mergeIndexes*(dir: string): string =
   ## Returns the merged and sorted indices into a single HTML block which can
   ## be further embedded into nimdoc templates.
   var (modules, symbols, docs) = readIndexDir(dir)
-  assert(not symbols.isNil)
 
   result = ""
   # Generate a quick jump list of documents.
diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim
index b7570bd15..99d67824e 100644
--- a/lib/posix/posix_other.nim
+++ b/lib/posix/posix_other.nim
@@ -10,7 +10,7 @@
 {.deadCodeElim: on.}  # dce option deprecated
 
 const
-  hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays
+  hasSpawnH = true # should exist for every Posix system nowadays
   hasAioH = defined(linux)
 
 when defined(linux) and not defined(android):
@@ -43,6 +43,9 @@ type
 
   Dirent* {.importc: "struct dirent",
              header: "<dirent.h>", final, pure.} = object ## dirent_t struct
+    when defined(haiku):
+      d_dev*: Dev ## Device (not POSIX)
+      d_pdev*: Dev ## Parent device (only for queries) (not POSIX)
     d_ino*: Ino  ## File serial number.
     when defined(dragonfly):
       # DragonflyBSD doesn't have `d_reclen` field.
@@ -54,6 +57,9 @@ type
                     ## (not POSIX)
       when defined(linux) or defined(openbsd):
         d_off*: Off  ## Not an offset. Value that ``telldir()`` would return.
+    elif defined(haiku):
+      d_pino*: Ino ## Parent inode (only for queries) (not POSIX)
+      d_reclen*: cushort ## Length of this record. (not POSIX)
 
     d_name*: array[0..255, char] ## Name of entry.
 
@@ -599,6 +605,10 @@ else:
     MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint
       ## No SIGPIPE generated when an attempt to send is made on a stream-oriented socket that is no longer connected.
 
+when defined(haiku):
+  const
+    SIGKILLTHR* = 21 ## BeOS specific: Kill just the thread, not team
+
 when hasSpawnH:
   when defined(linux):
     # better be safe than sorry; Linux has this flag, macosx doesn't, don't
diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim
index 863a6843b..5bf9183ed 100644
--- a/lib/pure/asyncfutures.nim
+++ b/lib/pure/asyncfutures.nim
@@ -219,10 +219,10 @@ proc getHint(entry: StackTraceEntry): string =
   ## We try to provide some hints about stack trace entries that the user
   ## may not be familiar with, in particular calls inside the stdlib.
   result = ""
-  if entry.procname == "processPendingCallbacks":
+  if entry.procname == cstring"processPendingCallbacks":
     if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
       return "Executes pending callbacks"
-  elif entry.procname == "poll":
+  elif entry.procname == cstring"poll":
     if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
       return "Processes asynchronous completion events"
 
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index e7552e3e3..71a1600dc 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -493,8 +493,6 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
   ## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the
   ## protocol uses ``\r\L`` to delimit a new line.
   assert SocketFlag.Peek notin flags ## TODO:
-  assert(not resString.mget.isNil(),
-         "String inside resString future needs to be initialised")
   result = newFuture[void]("asyncnet.recvLineInto")
 
   # TODO: Make the async transformation check for FutureVar params and complete
@@ -657,7 +655,7 @@ when defineSsl:
 
   proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket,
                             handshake: SslHandshakeType,
-                            hostname: string = nil) =
+                            hostname: string = "") =
     ## Wraps a connected socket in an SSL context. This function effectively
     ## turns ``socket`` into an SSL socket.
     ## ``hostname`` should be specified so that the client knows which hostname
@@ -672,7 +670,7 @@ when defineSsl:
 
     case handshake
     of handshakeAsClient:
-      if not hostname.isNil and not isIpAddress(hostname):
+      if hostname.len > 0 and not isIpAddress(hostname):
         # Set the SNI address for this connection. This call can fail if
         # we're not using TLSv1+.
         discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim
index bfecfe447..545958977 100644
--- a/lib/pure/collections/intsets.nim
+++ b/lib/pure/collections/intsets.nim
@@ -184,7 +184,7 @@ proc missingOrExcl*(s: var IntSet, key: int) : bool =
   ## `key` is removed from `s` and false is returned.
   var count = s.elems
   exclImpl(s, key)
-  result = count == s.elems 
+  result = count == s.elems
 
 proc containsOrIncl*(s: var IntSet, key: int): bool =
   ## returns true if `s` contains `key`, otherwise `key` is included in `s`
@@ -212,7 +212,10 @@ proc initIntSet*: IntSet =
 
   #newSeq(result.data, InitIntSetSize)
   #result.max = InitIntSetSize-1
-  result.data = nil
+  when defined(nimNoNilSeqs):
+    result.data = @[]
+  else:
+    result.data = nil
   result.max = 0
   result.counter = 0
   result.head = nil
@@ -222,7 +225,10 @@ proc clear*(result: var IntSet) =
   #setLen(result.data, InitIntSetSize)
   #for i in 0..InitIntSetSize-1: result.data[i] = nil
   #result.max = InitIntSetSize-1
-  result.data = nil
+  when defined(nimNoNilSeqs):
+    result.data = @[]
+  else:
+    result.data = nil
   result.max = 0
   result.counter = 0
   result.head = nil
@@ -234,7 +240,10 @@ proc assign*(dest: var IntSet, src: IntSet) =
   ## copies `src` to `dest`. `dest` does not need to be initialized by
   ## `initIntSet`.
   if src.elems <= src.a.len:
-    dest.data = nil
+    when defined(nimNoNilSeqs):
+      dest.data = @[]
+    else:
+      dest.data = nil
     dest.max = 0
     dest.counter = src.counter
     dest.head = nil
@@ -247,11 +256,9 @@ proc assign*(dest: var IntSet, src: IntSet) =
 
     var it = src.head
     while it != nil:
-
       var h = it.key and dest.max
       while dest.data[h] != nil: h = nextTry(h, dest.max)
       assert(dest.data[h] == nil)
-
       var n: PTrunk
       new(n)
       n.next = dest.head
@@ -259,7 +266,6 @@ proc assign*(dest: var IntSet, src: IntSet) =
       n.bits = it.bits
       dest.head = n
       dest.data[h] = n
-
       it = it.next
 
 proc union*(s1, s2: IntSet): IntSet =
@@ -315,7 +321,7 @@ proc len*(s: IntSet): int {.inline.} =
     for _ in s:
       inc(result)
 
-proc card*(s: IntSet): int {.inline.} = 
+proc card*(s: IntSet): int {.inline.} =
   ## alias for `len() <#len>` _.
   result = s.len()
 
@@ -361,7 +367,7 @@ when isMainModule:
   x.incl(1056)
 
   x.incl(1044)
-  x.excl(1044) 
+  x.excl(1044)
 
   assert x.containsOrIncl(888) == false
   assert 888 in x
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index db33e41af..612624f1d 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -25,6 +25,28 @@ import macros
 when not defined(nimhygiene):
   {.pragma: dirty.}
 
+
+macro evalOnceAs(expAlias, exp: untyped, letAssigneable: static[bool]): untyped =
+  ## Injects ``expAlias`` in caller scope, to avoid bugs involving multiple
+  ##  substitution in macro arguments such as
+  ## https://github.com/nim-lang/Nim/issues/7187
+  ## ``evalOnceAs(myAlias, myExp)`` will behave as ``let myAlias = myExp``
+  ## except when ``letAssigneable`` is false (eg to handle openArray) where
+  ## it just forwards ``exp`` unchanged
+  expectKind(expAlias, nnkIdent)
+  var val = exp
+
+  result = newStmtList()
+  # If `exp` is not a symbol we evaluate it once here and then use the temporary
+  # symbol as alias
+  if exp.kind != nnkSym and letAssigneable:
+    val = genSym()
+    result.add(newLetStmt(val, exp))
+
+  result.add(
+    newProc(name = genSym(nskTemplate, $expAlias), params = [getType(untyped)],
+      body = val, procType = nnkTemplateDef))
+
 proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
   ## Takes several sequences' items and returns them inside a new sequence.
   ##
@@ -496,13 +518,17 @@ template toSeq*(iter: untyped): untyped =
   ##         result = true)
   ##   assert odd_numbers == @[1, 3, 5, 7, 9]
 
+  # Note: see also `mapIt` for explanation of some of the implementation
+  # subtleties.
   when compiles(iter.len):
-    var i = 0
-    var result = newSeq[type(iter)](iter.len)
-    for x in iter:
-      result[i] = x
-      inc i
-    result
+    block:
+      evalOnceAs(iter2, iter, true)
+      var result = newSeq[type(iter)](iter2.len)
+      var i = 0
+      for x in iter2:
+        result[i] = x
+        inc i
+      result
   else:
     var result: seq[type(iter)] = @[]
     for x in iter:
@@ -635,8 +661,7 @@ template mapIt*(s, typ, op: untyped): untyped =
     result.add(op)
   result
 
-
-template mapIt*(s, op: untyped): untyped =
+template mapIt*(s: typed, op: untyped): untyped =
   ## Convenience template around the ``map`` proc to reduce typing.
   ##
   ## The template injects the ``it`` variable which you can use directly in an
@@ -653,19 +678,24 @@ template mapIt*(s, op: untyped): untyped =
     block:
       var it{.inject.}: type(items(s));
       op))
-  var result: seq[outType]
   when compiles(s.len):
-    let t = s
-    var i = 0
-    result = newSeq[outType](t.len)
-    for it {.inject.} in t:
-      result[i] = op
-      i += 1
+    block: # using a block avoids https://github.com/nim-lang/Nim/issues/8580
+
+      # BUG: `evalOnceAs(s2, s, false)` would lead to C compile errors
+      # (`error: use of undeclared identifier`) instead of Nim compile errors
+      evalOnceAs(s2, s, compiles((let _ = s)))
+
+      var i = 0
+      var result = newSeq[outType](s2.len)
+      for it {.inject.} in s2:
+        result[i] = op
+        i += 1
+      result
   else:
-    result = @[]
+    var result: seq[outType] = @[]
     for it {.inject.} in s:
       result.add(op)
-  result
+    result
 
 template applyIt*(varSeq, op: untyped) =
   ## Convenience template around the mutable ``apply`` proc to reduce typing.
@@ -752,6 +782,14 @@ macro mapLiterals*(constructor, op: untyped;
 
 when isMainModule:
   import strutils
+
+  # helper for testing double substitution side effects which are handled
+  # by `evalOnceAs`
+  var counter = 0
+  proc identity[T](a:T):auto=
+    counter.inc
+    a
+
   block: # concat test
     let
       s1 = @[1, 2, 3]
@@ -997,6 +1035,12 @@ when isMainModule:
           result = true)
     assert odd_numbers == @[1, 3, 5, 7, 9]
 
+  block:
+    # tests https://github.com/nim-lang/Nim/issues/7187
+    counter = 0
+    let ret = toSeq(@[1, 2, 3].identity().filter(proc (x: int): bool = x < 3))
+    doAssert ret == @[1, 2]
+    doAssert counter == 1
   block: # foldl tests
     let
       numbers = @[5, 9, 11]
@@ -1023,10 +1067,12 @@ when isMainModule:
     assert multiplication == 495, "Multiplication is (5*(9*(11)))"
     assert concatenation == "nimiscool"
 
-  block: # mapIt tests
+  block: # mapIt + applyIt test
+    counter = 0
     var
       nums = @[1, 2, 3, 4]
-      strings = nums.mapIt($(4 * it))
+      strings = nums.identity.mapIt($(4 * it))
+    doAssert counter == 1
     nums.applyIt(it * 3)
     assert nums[0] + nums[3] == 15
     assert strings[2] == "12"
@@ -1044,5 +1090,51 @@ when isMainModule:
     doAssert mapLiterals((1, ("abc"), 2), float, nested=false) == (float(1), "abc", float(2))
     doAssert mapLiterals(([1], ("abc"), 2), `$`, nested=true) == (["1"], "abc", "2")
 
+  block: # mapIt with openArray
+    counter = 0
+    proc foo(x: openArray[int]): seq[int] = x.mapIt(it * 10)
+    doAssert foo([identity(1),identity(2)]) == @[10, 20]
+    doAssert counter == 2
+
+  block: # mapIt with direct openArray
+    proc foo1(x: openArray[int]): seq[int] = x.mapIt(it * 10)
+    counter = 0
+    doAssert foo1(openArray[int]([identity(1),identity(2)])) == @[10,20]
+    doAssert counter == 2
+
+    # Corner cases (openArray litterals should not be common)
+    template foo2(x: openArray[int]): seq[int] = x.mapIt(it * 10)
+    counter = 0
+    doAssert foo2(openArray[int]([identity(1),identity(2)])) == @[10,20]
+    # TODO: this fails; not sure how to fix this case
+    # doAssert counter == 2
+
+    counter = 0
+    doAssert openArray[int]([identity(1), identity(2)]).mapIt(it) == @[1,2]
+    # ditto
+    # doAssert counter == 2
+
+  block: # mapIt empty test, see https://github.com/nim-lang/Nim/pull/8584#pullrequestreview-144723468
+    # NOTE: `[].mapIt(it)` is illegal, just as `let a = @[]` is (lacks type
+    # of elements)
+    doAssert: not compiles(mapIt(@[], it))
+    doAssert: not compiles(mapIt([], it))
+    doAssert newSeq[int](0).mapIt(it) == @[]
+
+  block: # mapIt redifinition check, see https://github.com/nim-lang/Nim/issues/8580
+    let s2 = [1,2].mapIt(it)
+    doAssert s2 == @[1,2]
+
+  block:
+    counter = 0
+    doAssert [1,2].identity().mapIt(it*2).mapIt(it*10) == @[20, 40]
+    # https://github.com/nim-lang/Nim/issues/7187 test case
+    doAssert counter == 1
+
+  block: # mapIt with invalid RHS for `let` (#8566)
+    type X = enum
+      A, B
+    doAssert mapIt(X, $it) == @["A", "B"]
+
   when not defined(testing):
     echo "Finished doc tests"
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index 308f31eae..f85de7546 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -1329,7 +1329,7 @@ when isMainModule:
     doAssert clearTable[42] == "asd"
     clearTable.clear()
     doAssert(not clearTable.hasKey(123123))
-    doAssert clearTable.getOrDefault(42) == nil
+    doAssert clearTable.getOrDefault(42) == ""
 
   block: #5482
     var a = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable()
diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim
index 6d41aa1b2..541265da9 100644
--- a/lib/pure/concurrency/cpuinfo.nim
+++ b/lib/pure/concurrency/cpuinfo.nim
@@ -43,6 +43,14 @@ when defined(genode):
   proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {.
     importcpp: "@->cpu().affinity_space().total()".}
 
+when defined(haiku):
+  {.emit: "#include <OS.h>".}
+  type
+    SystemInfo {.importc: "system_info", bycopy.} = object
+      cpuCount {.importc: "cpu_count".}: uint32
+
+  proc getSystemInfo(info: ptr SystemInfo): int32 {.importc: "get_system_info".}
+
 proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
   ## returns the numer of the processors/cores the machine has.
   ## Returns 0 if it cannot be detected.
@@ -86,6 +94,10 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
     result = sysconf(SC_NPROC_ONLN)
   elif defined(genode):
     result = runtimeEnv.affinitySpaceTotal().int
+  elif defined(haiku):
+    var sysinfo: SystemInfo
+    if getSystemInfo(addr sysinfo) == 0:
+      result = sysinfo.cpuCount.int
   else:
     result = sysconf(SC_NPROCESSORS_ONLN)
   if result <= 0: result = 0
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index 6ec71e912..f3b13fac5 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -30,7 +30,7 @@ proc destroySemaphore(cv: var Semaphore) {.inline.} =
   deinitCond(cv.c)
   deinitLock(cv.L)
 
-proc await(cv: var Semaphore) =
+proc blockUntil(cv: var Semaphore) =
   acquire(cv.L)
   while cv.counter <= 0:
     wait(cv.c, cv.L)
@@ -81,7 +81,7 @@ proc closeBarrier(b: ptr Barrier) {.compilerProc.} =
     fence()
     b.interest = true
     fence()
-    while b.left != b.entered: await(b.cv)
+    while b.left != b.entered: blockUntil(b.cv)
     destroySemaphore(b.cv)
 
 {.pop.}
@@ -89,8 +89,6 @@ proc closeBarrier(b: ptr Barrier) {.compilerProc.} =
 # ----------------------------------------------------------------------------
 
 type
-  foreign* = object ## a region that indicates the pointer comes from a
-                    ## foreign thread heap.
   AwaitInfo = object
     cv: Semaphore
     idx: int
@@ -99,7 +97,7 @@ type
   FlowVarBaseObj = object of RootObj
     ready, usesSemaphore, awaited: bool
     cv: Semaphore #\
-    # for 'awaitAny' support
+    # for 'blockUntilAny' support
     ai: ptr AwaitInfo
     idx: int
     data: pointer  # we incRef and unref it to keep it alive; note this MUST NOT
@@ -130,12 +128,12 @@ type
     q: ToFreeQueue
     readyForTask: Semaphore
 
-proc await*(fv: FlowVarBase) =
+proc blockUntil*(fv: FlowVarBase) =
   ## waits until the value for the flowVar arrives. Usually it is not necessary
   ## to call this explicitly.
   if fv.usesSemaphore and not fv.awaited:
     fv.awaited = true
-    await(fv.cv)
+    blockUntil(fv.cv)
     destroySemaphore(fv.cv)
 
 proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool =
@@ -143,7 +141,7 @@ proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool =
     w.data = data
     w.f = fn
     signal(w.taskArrived)
-    await(w.taskStarted)
+    blockUntil(w.taskStarted)
     result = true
 
 proc cleanFlowVars(w: ptr Worker) =
@@ -178,11 +176,11 @@ proc attach(fv: FlowVarBase; i: int): bool =
   release(fv.cv.L)
 
 proc finished(fv: FlowVarBase) =
-  doAssert fv.ai.isNil, "flowVar is still attached to an 'awaitAny'"
+  doAssert fv.ai.isNil, "flowVar is still attached to an 'blockUntilAny'"
   # we have to protect against the rare cases where the owner of the flowVar
   # simply disregards the flowVar and yet the "flowVar" has not yet written
   # anything to it:
-  await(fv)
+  blockUntil(fv)
   if fv.data.isNil: return
   let owner = cast[ptr Worker](fv.owner)
   let q = addr(owner.q)
@@ -191,7 +189,7 @@ proc finished(fv: FlowVarBase) =
     #echo "EXHAUSTED!"
     release(q.lock)
     wakeupWorkerToProcessQueue(owner)
-    await(q.empty)
+    blockUntil(q.empty)
     acquire(q.lock)
   q.data[q.len] = cast[pointer](fv.data)
   inc q.len
@@ -222,7 +220,7 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
   ## to ``action``. Note that due to Nim's parameter passing semantics this
   ## means that ``T`` doesn't need to be copied and so ``awaitAndThen`` can
   ## sometimes be more efficient than ``^``.
-  await(fv)
+  blockUntil(fv)
   when T is string or T is seq:
     action(cast[T](fv.data))
   elif T is ref:
@@ -231,31 +229,31 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
     action(fv.blob)
   finished(fv)
 
-proc unsafeRead*[T](fv: FlowVar[ref T]): foreign ptr T =
+proc unsafeRead*[T](fv: FlowVar[ref T]): ptr T =
   ## blocks until the value is available and then returns this value.
-  await(fv)
-  result = cast[foreign ptr T](fv.data)
+  blockUntil(fv)
+  result = cast[ptr T](fv.data)
 
 proc `^`*[T](fv: FlowVar[ref T]): ref T =
   ## blocks until the value is available and then returns this value.
-  await(fv)
+  blockUntil(fv)
   let src = cast[ref T](fv.data)
   deepCopy result, src
 
 proc `^`*[T](fv: FlowVar[T]): T =
   ## blocks until the value is available and then returns this value.
-  await(fv)
+  blockUntil(fv)
   when T is string or T is seq:
     # XXX closures? deepCopy?
     result = cast[T](fv.data)
   else:
     result = fv.blob
 
-proc awaitAny*(flowVars: openArray[FlowVarBase]): int =
+proc blockUntilAny*(flowVars: openArray[FlowVarBase]): int =
   ## awaits any of the given flowVars. Returns the index of one flowVar for
-  ## which a value arrived. A flowVar only supports one call to 'awaitAny' at
-  ## the same time. That means if you awaitAny([a,b]) and awaitAny([b,c]) the second
-  ## call will only await 'c'. If there is no flowVar left to be able to wait
+  ## which a value arrived. A flowVar only supports one call to 'blockUntilAny' at
+  ## the same time. That means if you blockUntilAny([a,b]) and blockUntilAny([b,c]) the second
+  ## call will only blockUntil 'c'. If there is no flowVar left to be able to wait
   ## on, -1 is returned.
   ## **Note**: This results in non-deterministic behaviour and should be avoided.
   var ai: AwaitInfo
@@ -271,7 +269,7 @@ proc awaitAny*(flowVars: openArray[FlowVarBase]): int =
       inc conflicts
   if conflicts < flowVars.len:
     if result < 0:
-      await(ai.cv)
+      blockUntil(ai.cv)
       result = ai.idx
     for i in 0 .. flowVars.high:
       discard cas(addr flowVars[i].ai, addr ai, nil)
@@ -328,7 +326,7 @@ proc slave(w: ptr Worker) {.thread.} =
       w.ready = true
     readyWorker = w
     signal(gSomeReady)
-    await(w.taskArrived)
+    blockUntil(w.taskArrived)
     # XXX Somebody needs to look into this (why does this assertion fail
     # in Visual Studio?)
     when not defined(vcc) and not defined(tcc): assert(not w.ready)
@@ -353,7 +351,7 @@ proc distinguishedSlave(w: ptr Worker) {.thread.} =
     else:
       w.ready = true
     signal(w.readyForTask)
-    await(w.taskArrived)
+    blockUntil(w.taskArrived)
     assert(not w.ready)
     w.f(w, w.data)
     if w.q.len != 0: w.cleanFlowVars
@@ -501,7 +499,7 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
         # on the current thread instead.
         var self = addr(workersData[localThreadId-1])
         fn(self, data)
-        await(self.taskStarted)
+        blockUntil(self.taskStarted)
         return
 
     if isSlave:
@@ -526,7 +524,7 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
 
         inc numSlavesWaiting
 
-    await(gSomeReady)
+    blockUntil(gSomeReady)
 
     if isSlave:
       withLock numSlavesLock:
@@ -544,7 +542,7 @@ proc nimSpawn4(fn: WorkerProc; data: pointer; id: ThreadId) {.compilerProc.} =
   release(distinguishedLock)
   while true:
     if selectWorker(addr(distinguishedData[id]), fn, data): break
-    await(distinguishedData[id].readyForTask)
+    blockUntil(distinguishedData[id].readyForTask)
 
 
 proc sync*() =
@@ -557,7 +555,7 @@ proc sync*() =
       if not allReady: break
       allReady = allReady and workersData[i].ready
     if allReady: break
-    await(gSomeReady)
+    blockUntil(gSomeReady)
     inc toRelease
 
   for i in 0 ..< toRelease:
diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim
index b6ef30e7c..6d7dcf078 100644
--- a/lib/pure/coro.nim
+++ b/lib/pure/coro.nim
@@ -43,6 +43,10 @@ when defined(windows):
     {.warning: "ucontext coroutine backend is not available on windows, defaulting to fibers.".}
   when defined(nimCoroutinesSetjmp):
     {.warning: "setjmp coroutine backend is not available on windows, defaulting to fibers.".}
+elif defined(haiku):
+  const coroBackend = CORO_BACKEND_SETJMP
+  when defined(nimCoroutinesUcontext):
+    {.warning: "ucontext coroutine backend is not available on haiku, defaulting to setjmp".}
 elif defined(nimCoroutinesSetjmp) or defined(nimCoroutinesSetjmpBundled):
   const coroBackend = CORO_BACKEND_SETJMP
 else:
@@ -55,21 +59,21 @@ when coroBackend == CORO_BACKEND_FIBERS:
 
 elif coroBackend == CORO_BACKEND_UCONTEXT:
   type
-    stack_t {.importc, header: "<sys/ucontext.h>".} = object
+    stack_t {.importc, header: "<ucontext.h>".} = object
       ss_sp: pointer
       ss_flags: int
       ss_size: int
 
-    ucontext_t {.importc, header: "<sys/ucontext.h>".} = object
+    ucontext_t {.importc, header: "<ucontext.h>".} = object
       uc_link: ptr ucontext_t
       uc_stack: stack_t
 
     Context = ucontext_t
 
-  proc getcontext(context: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".}
-  proc setcontext(context: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".}
-  proc swapcontext(fromCtx, toCtx: var ucontext_t): int32 {.importc, header: "<sys/ucontext.h>".}
-  proc makecontext(context: var ucontext_t, fn: pointer, argc: int32) {.importc, header: "<sys/ucontext.h>", varargs.}
+  proc getcontext(context: var ucontext_t): int32 {.importc, header: "<ucontext.h>".}
+  proc setcontext(context: var ucontext_t): int32 {.importc, header: "<ucontext.h>".}
+  proc swapcontext(fromCtx, toCtx: var ucontext_t): int32 {.importc, header: "<ucontext.h>".}
+  proc makecontext(context: var ucontext_t, fn: pointer, argc: int32) {.importc, header: "<ucontext.h>", varargs.}
 
 elif coroBackend == CORO_BACKEND_SETJMP:
   proc coroExecWithStack*(fn: pointer, stack: pointer) {.noreturn, importc: "narch_$1", fastcall.}
@@ -190,7 +194,7 @@ proc switchTo(current, to: CoroutinePtr) =
         elif to.state == CORO_CREATED:
           # Coroutine is started.
           coroExecWithStack(runCurrentTask, to.stack.bottom)
-          doAssert false
+          #doAssert false
     else:
       {.error: "Invalid coroutine backend set.".}
   # Execution was just resumed. Restore frame information and set active stack.
diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim
index 0adba5b1e..5847cfadb 100644
--- a/lib/pure/distros.nim
+++ b/lib/pure/distros.nim
@@ -126,6 +126,8 @@ type
     OpenBSD
     DragonFlyBSD
 
+    Haiku
+
 
 const
   LacksDevPackages* = {Distribution.Gentoo, Distribution.Slackware,
@@ -166,6 +168,8 @@ proc detectOsImpl(d: Distribution): bool =
   of Distribution.Solaris:
     let uname = toLowerAscii(uname())
     result = ("sun" in uname) or ("solaris" in uname)
+  of Distribution.Haiku:
+    result = defined(haiku)
   else:
     let dd = toLowerAscii($d)
     result = dd in toLowerAscii(uname()) or dd in toLowerAscii(release())
@@ -224,6 +228,8 @@ proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) =
       result = ("pacman -S " & p, true)
     else:
       result = ("<your package manager here> install " & p, true)
+  elif defined(haiku):
+    result = ("pkgman install " & p, true)
   else:
     result = ("brew install " & p, false)
 
diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim
index 3c1cf73f4..2039a31be 100644
--- a/lib/pure/encodings.nim
+++ b/lib/pure/encodings.nim
@@ -255,7 +255,7 @@ when defined(windows):
 
 else:
   when defined(haiku):
-    const iconvDll = "(libc.so.6|libiconv.so|libtextencoding.so)"
+    const iconvDll = "libiconv.so"
   elif defined(macosx):
     const iconvDll = "libiconv.dylib"
   else:
@@ -272,6 +272,8 @@ else:
     const EILSEQ = 86.cint
   elif defined(solaris):
     const EILSEQ = 88.cint
+  elif defined(haiku):
+    const EILSEQ = -2147454938.cint
 
   var errno {.importc, header: "<errno.h>".}: cint
 
diff --git a/lib/pure/fenv.nim b/lib/pure/fenv.nim
index c946c4261..0725973ca 100644
--- a/lib/pure/fenv.nim
+++ b/lib/pure/fenv.nim
@@ -12,7 +12,7 @@
 
 {.deadCodeElim: on.}  # dce option deprecated
 
-when defined(Posix) and not defined(haiku):
+when defined(Posix):
   {.passl: "-lm".}
 
 var
diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim
index c38c36874..f54fe87f7 100644
--- a/lib/pure/htmlparser.nim
+++ b/lib/pure/htmlparser.nim
@@ -1869,7 +1869,6 @@ proc entityToUtf8*(entity: string): string =
   ## "" is returned if the entity name is unknown. The HTML parser
   ## already converts entities to UTF-8.
   runnableExamples:
-    doAssert entityToUtf8(nil) == ""
     doAssert entityToUtf8("") == ""
     doAssert entityToUtf8("a") == ""
     doAssert entityToUtf8("gt") == ">"
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 8b4fb0f8c..0192e71e7 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -378,23 +378,23 @@ proc newMultipartData*: MultipartData =
   ## Constructs a new ``MultipartData`` object.
   MultipartData(content: @[])
 
-proc add*(p: var MultipartData, name, content: string, filename: string = nil,
-          contentType: string = nil) =
+proc add*(p: var MultipartData, name, content: string, filename: string = "",
+          contentType: string = "") =
   ## Add a value to the multipart data. Raises a `ValueError` exception if
   ## `name`, `filename` or `contentType` contain newline characters.
 
   if {'\c','\L'} in name:
     raise newException(ValueError, "name contains a newline character")
-  if filename != nil and {'\c','\L'} in filename:
+  if {'\c','\L'} in filename:
     raise newException(ValueError, "filename contains a newline character")
-  if contentType != nil and {'\c','\L'} in contentType:
+  if {'\c','\L'} in contentType:
     raise newException(ValueError, "contentType contains a newline character")
 
   var str = "Content-Disposition: form-data; name=\"" & name & "\""
-  if filename != nil:
+  if filename.len > 0:
     str.add("; filename=\"" & filename & "\"")
   str.add("\c\L")
-  if contentType != nil:
+  if contentType.len > 0:
     str.add("Content-Type: " & contentType & "\c\L")
   str.add("\c\L" & content & "\c\L")
 
@@ -434,7 +434,7 @@ proc addFiles*(p: var MultipartData, xs: openarray[tuple[name, file: string]]):
     var contentType: string
     let (_, fName, ext) = splitFile(file)
     if ext.len > 0:
-      contentType = m.getMimetype(ext[1..ext.high], nil)
+      contentType = m.getMimetype(ext[1..ext.high], "")
     p.add(name, readFile(file), fName & ext, contentType)
   result = p
 
@@ -457,7 +457,7 @@ proc `[]=`*(p: var MultipartData, name: string,
   p.add(name, file.content, file.name, file.contentType)
 
 proc format(p: MultipartData): tuple[contentType, body: string] =
-  if p == nil or p.content == nil or p.content.len == 0:
+  if p == nil or p.content.len == 0:
     return ("", "")
 
   # Create boundary that is not in the data to be formatted
@@ -807,6 +807,7 @@ type
     lastProgressReport: float
     when SocketType is AsyncSocket:
       bodyStream: FutureStream[string]
+      parseBodyFut: Future[void]
     else:
       bodyStream: Stream
     getBody: bool ## When `false`, the body is never read in requestAux.
@@ -1066,10 +1067,14 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
   if getBody:
     when client is HttpClient:
       client.bodyStream = newStringStream()
+      result.bodyStream = client.bodyStream
+      parseBody(client, result.headers, result.version)
     else:
       client.bodyStream = newFutureStream[string]("parseResponse")
-    await parseBody(client, result.headers, result.version)
-    result.bodyStream = client.bodyStream
+      result.bodyStream = client.bodyStream
+      assert(client.parseBodyFut.isNil or client.parseBodyFut.finished)
+      client.parseBodyFut = parseBody(client, result.headers, result.version)
+        # do not wait here for the body request to complete
 
 proc newConnection(client: HttpClient | AsyncHttpClient,
                    url: Uri) {.multisync.} =
@@ -1159,6 +1164,12 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string,
   # Helper that actually makes the request. Does not handle redirects.
   let requestUrl = parseUri(url)
 
+  when client is AsyncHttpClient:
+    if not client.parseBodyFut.isNil:
+      # let the current operation finish before making another request
+      await client.parseBodyFut
+      client.parseBodyFut = nil
+
   await newConnection(client, requestUrl)
 
   let effectiveHeaders = client.headers.override(headers)
diff --git a/lib/pure/ioselects/ioselectors_kqueue.nim b/lib/pure/ioselects/ioselectors_kqueue.nim
index 142e988d0..0e133f650 100644
--- a/lib/pure/ioselects/ioselectors_kqueue.nim
+++ b/lib/pure/ioselects/ioselectors_kqueue.nim
@@ -567,8 +567,11 @@ proc selectInto*[T](s: Selector[T], timeout: int,
         doAssert(true, "Unsupported kqueue filter in the queue!")
 
       if (kevent.flags and EV_EOF) != 0:
+        # TODO this error handling needs to be rethought.
+        # `fflags` can sometimes be `0x80000000` and thus we use 'cast'
+        # here:
         if kevent.fflags != 0:
-          rkey.errorCode = kevent.fflags.OSErrorCode
+          rkey.errorCode = cast[OSErrorCode](kevent.fflags)
         else:
           # This assumes we are dealing with sockets.
           # TODO: For future-proofing it might be a good idea to give the
diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim
index cd6a72b44..521b31a64 100644
--- a/lib/pure/ioselects/ioselectors_select.nim
+++ b/lib/pure/ioselects/ioselectors_select.nim
@@ -310,7 +310,10 @@ proc selectInto*[T](s: Selector[T], timeout: int,
   var rset, wset, eset: FdSet
 
   if timeout != -1:
-    tv.tv_sec = timeout.int32 div 1_000
+    when defined(genode):
+      tv.tv_sec = Time(timeout div 1_000)
+    else:
+      tv.tv_sec = timeout.int32 div 1_000
     tv.tv_usec = (timeout.int32 %% 1_000) * 1_000
   else:
     ptv = nil
@@ -391,7 +394,6 @@ proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
     for i in 0..<FD_SETSIZE:
       if s.fds[i].ident == fdi:
         return true
-      inc(i)
 
 when hasThreadSupport:
   template withSelectLock[T](s: Selector[T], body: untyped) =
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index b9279b18c..69ffb1796 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -1144,7 +1144,7 @@ proc processType(typeName: NimNode, obj: NimNode,
       result = quote do:
         (
           verifyJsonKind(`jsonNode`, {JString, JNull}, astToStr(`jsonNode`));
-          if `jsonNode`.kind == JNull: nil else: `jsonNode`.str
+          if `jsonNode`.kind == JNull: "" else: `jsonNode`.str
         )
     of "biggestint":
       result = quote do:
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index b921b1841..79f287651 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -49,7 +49,7 @@ proc fac*(n: int): int =
 
 {.push checks:off, line_dir:off, stack_trace:off.}
 
-when defined(Posix) and not defined(haiku):
+when defined(Posix):
   {.passl: "-lm".}
 
 const
@@ -162,9 +162,6 @@ when not defined(JS): # C
   proc log10*(x: float32): float32 {.importc: "log10f", header: "<math.h>".}
   proc log10*(x: float64): float64 {.importc: "log10", header: "<math.h>".}
     ## Computes the common logarithm (base 10) of `x`
-  proc log2*(x: float32): float32 {.importc: "log2f", header: "<math.h>".}
-  proc log2*(x: float64): float64 {.importc: "log2", header: "<math.h>".}
-    ## Computes the binary logarithm (base 2) of `x`
   proc exp*(x: float32): float32 {.importc: "expf", header: "<math.h>".}
   proc exp*(x: float64): float64 {.importc: "exp", header: "<math.h>".}
     ## Computes the exponential function of `x` (pow(E, x))
@@ -268,6 +265,8 @@ proc arcsech*[T: float32|float64](x: T): T = arccosh(1.0 / x)
 proc arccsch*[T: float32|float64](x: T): T = arcsinh(1.0 / x)
   ## Computes the inverse hyperbolic cosecant of `x`
 
+const windowsCC89 = defined(windows) and (defined(vcc) or defined(bcc))
+
 when not defined(JS): # C
   proc hypot*(x, y: float32): float32 {.importc: "hypotf", header: "<math.h>".}
   proc hypot*(x, y: float64): float64 {.importc: "hypot", header: "<math.h>".}
@@ -280,25 +279,27 @@ when not defined(JS): # C
     ##
     ## To compute power between integers, use `^` e.g. 2 ^ 6
 
-  proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".}
-  proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".}
-    ## The error function
-  proc erfc*(x: float32): float32 {.importc: "erfcf", header: "<math.h>".}
-  proc erfc*(x: float64): float64 {.importc: "erfc", header: "<math.h>".}
-    ## The complementary error function
-
-  proc gamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".}
-  proc gamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".}
-    ## The gamma function
-  proc tgamma*(x: float32): float32
-    {.deprecated: "use gamma instead", importc: "tgammaf", header: "<math.h>".}
-  proc tgamma*(x: float64): float64
-    {.deprecated: "use gamma instead", importc: "tgamma", header: "<math.h>".}
-    ## The gamma function
-    ## **Deprecated since version 0.19.0**: Use ``gamma`` instead.
-  proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "<math.h>".}
-  proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "<math.h>".}
-    ## Natural log of the gamma function
+  # TODO: add C89 version on windows
+  when not windowsCC89:
+    proc erf*(x: float32): float32 {.importc: "erff", header: "<math.h>".}
+    proc erf*(x: float64): float64 {.importc: "erf", header: "<math.h>".}
+      ## The error function
+    proc erfc*(x: float32): float32 {.importc: "erfcf", header: "<math.h>".}
+    proc erfc*(x: float64): float64 {.importc: "erfc", header: "<math.h>".}
+      ## The complementary error function
+
+    proc gamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".}
+    proc gamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".}
+      ## The gamma function
+    proc tgamma*(x: float32): float32
+      {.deprecated: "use gamma instead", importc: "tgammaf", header: "<math.h>".}
+    proc tgamma*(x: float64): float64
+      {.deprecated: "use gamma instead", importc: "tgamma", header: "<math.h>".}
+      ## The gamma function
+      ## **Deprecated since version 0.19.0**: Use ``gamma`` instead.
+    proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "<math.h>".}
+    proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "<math.h>".}
+      ## Natural log of the gamma function
 
   proc floor*(x: float32): float32 {.importc: "floorf", header: "<math.h>".}
   proc floor*(x: float64): float64 {.importc: "floor", header: "<math.h>".}
@@ -314,7 +315,7 @@ when not defined(JS): # C
     ## .. code-block:: nim
     ##  echo ceil(-2.1) ## -2.0
 
-  when defined(windows) and (defined(vcc) or defined(bcc)):
+  when windowsCC89:
     # MSVC 2010 don't have trunc/truncf
     # this implementation was inspired by Go-lang Math.Trunc
     proc truncImpl(f: float64): float64 =
@@ -452,6 +453,28 @@ when not defined(JS):
     var exp: int32
     result = c_frexp(x, exp)
     exponent = exp
+
+  when windowsCC89:
+    # taken from Go-lang Math.Log2
+    const ln2 = 0.693147180559945309417232121458176568075500134360255254120680009
+    template log2Impl[T](x: T): T =
+      var exp: int32
+      var frac = frexp(x, exp)
+      # Make sure exact powers of two give an exact answer.
+      # Don't depend on Log(0.5)*(1/Ln2)+exp being exactly exp-1.
+      if frac == 0.5: return T(exp - 1)
+      log10(frac)*(1/ln2) + T(exp)
+
+    proc log2*(x: float32): float32 = log2Impl(x)
+    proc log2*(x: float64): float64 = log2Impl(x)
+      ## Log2 returns the binary logarithm of x.
+      ## The special cases are the same as for Log.
+
+  else:
+    proc log2*(x: float32): float32 {.importc: "log2f", header: "<math.h>".}
+    proc log2*(x: float64): float64 {.importc: "log2", header: "<math.h>".}
+      ## Computes the binary logarithm (base 2) of `x`
+
 else:
   proc frexp*[T: float32|float64](x: T, exponent: var int): T =
     if x == 0.0:
@@ -506,8 +529,8 @@ proc sgn*[T: SomeNumber](x: T): int {.inline.} =
 {.pop.}
 
 proc `^`*[T](x: T, y: Natural): T =
-  ## Computes ``x`` to the power ``y`. ``x`` must be non-negative, use
-  ## `pow <#pow,float,float>` for negative exponents.
+  ## Computes ``x`` to the power ``y``. ``x`` must be non-negative, use
+  ## `pow <#pow,float,float>`_ for negative exponents.
   when compiles(y >= T(0)):
     assert y >= T(0)
   else:
@@ -564,7 +587,7 @@ proc lcm*[T](x, y: T): T =
   ## Computes the least common multiple of ``x`` and ``y``.
   x div gcd(x, y) * y
 
-when isMainModule and not defined(JS):
+when isMainModule and not defined(JS) and not windowsCC89:
   # Check for no side effect annotation
   proc mySqrt(num: float): float {.noSideEffect.} =
     return sqrt(num)
@@ -692,3 +715,14 @@ when isMainModule:
 
   block: # log
     doAssert log(4.0, 3.0) == ln(4.0) / ln(3.0)
+    doAssert log2(8.0'f64) == 3.0'f64
+    doAssert log2(4.0'f64) == 2.0'f64
+    doAssert log2(2.0'f64) == 1.0'f64
+    doAssert log2(1.0'f64) == 0.0'f64
+    doAssert classify(log2(0.0'f64)) == fcNegInf
+
+    doAssert log2(8.0'f32) == 3.0'f32
+    doAssert log2(4.0'f32) == 2.0'f32
+    doAssert log2(2.0'f32) == 1.0'f32
+    doAssert log2(1.0'f32) == 0.0'f32
+    doAssert classify(log2(0.0'f32)) == fcNegInf
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index 5545ca2d1..d5fb0f89b 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -248,9 +248,10 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
   hints.ai_socktype = toInt(sockType)
   hints.ai_protocol = toInt(protocol)
   # OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED.
-  # FreeBSD doesn't support AI_V4MAPPED but defines the macro.
+  # FreeBSD, Haiku don't support AI_V4MAPPED but defines the macro.
   # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092
-  when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and not defined(android):
+  # https://dev.haiku-os.org/ticket/14323
+  when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and not defined(android) and not defined(haiku):
     if domain == AF_INET6:
       hints.ai_flags = AI_V4MAPPED
   var gaiResult = getaddrinfo(address, $port, addr(hints), result)
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 771e7de10..0e56100d9 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -231,7 +231,7 @@ proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
     raiseOSError(osLastError())
   result = newSocket(fd, domain, sockType, protocol, buffered)
 
-proc parseIPv4Address(address_str: string): IpAddress =
+proc parseIPv4Address(addressStr: string): IpAddress =
   ## Parses IPv4 adresses
   ## Raises EInvalidValue on errors
   var
@@ -241,15 +241,15 @@ proc parseIPv4Address(address_str: string): IpAddress =
 
   result.family = IpAddressFamily.IPv4
 
-  for i in 0 .. high(address_str):
-    if address_str[i] in strutils.Digits: # Character is a number
+  for i in 0 .. high(addressStr):
+    if addressStr[i] in strutils.Digits: # Character is a number
       currentByte = currentByte * 10 +
-        cast[uint16](ord(address_str[i]) - ord('0'))
+        cast[uint16](ord(addressStr[i]) - ord('0'))
       if currentByte > 255'u16:
         raise newException(ValueError,
           "Invalid IP Address. Value is out of range")
       seperatorValid = true
-    elif address_str[i] == '.': # IPv4 address separator
+    elif addressStr[i] == '.': # IPv4 address separator
       if not seperatorValid or byteCount >= 3:
         raise newException(ValueError,
           "Invalid IP Address. The address consists of too many groups")
@@ -265,11 +265,11 @@ proc parseIPv4Address(address_str: string): IpAddress =
     raise newException(ValueError, "Invalid IP Address")
   result.address_v4[byteCount] = cast[uint8](currentByte)
 
-proc parseIPv6Address(address_str: string): IpAddress =
+proc parseIPv6Address(addressStr: string): IpAddress =
   ## Parses IPv6 adresses
   ## Raises EInvalidValue on errors
   result.family = IpAddressFamily.IPv6
-  if address_str.len < 2:
+  if addressStr.len < 2:
     raise newException(ValueError, "Invalid IP Address")
 
   var
@@ -282,7 +282,7 @@ proc parseIPv6Address(address_str: string): IpAddress =
     v4StartPos = -1
     byteCount = 0
 
-  for i,c in address_str:
+  for i,c in addressStr:
     if c == ':':
       if not seperatorValid:
         raise newException(ValueError,
@@ -293,7 +293,7 @@ proc parseIPv6Address(address_str: string): IpAddress =
             "Invalid IP Address. Address contains more than one \"::\" seperator")
         dualColonGroup = groupCount
         seperatorValid = false
-      elif i != 0 and i != high(address_str):
+      elif i != 0 and i != high(addressStr):
         if groupCount >= 8:
           raise newException(ValueError,
             "Invalid IP Address. The address consists of too many groups")
@@ -303,11 +303,11 @@ proc parseIPv6Address(address_str: string): IpAddress =
         groupCount.inc()
         if dualColonGroup != -1: seperatorValid = false
       elif i == 0: # only valid if address starts with ::
-        if address_str[1] != ':':
+        if addressStr[1] != ':':
           raise newException(ValueError,
             "Invalid IP Address. Address may not start with \":\"")
-      else: # i == high(address_str) - only valid if address ends with ::
-        if address_str[high(address_str)-1] != ':':
+      else: # i == high(addressStr) - only valid if address ends with ::
+        if addressStr[high(addressStr)-1] != ':':
           raise newException(ValueError,
             "Invalid IP Address. Address may not end with \":\"")
       lastWasColon = true
@@ -345,7 +345,7 @@ proc parseIPv6Address(address_str: string): IpAddress =
       result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
       groupCount.inc()
   else: # Must parse IPv4 address
-    for i,c in address_str[v4StartPos..high(address_str)]:
+    for i,c in addressStr[v4StartPos..high(addressStr)]:
       if c in strutils.Digits: # Character is a number
         currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
         if currentShort > 255'u32:
@@ -386,21 +386,21 @@ proc parseIPv6Address(address_str: string): IpAddress =
     raise newException(ValueError,
       "Invalid IP Address. The address consists of too many groups")
 
-proc parseIpAddress*(address_str: string): IpAddress =
+proc parseIpAddress*(addressStr: string): IpAddress =
   ## Parses an IP address
   ## Raises EInvalidValue on error
-  if address_str == nil:
-    raise newException(ValueError, "IP Address string is nil")
-  if address_str.contains(':'):
-    return parseIPv6Address(address_str)
+  if addressStr.len == 0:
+    raise newException(ValueError, "IP Address string is empty")
+  if addressStr.contains(':'):
+    return parseIPv6Address(addressStr)
   else:
-    return parseIPv4Address(address_str)
+    return parseIPv4Address(addressStr)
 
-proc isIpAddress*(address_str: string): bool {.tags: [].} =
+proc isIpAddress*(addressStr: string): bool {.tags: [].} =
   ## Checks if a string is an IP address
   ## Returns true if it is, false otherwise
   try:
-    discard parseIpAddress(address_str)
+    discard parseIpAddress(addressStr)
   except ValueError:
     return false
   return true
@@ -587,7 +587,7 @@ when defineSsl:
   proc pskClientCallback(ssl: SslPtr; hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr cuchar;
     max_psk_len: cuint): cuint {.cdecl.} =
     let ctx = SSLContext(context: ssl.SSL_get_SSL_CTX)
-    let hintString = if hint == nil: nil else: $hint
+    let hintString = if hint == nil: "" else: $hint
     let (identityString, pskString) = (ctx.clientGetPskFunc)(hintString)
     if psk.len.cuint > max_psk_len:
       return 0
@@ -657,7 +657,7 @@ when defineSsl:
 
   proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket,
                             handshake: SslHandshakeType,
-                            hostname: string = nil) =
+                            hostname: string = "") =
     ## Wraps a connected socket in an SSL context. This function effectively
     ## turns ``socket`` into an SSL socket.
     ## ``hostname`` should be specified so that the client knows which hostname
@@ -671,7 +671,7 @@ when defineSsl:
     wrapSocket(ctx, socket)
     case handshake
     of handshakeAsClient:
-      if not hostname.isNil and not isIpAddress(hostname):
+      if hostname.len > 0 and not isIpAddress(hostname):
         # Discard result in case OpenSSL version doesn't support SNI, or we're
         # not using TLSv1+
         discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
diff --git a/lib/pure/options.nim b/lib/pure/options.nim
index ce58943f9..12e38d8b5 100644
--- a/lib/pure/options.nim
+++ b/lib/pure/options.nim
@@ -129,7 +129,7 @@ proc get*[T](self: Option[T]): T =
   ## Returns contents of the Option. If it is none, then an exception is
   ## thrown.
   if self.isNone:
-    raise UnpackError(msg : "Can't obtain a value from a `none`")
+    raise UnpackError(msg: "Can't obtain a value from a `none`")
   self.val
 
 proc get*[T](self: Option[T], otherwise: T): T =
@@ -139,6 +139,13 @@ proc get*[T](self: Option[T], otherwise: T): T =
   else:
     otherwise
 
+proc get*[T](self: var Option[T]): var T =
+  ## Returns contents of the Option. If it is none, then an exception is
+  ## thrown.
+  if self.isNone:
+    raise UnpackError(msg: "Can't obtain a value from a `none`")
+  return self.val
+
 proc map*[T](self: Option[T], callback: proc (input: T)) =
   ## Applies a callback to the value in this Option
   if self.isSome:
@@ -187,9 +194,11 @@ proc `$`*[T](self: Option[T]): string =
   ## If the option does not have a value, the result will be `None[T]` where `T` is the name of
   ## the type contained in the option.
   if self.isSome:
-    "Some(" & $self.val & ")"
+    result = "Some("
+    result.addQuoted self.val
+    result.add ")"
   else:
-    "None[" & name(T) & "]"
+    result = "None[" & name(T) & "]"
 
 when isMainModule:
   import unittest, sequtils
@@ -240,7 +249,7 @@ when isMainModule:
       check(stringNone.get("Correct") == "Correct")
 
     test "$":
-      check($(some("Correct")) == "Some(Correct)")
+      check($(some("Correct")) == "Some(\"Correct\")")
       check($(stringNone) == "None[string]")
 
     test "map with a void result":
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 9cc83c372..8fbc20bb5 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -615,7 +615,10 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
 
 when not declared(ENOENT) and not defined(Windows):
   when NoFakeVars:
-    const ENOENT = cint(2) # 2 on most systems including Solaris
+    when not defined(haiku):
+      const ENOENT = cint(2) # 2 on most systems including Solaris
+    else:
+      const ENOENT = cint(-2147459069)
   else:
     var ENOENT {.importc, header: "<errno.h>".}: cint
 
@@ -972,6 +975,14 @@ proc rawCreateDir(dir: string): bool =
       result = false
     else:
       raiseOSError(osLastError(), dir)
+  elif defined(haiku):
+    let res = mkdir(dir, 0o777)
+    if res == 0'i32:
+      result = true
+    elif errno == EEXIST or errno == EROFS:
+      result = false
+    else:
+      raiseOSError(osLastError(), dir)
   elif defined(posix):
     let res = mkdir(dir, 0o777)
     if res == 0'i32:
@@ -1352,9 +1363,15 @@ elif defined(nintendoswitch):
   proc paramCount*(): int {.tags: [ReadIOEffect].} =
     raise newException(OSError, "paramCount is not implemented on Nintendo Switch")
 
+elif defined(genode):
+  proc paramStr*(i: int): TaintedString =
+    raise newException(OSError, "paramStr is not implemented on Genode")
+
+  proc paramCount*(): int =
+    raise newException(OSError, "paramCount is not implemented on Genode")
+
 elif not defined(createNimRtl) and
-  not(defined(posix) and appType == "lib") and
-  not defined(genode):
+  not(defined(posix) and appType == "lib"):
   # On Posix, there is no portable way to get the command line from a DLL.
   var
     cmdCount {.importc: "cmdCount".}: cint
diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim
index a7ebd9d15..0414eae5d 100644
--- a/lib/pure/ospaths.nim
+++ b/lib/pure/ospaths.nim
@@ -525,14 +525,14 @@ proc getConfigDir*(): string {.rtl, extern: "nos$1",
   ## Returns the config directory of the current user for applications.
   ##
   ## On non-Windows OSs, this proc conforms to the XDG Base Directory
-  ## spec. Thus, this proc returns the value of the XDG_CONFIG_DIR environment
+  ## spec. Thus, this proc returns the value of the XDG_CONFIG_HOME environment
   ## variable if it is set, and returns the default configuration directory,
   ## "~/.config/", otherwise.
   ##
   ## An OS-dependent trailing slash is always present at the end of the
   ## returned string; `\\` on Windows and `/` on all other OSs.
   when defined(windows): return string(getEnv("APPDATA")) & "\\"
-  elif getEnv("XDG_CONFIG_DIR"): return string(getEnv("XDG_CONFIG_DIR")) & "/"
+  elif getEnv("XDG_CONFIG_HOME"): return string(getEnv("XDG_CONFIG_HOME")) & "/"
   else: return string(getEnv("HOME")) & "/.config/"
 
 proc getTempDir*(): string {.rtl, extern: "nos$1",
@@ -553,25 +553,25 @@ proc getTempDir*(): string {.rtl, extern: "nos$1",
 
 proc expandTilde*(path: string): string {.
   tags: [ReadEnvEffect, ReadIOEffect].} =
-  ## Expands a path starting with ``~/`` to a full path.
+  ## Expands ``~`` or a path starting with ``~/`` to a full path, replacing
+  ## ``~`` with ``getHomeDir()`` (otherwise returns ``path`` unmodified).
   ##
-  ## If `path` starts with the tilde character and is followed by `/` or `\\`
-  ## this proc will return the reminder of the path appended to the result of
-  ## the getHomeDir() proc, otherwise the input path will be returned without
-  ## modification.
-  ##
-  ## The behaviour of this proc is the same on the Windows platform despite
-  ## not having this convention. Example:
-  ##
-  ## .. code-block:: nim
-  ##   let configFile = expandTilde("~" / "appname.cfg")
-  ##   echo configFile
-  ##   # --> C:\Users\amber\appname.cfg
-  if len(path) > 1 and path[0] == '~' and (path[1] == '/' or path[1] == '\\'):
+  ## Windows: this is still supported despite Windows platform not having this
+  ## convention; also, both ``~/`` and ``~\`` are handled.
+  runnableExamples:
+    doAssert expandTilde("~" / "appname.cfg") == getHomeDir() / "appname.cfg"
+  if len(path) == 0 or path[0] != '~':
+    result = path
+  elif len(path) == 1:
+    result = getHomeDir()
+  elif (path[1] in {DirSep, AltSep}):
     result = getHomeDir() / path.substr(2)
   else:
+    # TODO: handle `~bob` and `~bob/` which means home of bob
     result = path
 
+# TODO: consider whether quoteShellPosix, quoteShellWindows, quoteShell, quoteShellCommand
+# belong in `strutils` instead; they are not specific to paths
 proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
   ## Quote s, so it can be safely passed to Windows API.
   ## Based on Python's subprocess.list2cmdline
@@ -623,6 +623,18 @@ when defined(windows) or defined(posix) or defined(nintendoswitch):
     else:
       return quoteShellPosix(s)
 
+  proc quoteShellCommand*(args: openArray[string]): string =
+    ## Concatenates and quotes shell arguments `args`
+    runnableExamples:
+      when defined(posix):
+        assert quoteShellCommand(["aaa", "", "c d"]) == "aaa '' 'c d'"
+      when defined(windows):
+        assert quoteShellCommand(["aaa", "", "c d"]) == "aaa \"\" \"c d\""
+    # can't use `map` pending https://github.com/nim-lang/Nim/issues/8303
+    for i in 0..<args.len:
+      if i > 0: result.add " "
+      result.add quoteShell(args[i])
+
 when isMainModule:
   assert quoteShellWindows("aaa") == "aaa"
   assert quoteShellWindows("aaa\"") == "aaa\\\""
diff --git a/lib/pure/parseopt2.nim b/lib/pure/parseopt2.nim
index b54a56c0c..51a70b6d1 100644
--- a/lib/pure/parseopt2.nim
+++ b/lib/pure/parseopt2.nim
@@ -44,10 +44,10 @@ type
 proc initOptParser*(cmdline: seq[string]): OptParser {.rtl.} =
   ## Initalizes option parses with cmdline. cmdline should not contain
   ## argument 0 - program name.
-  ## If cmdline == nil default to current command line arguments.
+  ## If cmdline.len == 0 default to current command line arguments.
   result.remainingShortOptions = ""
   when not defined(createNimRtl):
-    if cmdline == nil:
+    if cmdline.len == 0:
       result.cmd = commandLineParams()
       return
   else:
@@ -60,7 +60,7 @@ proc initOptParser*(cmdline: string): OptParser {.rtl, deprecated.} =
   ## and calls initOptParser(openarray[string])
   ## Do not use.
   if cmdline == "": # backward compatibility
-    return initOptParser(seq[string](nil))
+    return initOptParser(@[])
   else:
     return initOptParser(cmdline.split)
 
diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim
index e0000aad3..fe933fb79 100644
--- a/lib/pure/parsexml.nim
+++ b/lib/pure/parsexml.nim
@@ -95,6 +95,7 @@ type
     kind: XmlEventKind
     err: XmlErrorKind
     state: ParserState
+    cIsEmpty: bool
     filename: string
     options: set[XmlParseOption]
 
@@ -125,7 +126,8 @@ proc open*(my: var XmlParser, input: Stream, filename: string,
   my.kind = xmlError
   my.a = ""
   my.b = ""
-  my.c = nil
+  my.c = ""
+  my.cIsEmpty = true
   my.options = options
 
 proc close*(my: var XmlParser) {.inline.} =
@@ -482,6 +484,7 @@ proc parseTag(my: var XmlParser) =
     my.kind = xmlElementOpen
     my.state = stateAttr
     my.c = my.a # save for later
+    my.cIsEmpty = false
   else:
     my.kind = xmlElementStart
     let slash = my.buf[my.bufpos] == '/'
@@ -490,7 +493,8 @@ proc parseTag(my: var XmlParser) =
     if slash and my.buf[my.bufpos] == '>':
       inc(my.bufpos)
       my.state = stateEmptyElementTag
-      my.c = nil
+      my.c = ""
+      my.cIsEmpty = true
     elif my.buf[my.bufpos] == '>':
       inc(my.bufpos)
     else:
@@ -678,7 +682,7 @@ proc next*(my: var XmlParser) =
   of stateEmptyElementTag:
     my.state = stateNormal
     my.kind = xmlElementEnd
-    if not my.c.isNil:
+    if not my.cIsEmpty:
       my.a = my.c
   of stateError:
     my.kind = xmlError
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index d16527a56..02a2d6900 100644
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -765,7 +765,7 @@ template fillMatches(s, caps, c) =
     if startIdx != -1:
       caps[k] = substr(s, startIdx, endIdx)
     else:
-      caps[k] = nil
+      caps[k] = ""
 
 proc matchLen*(s: string, pattern: Peg, matches: var openArray[string],
                start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
@@ -1854,7 +1854,7 @@ when isMainModule:
   assert match("prefix/start", peg"^start$", 7)
 
   if "foo" =~ peg"{'a'}?.*":
-    assert matches[0] == nil
+    assert matches[0].len == 0
   else: assert false
 
   if "foo" =~ peg"{''}.*":
diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim
index 9b9cdb52a..fb371cdce 100644
--- a/lib/pure/ropes.nim
+++ b/lib/pure/ropes.nim
@@ -37,7 +37,7 @@ type
     length: int
     data: string # != nil if a leaf
 
-proc isConc(r: Rope): bool {.inline.} = return isNil(r.data)
+proc isConc(r: Rope): bool {.inline.} = return r.length > 0
 
 # Note that the left and right pointers are not needed for leafs.
 # Leaves have relatively high memory overhead (~30 bytes on a 32
@@ -50,12 +50,12 @@ proc isConc(r: Rope): bool {.inline.} = return isNil(r.data)
 proc len*(a: Rope): int {.rtl, extern: "nro$1".} =
   ## the rope's length
   if a == nil: result = 0
-  else: result = a.length
+  else: result = abs a.length
 
 proc newRope(): Rope = new(result)
 proc newRope(data: string): Rope =
   new(result)
-  result.length = len(data)
+  result.length = -len(data)
   result.data = data
 
 var
@@ -129,7 +129,7 @@ proc insertInCache(s: string, tree: Rope): Rope =
       result.left = t
       t.right = nil
 
-proc rope*(s: string = nil): Rope {.rtl, extern: "nro$1Str".} =
+proc rope*(s: string = ""): Rope {.rtl, extern: "nro$1Str".} =
   ## Converts a string to a rope.
   if s.len == 0:
     result = nil
@@ -170,17 +170,7 @@ proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} =
     result = a
   else:
     result = newRope()
-    result.length = a.length + b.length
-    when false:
-      # XXX rebalancing would be nice, but is too expensive.
-      result.left = a.left
-      var x = newRope()
-      x.left = a.right
-      x.right = b
-      result.right = x
-    else:
-      result.left = a
-      result.right = b
+    result.length = abs(a.length) + abs(b.length)
 
 proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} =
   ## the concatenation operator for ropes.
@@ -229,7 +219,6 @@ iterator leaves*(r: Rope): string =
         stack.add(it.right)
         it = it.left
         assert(it != nil)
-      assert(it.data != nil)
       yield it.data
 
 iterator items*(r: Rope): char =
@@ -250,54 +239,6 @@ proc `$`*(r: Rope): string  {.rtl, extern: "nroToString".}=
   result = newStringOfCap(r.len)
   for s in leaves(r): add(result, s)
 
-when false:
-  # Format string caching seems reasonable: All leaves can be shared and format
-  # string parsing has to be done only once. A compiled format string is stored
-  # as a rope. A negative length is used for the index into the args array.
-  proc compiledArg(idx: int): Rope =
-    new(result)
-    result.length = -idx
-
-  proc compileFrmt(frmt: string): Rope =
-    var i = 0
-    var length = len(frmt)
-    result = nil
-    var num = 0
-    while i < length:
-      if frmt[i] == '$':
-        inc(i)
-        case frmt[i]
-        of '$':
-          add(result, "$")
-          inc(i)
-        of '#':
-          inc(i)
-          add(result, compiledArg(num+1))
-          inc(num)
-        of '0'..'9':
-          var j = 0
-          while true:
-            j = j * 10 + ord(frmt[i]) - ord('0')
-            inc(i)
-            if frmt[i] notin {'0'..'9'}: break
-          add(s, compiledArg(j))
-        of '{':
-          inc(i)
-          var j = 0
-          while frmt[i] in {'0'..'9'}:
-            j = j * 10 + ord(frmt[i]) - ord('0')
-            inc(i)
-          if frmt[i] == '}': inc(i)
-          else: raise newException(EInvalidValue, "invalid format string")
-          add(s, compiledArg(j))
-        else: raise newException(EInvalidValue, "invalid format string")
-      var start = i
-      while i < length:
-        if frmt[i] != '$': inc(i)
-        else: break
-      if i - 1 >= start:
-        add(result, substr(frmt, start, i-1))
-
 proc `%`*(frmt: string, args: openArray[Rope]): Rope {.
   rtl, extern: "nroFormat".} =
   ## `%` substitution operator for ropes. Does not support the ``$identifier``
diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim
index c2c674b84..d9b863a52 100644
--- a/lib/pure/smtp.nim
+++ b/lib/pure/smtp.nim
@@ -119,8 +119,7 @@ proc newSmtp*(useSsl = false, debug=false,
     when compiledWithSsl:
       sslContext.wrapSocket(result.sock)
     else:
-      raise newException(SystemError,
-                         "SMTP module compiled without SSL support")
+      {.error: "SMTP module compiled without SSL support".}
 
 proc newAsyncSmtp*(useSsl = false, debug=false,
                    sslContext = defaultSslContext): AsyncSmtp =
@@ -133,8 +132,7 @@ proc newAsyncSmtp*(useSsl = false, debug=false,
     when compiledWithSsl:
       sslContext.wrapSocket(result.sock)
     else:
-      raise newException(SystemError,
-                         "SMTP module compiled without SSL support")
+      {.error: "SMTP module compiled without SSL support".}
 
 proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] =
   var retFuture = newFuture[void]()
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index 1ab73faea..09626136f 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -377,7 +377,10 @@ when not defined(js):
 
   proc ssClose(s: Stream) =
     var s = StringStream(s)
-    s.data = nil
+    when defined(nimNoNilSeqs):
+      s.data = ""
+    else:
+      s.data = nil
 
   proc newStringStream*(s: string = ""): StringStream =
     ## creates a new stream from the string `s`.
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index f8c5f9a91..33f153587 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -358,9 +358,6 @@ proc isNilOrEmpty*(s: string): bool {.noSideEffect, procvar, rtl,
 
 proc isNilOrWhitespace*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nsuIsNilOrWhitespace".} =
   ## Checks if `s` is nil or consists entirely of whitespace characters.
-  if len(s) == 0:
-    return true
-
   result = true
   for c in s:
     if not c.isSpaceAscii():
@@ -624,12 +621,13 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
   ## Substrings are separated from the right by the string `sep`
   rsplitCommon(s, sep, maxsplit, sep.len)
 
-iterator splitLines*(s: string): string =
+iterator splitLines*(s: string, keepEol = false): string =
   ## Splits the string `s` into its containing lines.
   ##
   ## Every `character literal <manual.html#character-literals>`_ newline
   ## combination (CR, LF, CR-LF) is supported. The result strings contain no
-  ## trailing ``\n``.
+  ## trailing end of line characters unless parameter ``keepEol`` is set to
+  ## ``true``.
   ##
   ## Example:
   ##
@@ -649,22 +647,30 @@ iterator splitLines*(s: string): string =
   ##   ""
   var first = 0
   var last = 0
+  var eolpos = 0
   while true:
     while last < s.len and s[last] notin {'\c', '\l'}: inc(last)
-    yield substr(s, first, last-1)
-    # skip newlines:
-    if last >= s.len: break
-    if s[last] == '\l': inc(last)
-    elif s[last] == '\c':
-      inc(last)
-      if last < s.len and s[last] == '\l': inc(last)
+
+    eolpos = last
+    if last < s.len:
+      if s[last] == '\l': inc(last)
+      elif s[last] == '\c':
+        inc(last)
+        if last < s.len and s[last] == '\l': inc(last)
+
+    yield substr(s, first, if keepEol: last-1 else: eolpos-1)
+
+    # no eol characters consumed means that the string is over
+    if eolpos == last:
+      break
+
     first = last
 
-proc splitLines*(s: string): seq[string] {.noSideEffect,
+proc splitLines*(s: string, keepEol = false): seq[string] {.noSideEffect,
   rtl, extern: "nsuSplitLines".} =
   ## The same as the `splitLines <#splitLines.i,string>`_ iterator, but is a
   ## proc that returns a sequence of substrings.
-  accumulateResult(splitLines(s))
+  accumulateResult(splitLines(s, keepEol=keepEol))
 
 proc countLines*(s: string): int {.noSideEffect,
   rtl, extern: "nsuCountLines".} =
@@ -908,7 +914,7 @@ proc parseOctInt*(s: string): int {.noSideEffect,
   ## `s` are ignored.
   let L = parseutils.parseOct(s, result, 0)
   if L != s.len or L == 0:
-    raise newException(ValueError, "invalid oct integer: " & s)  
+    raise newException(ValueError, "invalid oct integer: " & s)
 
 proc parseHexInt*(s: string): int {.noSideEffect, procvar,
   rtl, extern: "nsuParseHexInt".} =
@@ -1369,9 +1375,11 @@ proc find*(s: string, sub: char, start: Natural = 0, last: Natural = 0): int {.n
       if sub == s[i]: return i
   else:
     when hasCStringBuiltin:
-      let found = c_memchr(s[start].unsafeAddr, sub, last-start+1)
-      if not found.isNil:
-        return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring)
+      let L = last-start+1
+      if L > 0:
+        let found = c_memchr(s[start].unsafeAddr, sub, L)
+        if not found.isNil:
+          return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring)
     else:
       for i in start..last:
         if sub == s[i]: return i
@@ -1518,7 +1526,7 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect,
   elif subLen == 1:
     # when the pattern is a single char, we use a faster
     # char-based search that doesn't need a skip table:
-    var c = sub[0]
+    let c = sub[0]
     let last = s.high
     var i = 0
     while true:
diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim
index 258b40191..8ded552d9 100644
--- a/lib/pure/sugar.nim
+++ b/lib/pure/sugar.nim
@@ -198,3 +198,41 @@ macro dump*(x: typed): untyped =
   let r = quote do:
     debugEcho `s`, " = ", `x`
   return r
+
+# TODO: consider exporting this in macros.nim
+proc freshIdentNodes(ast: NimNode): NimNode =
+  # Replace NimIdent and NimSym by a fresh ident node
+  # see also https://github.com/nim-lang/Nim/pull/8531#issuecomment-410436458
+  proc inspect(node: NimNode): NimNode =
+    case node.kind:
+    of nnkIdent, nnkSym:
+      result = ident($node)
+    of nnkEmpty, nnkLiterals:
+      result = node
+    else:
+      result = node.kind.newTree()
+      for child in node:
+        result.add inspect(child)
+  result = inspect(ast)
+
+macro distinctBase*(T: typedesc): untyped =
+  ## reverses ``type T = distinct A``; works recursively.
+  runnableExamples:
+    type T = distinct int
+    doAssert distinctBase(T) is int
+    doAssert: not compiles(distinctBase(int))
+    type T2 = distinct T
+    doAssert distinctBase(T2) is int
+
+  let typeNode = getTypeImpl(T)
+  expectKind(typeNode, nnkBracketExpr)
+  if typeNode[0].typeKind != ntyTypeDesc:
+    error "expected typeDesc, got " & $typeNode[0]
+  var typeSym = typeNode[1]
+  typeSym = getTypeImpl(typeSym)
+  if typeSym.typeKind != ntyDistinct:
+    error "type is not distinct"
+  typeSym = typeSym[0]
+  while typeSym.typeKind == ntyDistinct:
+    typeSym = getTypeImpl(typeSym)[0]
+  typeSym.freshIdentNodes
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index cdb7a4466..cbf3e6413 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -276,20 +276,22 @@ type
   FixedTimeUnit* = range[Nanoseconds..Weeks] ## Subrange of ``TimeUnit`` that only includes units of fixed duration.
                                              ## These are the units that can be represented by a ``Duration``.
 
-  Timezone* = object ## Timezone interface for supporting ``DateTime``'s of arbritary timezones.
-                     ## The ``times`` module only supplies implementations for the systems local time and UTC.
-                     ## The members ``zoneInfoFromUtc`` and ``zoneInfoFromTz`` should not be accessed directly
-                     ## and are only exported so that ``Timezone`` can be implemented by other modules.
-    zoneInfoFromUtc*: proc (time: Time): ZonedTime {.tags: [], raises: [], benign.}
-    zoneInfoFromTz*:  proc (adjTime: Time): ZonedTime {.tags: [], raises: [], benign.}
-    name*: string ## The name of the timezone, f.ex 'Europe/Stockholm' or 'Etc/UTC'. Used for checking equality.
-                  ## Se also: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
-
-  ZonedTime* = object ## Represents a zoned instant in time that is not associated with any calendar.
-                      ## This type is only used for implementing timezones.
-    adjTime*: Time  ## Time adjusted to a timezone.
-    utcOffset*: int ## Offset from UTC in seconds.
-                    ## The point in time represented by ``ZonedTime`` is ``adjTime + utcOffset.seconds``.
+  Timezone* = ref object ## \
+      ## Timezone interface for supporting ``DateTime``'s of arbritary
+      ## timezones. The ``times`` module only supplies implementations for the
+      ## systems local time and UTC.
+    zonedTimeFromTimeImpl: proc (x: Time): ZonedTime
+        {.tags: [], raises: [], benign.}
+    zonedTimeFromAdjTimeImpl: proc (x: Time): ZonedTime
+        {.tags: [], raises: [], benign.}
+    name: string
+
+  ZonedTime* = object ## Represents a point in time with an associated
+                      ## UTC offset and DST flag. This type is only used for
+                      ## implementing timezones.
+    time*: Time     ## The point in time being represented.
+    utcOffset*: int ## The offset in seconds west of UTC,
+                    ## including any offset due to DST.
     isDst*: bool    ## Determines whether DST is in effect.
 
   DurationParts* = array[FixedTimeUnit, int64] # Array of Duration parts starts
@@ -343,10 +345,9 @@ proc normalize[T: Duration|Time](seconds, nanoseconds: int64): T =
   result.nanosecond = nanosecond.int
 
 # Forward declarations
-proc utcZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .}
-proc utcZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .}
-proc localZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .}
-proc localZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .}
+proc utcTzInfo(time: Time): ZonedTime {.tags: [], raises: [], benign .}
+proc localZonedTimeFromTime(time: Time): ZonedTime {.tags: [], raises: [], benign .}
+proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .}
 proc initTime*(unix: int64, nanosecond: NanosecondRange): Time
   {.tags: [], raises: [], benign noSideEffect.}
 
@@ -493,7 +494,7 @@ proc fromEpochDay(epochday: int64): tuple[monthday: MonthdayRange, month: Month,
 
 proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int): YeardayRange {.tags: [], raises: [], benign .} =
   ## Returns the day of the year.
-  ## Equivalent with ``initDateTime(day, month, year).yearday``.
+  ## Equivalent with ``initDateTime(monthday, month, year, 0, 0, 0).yearday``.
   assertValidDate monthday, month, year
   const daysUntilMonth:     array[Month, int] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
   const daysUntilMonthLeap: array[Month, int] = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
@@ -505,11 +506,11 @@ proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int): YeardayRan
 
 proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay {.tags: [], raises: [], benign .} =
   ## Returns the day of the week enum from day, month and year.
-  ## Equivalent with ``initDateTime(day, month, year).weekday``.
+  ## Equivalent with ``initDateTime(monthday, month, year, 0, 0, 0).weekday``.
   assertValidDate monthday, month, year
   # 1970-01-01 is a Thursday, we adjust to the previous Monday
   let days = toEpochday(monthday, month, year) - 3
-  let weeks = (if days >= 0: days else: days - 6) div 7
+  let weeks = floorDiv(days, 7)
   let wd = days - weeks * 7
   # The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc.
   # so we must correct for the WeekDay type.
@@ -759,15 +760,14 @@ proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} =
   seconds.inc dt.hour * secondsInHour
   seconds.inc dt.minute * 60
   seconds.inc dt.second
-  # The code above ignores the UTC offset of `timeInfo`,
-  # so we need to compensate for that here.
   seconds.inc dt.utcOffset
   result = initTime(seconds, dt.nanosecond)
 
 proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime =
   ## Create a new ``DateTime`` using ``ZonedTime`` in the specified timezone.
-  let s = zt.adjTime.seconds
-  let epochday = (if s >= 0: s else: s - (secondsInDay - 1)) div secondsInDay
+  let adjTime = zt.time - initDuration(seconds = zt.utcOffset)
+  let s = adjTime.seconds
+  let epochday = floorDiv(s, secondsInDay)
   var rem = s - epochday * secondsInDay
   let hour = rem div secondsInHour
   rem = rem - hour * secondsInHour
@@ -784,7 +784,7 @@ proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime =
     hour: hour,
     minute: minute,
     second: second,
-    nanosecond: zt.adjTime.nanosecond,
+    nanosecond: zt.time.nanosecond,
     weekday: getDayOfWeek(d, m, y),
     yearday: getDayOfYear(d, m, y),
     isDst: zt.isDst,
@@ -792,14 +792,55 @@ proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime =
     utcOffset: zt.utcOffset
   )
 
-proc inZone*(time: Time, zone: Timezone): DateTime {.tags: [], raises: [], benign.} =
-  ## Break down ``time`` into a ``DateTime`` using ``zone`` as the timezone.
-  let zoneInfo = zone.zoneInfoFromUtc(time)
-  result = initDateTime(zoneInfo, zone)
+proc newTimezone*(
+      name: string,
+      zonedTimeFromTimeImpl: proc (time: Time): ZonedTime {.tags: [], raises: [], benign.},
+      zonedTimeFromAdjTimeImpl:  proc (adjTime: Time): ZonedTime {.tags: [], raises: [], benign.}
+    ): Timezone =
+  ## Create a new ``Timezone``.
+  ##
+  ## ``zonedTimeFromTimeImpl`` and ``zonedTimeFromAdjTimeImpl`` is used
+  ## as the underlying implementations for ``zonedTimeFromTime`` and
+  ## ``zonedTimeFromAdjTime``.
+  ##
+  ## If possible, the name parameter should match the name used in the
+  ## tz database. If the timezone doesn't exist in the tz database, or if the
+  ## timezone name is unknown, then any string that describes the timezone
+  ## unambiguously can be used. Note that the timezones name is used for
+  ## checking equality!
+  runnableExamples:
+    proc utcTzInfo(time: Time): ZonedTime =
+      ZonedTime(utcOffset: 0, isDst: false, time: time)
+    let utc = newTimezone("Etc/UTC", utcTzInfo, utcTzInfo)
+  Timezone(
+    name: name,
+    zonedTimeFromTimeImpl: zonedTimeFromTimeImpl,
+    zonedTimeFromAdjTimeImpl: zonedTimeFromAdjTimeImpl
+  )
 
-proc inZone*(dt: DateTime, zone: Timezone): DateTime  {.tags: [], raises: [], benign.} =
-  ## Convert ``dt`` into a ``DateTime`` using ``zone`` as the timezone.
-  dt.toTime.inZone(zone)
+proc name*(zone: Timezone): string =
+  ## The name of the timezone.
+  ##
+  ## If possible, the name will be the name used in the tz database.
+  ## If the timezone doesn't exist in the tz database, or if the timezone
+  ## name is unknown, then any string that describes the timezone
+  ## unambiguously might be used. For example, the string "LOCAL" is used
+  ## for the systems local timezone.
+  ##
+  ## See also: https://en.wikipedia.org/wiki/Tz_database
+  zone.name
+
+proc zonedTimeFromTime*(zone: Timezone, time: Time): ZonedTime =
+  ## Returns the ``ZonedTime`` for some point in time.
+  zone.zonedTimeFromTimeImpl(time)
+
+proc zonedTimeFromAdjTime*(zone: TimeZone, adjTime: Time): ZonedTime =
+  ## Returns the ``ZonedTime`` for some local time.
+  ##
+  ## Note that the ``Time`` argument does not represent a point in time, it
+  ## represent a local time! E.g if ``adjTime`` is ``fromUnix(0)``, it should be
+  ## interpreted as 1970-01-01T00:00:00 in the ``zone`` timezone, not in UTC.
+  zone.zonedTimeFromAdjTimeImpl(adjTime)
 
 proc `$`*(zone: Timezone): string =
   ## Returns the name of the timezone.
@@ -807,8 +848,20 @@ proc `$`*(zone: Timezone): string =
 
 proc `==`*(zone1, zone2: Timezone): bool =
   ## Two ``Timezone``'s are considered equal if their name is equal.
+  runnableExamples:
+    doAssert local() == local()
+    doAssert local() != utc()
   zone1.name == zone2.name
 
+proc inZone*(time: Time, zone: Timezone): DateTime {.tags: [], raises: [], benign.} =
+  ## Convert ``time`` into a ``DateTime`` using ``zone`` as the timezone.
+  result = initDateTime(zone.zonedTimeFromTime(time), zone)
+
+proc inZone*(dt: DateTime, zone: Timezone): DateTime  {.tags: [], raises: [], benign.} =
+  ## Returns a ``DateTime`` representing the same point in time as ``dt`` but
+  ## using ``zone`` as the timezone.
+  dt.toTime.inZone(zone)
+
 proc toAdjTime(dt: DateTime): Time =
   let epochDay = toEpochday(dt.monthday, dt.month, dt.year)
   var seconds = epochDay * secondsInDay
@@ -843,14 +896,14 @@ when defined(JS):
     proc getYear(js: JsDate): int {.tags: [], raises: [], benign, importcpp.}
     proc setFullYear(js: JsDate, year: int): void {.tags: [], raises: [], benign, importcpp.}
 
-    proc localZoneInfoFromUtc(time: Time): ZonedTime =
+    proc localZonedTimeFromTime(time: Time): ZonedTime =
       let jsDate = newDate(time.seconds.float * 1000)
       let offset = jsDate.getTimezoneOffset() * secondsInMin
-      result.adjTime = time - initDuration(seconds = offset)
+      result.time = time
       result.utcOffset = offset
       result.isDst = false
 
-    proc localZoneInfoFromTz(adjTime: Time): ZonedTime =
+    proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime =
       let utcDate = newDate(adjTime.seconds.float * 1000)
       let localDate = newDate(utcDate.getUTCFullYear(), utcDate.getUTCMonth(), utcDate.getUTCDate(),
         utcDate.getUTCHours(), utcDate.getUTCMinutes(), utcDate.getUTCSeconds(), 0)
@@ -861,8 +914,8 @@ when defined(JS):
       if utcDate.getUTCFullYear() in 0 .. 99:
         localDate.setFullYear(utcDate.getUTCFullYear())
 
-      result.adjTime = adjTime
       result.utcOffset = localDate.getTimezoneOffset() * secondsInMin
+      result.time = adjTime + initDuration(seconds = result.utcOffset)
       result.isDst = false
 
 else:
@@ -892,7 +945,7 @@ else:
           weekday {.importc: "tm_wday".},
           yearday {.importc: "tm_yday".},
           isdst {.importc: "tm_isdst".}: cint
-        when defined(linux) and defined(amd64):
+        when defined(linux) and defined(amd64) or defined(haiku):
           gmtoff {.importc: "tm_gmtoff".}: clong
           zone {.importc: "tm_zone".}: cstring
   type
@@ -915,13 +968,13 @@ else:
       return ((unix - tm.toAdjUnix).int, tm.isdst > 0)
     return (0, false)
 
-  proc localZoneInfoFromUtc(time: Time): ZonedTime =
+  proc localZonedTimeFromTime(time: Time): ZonedTime =
     let (offset, dst) = getLocalOffsetAndDst(time.seconds)
-    result.adjTime = time - initDuration(seconds = offset)
+    result.time = time
     result.utcOffset = offset
     result.isDst = dst
 
-  proc localZoneInfoFromTz(adjTime: Time): ZonedTime  =
+  proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime  =
     var adjUnix = adjTime.seconds
     let past = adjUnix - secondsInDay
     let (pastOffset, _) = getLocalOffsetAndDst(past)
@@ -943,31 +996,34 @@ else:
     # as a result of offset changes (normally due to dst)
     let utcUnix = adjTime.seconds + utcOffset
     let (finalOffset, dst) = getLocalOffsetAndDst(utcUnix)
-    result.adjTime = initTime(utcUnix - finalOffset, adjTime.nanosecond)
+    result.time = initTime(utcUnix, adjTime.nanosecond)
     result.utcOffset = finalOffset
     result.isDst = dst
 
-proc utcZoneInfoFromUtc(time: Time): ZonedTime =
-  result.adjTime = time
-  result.utcOffset = 0
-  result.isDst = false
+proc utcTzInfo(time: Time): ZonedTime =
+  ZonedTime(utcOffset: 0, isDst: false, time: time)
 
-proc utcZoneInfoFromTz(adjTime: Time): ZonedTime =
-  utcZoneInfoFromUtc(adjTime) # adjTime == time since we are in UTC
+var utcInstance {.threadvar.}: Timezone
+var localInstance {.threadvar.}: Timezone
 
 proc utc*(): TimeZone =
   ## Get the ``Timezone`` implementation for the UTC timezone.
   runnableExamples:
     doAssert now().utc.timezone == utc()
     doAssert utc().name == "Etc/UTC"
-  Timezone(zoneInfoFromUtc: utcZoneInfoFromUtc, zoneInfoFromTz: utcZoneInfoFromTz, name: "Etc/UTC")
+  if utcInstance.isNil:
+    utcInstance = newTimezone("Etc/UTC", utcTzInfo, utcTzInfo)
+  result = utcInstance
 
 proc local*(): TimeZone =
   ## Get the ``Timezone`` implementation for the local timezone.
   runnableExamples:
    doAssert now().timezone == local()
    doAssert local().name == "LOCAL"
-  Timezone(zoneInfoFromUtc: localZoneInfoFromUtc, zoneInfoFromTz: localZoneInfoFromTz, name: "LOCAL")
+  if localInstance.isNil:
+    localInstance = newTimezone("LOCAL", localZonedTimeFromTime,
+      localZonedTimeFromAdjTime)
+  result = localInstance
 
 proc utc*(dt: DateTime): DateTime =
   ## Shorthand for ``dt.inZone(utc())``.
@@ -1233,7 +1289,7 @@ proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
     second:  second,
     nanosecond: nanosecond
   )
-  result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone)
+  result = initDateTime(zone.zonedTimeFromAdjTime(dt.toAdjTime), zone)
 
 proc initDateTime*(monthday: MonthdayRange, month: Month, year: int,
                    hour: HourRange, minute: MinuteRange, second: SecondRange,
@@ -1263,16 +1319,15 @@ proc `+`*(dt: DateTime, interval: TimeInterval): DateTime =
   let (adjDur, absDur) = evaluateInterval(dt, interval)
 
   if adjDur != DurationZero:
-    var zInfo = dt.timezone.zoneInfoFromTz(dt.toAdjTime + adjDur)
+    var zt = dt.timezone.zonedTimeFromAdjTime(dt.toAdjTime + adjDur)
     if absDur != DurationZero:
-      let offsetDur = initDuration(seconds = zInfo.utcOffset)
-      zInfo = dt.timezone.zoneInfoFromUtc(zInfo.adjTime + offsetDur + absDur)
-      result = initDateTime(zInfo, dt.timezone)
+      zt = dt.timezone.zonedTimeFromTime(zt.time + absDur)
+      result = initDateTime(zt, dt.timezone)
     else:
-      result = initDateTime(zInfo, dt.timezone)
+      result = initDateTime(zt, dt.timezone)
   else:
-    var zInfo = dt.timezone.zoneInfoFromUtc(dt.toTime + absDur)
-    result = initDateTime(zInfo, dt.timezone)
+    var zt = dt.timezone.zonedTimeFromTime(dt.toTime + absDur)
+    result = initDateTime(zt, dt.timezone)
 
 proc `-`*(dt: DateTime, interval: TimeInterval): DateTime =
   ## Subtract ``interval`` from ``dt``. Components from ``interval`` are subtracted
@@ -1319,7 +1374,7 @@ proc `<=` * (a, b: DateTime): bool =
   return a.toTime <= b.toTime
 
 proc `==`*(a, b: DateTime): bool =
-  ## Returns true if ``a == b``, that is if both dates represent the same point in datetime.
+  ## Returns true if ``a == b``, that is if both dates represent the same point in time.
   return a.toTime == b.toTime
 
 
@@ -2065,7 +2120,7 @@ proc toDateTime(p: ParsedTime, zone: Timezone, f: TimeFormat,
 
   if p.utcOffset.isNone:
     # No timezone parsed - assume timezone is `zone`
-    result = initDateTime(zone.zoneInfoFromTz(result.toAdjTime), zone)
+    result = initDateTime(zone.zonedTimeFromAdjTime(result.toAdjTime), zone)
   else:
     # Otherwise convert to `zone`
     result.utcOffset = p.utcOffset.get()
@@ -2347,7 +2402,7 @@ proc fromSeconds*(since1970: int64): Time {.tags: [], raises: [], benign, deprec
 proc toSeconds*(time: Time): float {.tags: [], raises: [], benign, deprecated.} =
   ## Returns the time in seconds since the unix epoch.
   ##
-  ## **Deprecated since v0.18.0:** use ``fromUnix`` instead
+  ## **Deprecated since v0.18.0:** use ``toUnix`` instead
   time.seconds.float + time.nanosecond / convert(Seconds, Nanoseconds, 1)
 
 proc getLocalTime*(time: Time): DateTime {.tags: [], raises: [], benign, deprecated.} =
@@ -2390,13 +2445,14 @@ proc timeInfoToTime*(dt: DateTime): Time {.tags: [], benign, deprecated.} =
 when defined(JS):
   var start = getTime()
   proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} =
-    ## get the milliseconds from the start of the program.
-    ## **Deprecated since v0.8.10:** use ``epochTime`` or ``cpuTime`` instead.
     let dur = getTime() - start
     result = (convert(Seconds, Milliseconds, dur.seconds) +
       convert(Nanoseconds, Milliseconds, dur.nanosecond)).int
 else:
   proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.} =
+    ## get the milliseconds from the start of the program.
+    ##
+    ## **Deprecated since v0.8.10:** use ``epochTime`` or ``cpuTime`` instead.
     when defined(macosx):
       result = toInt(toFloat(int(getClock())) / (toFloat(clocksPerSec) / 1000.0))
     else:
@@ -2417,7 +2473,7 @@ proc getDayOfWeek*(day, month, year: int): WeekDay  {.tags: [], raises: [], beni
 proc getDayOfWeekJulian*(day, month, year: int): WeekDay {.deprecated.} =
   ## Returns the day of the week enum from day, month and year,
   ## according to the Julian calendar.
-  ## **Deprecated since v0.18.0:**
+  ## **Deprecated since v0.18.0**
   # Day & month start from one.
   let
     a = (14 - month) div 12
@@ -2425,3 +2481,23 @@ proc getDayOfWeekJulian*(day, month, year: int): WeekDay {.deprecated.} =
     m = month + (12*a) - 2
     d = (5 + day + y + (y div 4) + (31*m) div 12) mod 7
   result = d.WeekDay
+
+proc adjTime*(zt: ZonedTime): Time
+    {.deprecated: "Use zt.time instead".} =
+  ## **Deprecated since v0.19.0:** use the ``time`` field instead.
+  zt.time - initDuration(seconds = zt.utcOffset)
+
+proc `adjTime=`*(zt: var ZonedTime, adjTime: Time)
+    {.deprecated: "Use zt.time instead".} =
+  ## **Deprecated since v0.19.0:** use the ``time`` field instead.
+  zt.time = adjTime + initDuration(seconds = zt.utcOffset)
+
+proc zoneInfoFromUtc*(zone: Timezone, time: Time): ZonedTime
+    {.deprecated: "Use zonedTimeFromTime instead".} =
+  ## **Deprecated since v0.19.0:** use ``zonedTimeFromTime`` instead.
+  zone.zonedTimeFromTime(time)
+
+proc zoneInfoFromTz*(zone: Timezone, adjTime: Time): ZonedTime
+    {.deprecated: "Use zonedTimeFromAdjTime instead".} =
+  ## **Deprecated since v0.19.0:** use the ``zonedTimeFromAdjTime`` instead.
+  zone.zonedTimeFromAdjTime(adjTime)
\ No newline at end of file
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index d804ba7c8..757bf4745 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -176,10 +176,7 @@ method suiteEnded*(formatter: OutputFormatter) {.base, gcsafe.} =
   discard
 
 proc addOutputFormatter*(formatter: OutputFormatter) =
-  if formatters == nil:
-    formatters = @[formatter]
-  else:
-    formatters.add(formatter)
+  formatters.add(formatter)
 
 proc newConsoleOutputFormatter*(outputLevel: OutputLevel = PRINT_ALL,
                                 colorOutput = true): ConsoleOutputFormatter =
@@ -225,7 +222,7 @@ method testStarted*(formatter: ConsoleOutputFormatter, testName: string) =
   formatter.isInTest = true
 
 method failureOccurred*(formatter: ConsoleOutputFormatter, checkpoints: seq[string], stackTrace: string) =
-  if stackTrace != nil:
+  if stackTrace.len > 0:
     echo stackTrace
   let prefix = if formatter.isInSuite: "    " else: ""
   for msg in items(checkpoints):
@@ -236,7 +233,7 @@ method testEnded*(formatter: ConsoleOutputFormatter, testResult: TestResult) =
 
   if formatter.outputLevel != PRINT_NONE and
      (formatter.outputLevel == PRINT_ALL or testResult.status == FAILED):
-    let prefix = if testResult.suiteName != nil: "  " else: ""
+    let prefix = if testResult.suiteName.len > 0: "  " else: ""
     template rawPrint() = echo(prefix, "[", $testResult.status, "] ", testResult.testName)
     when not defined(ECMAScript):
       if formatter.colorOutput and not defined(ECMAScript):
@@ -301,7 +298,7 @@ method failureOccurred*(formatter: JUnitOutputFormatter, checkpoints: seq[string
   ## ``stackTrace`` is provided only if the failure occurred due to an exception.
   ## ``checkpoints`` is never ``nil``.
   formatter.testErrors.add(checkpoints)
-  if stackTrace != nil:
+  if stackTrace.len > 0:
     formatter.testStackTrace = stackTrace
 
 method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) =
@@ -392,7 +389,7 @@ proc shouldRun(currentSuiteName, testName: string): bool =
   return false
 
 proc ensureInitialized() =
-  if formatters == nil:
+  if formatters.len == 0:
     formatters = @[OutputFormatter(defaultConsoleFormatter())]
 
   if not disabledParamFiltering and not testsFilters.isValid:
@@ -507,7 +504,7 @@ template test*(name, body) {.dirty.} =
       if testStatusIMPL == FAILED:
         programResult += 1
       let testResult = TestResult(
-        suiteName: when declared(testSuiteName): testSuiteName else: nil,
+        suiteName: when declared(testSuiteName): testSuiteName else: "",
         testName: name,
         status: testStatusIMPL
       )
@@ -525,8 +522,6 @@ proc checkpoint*(msg: string) =
   ##  checkpoint("Checkpoint B")
   ##
   ## outputs "Checkpoint A" once it fails.
-  if checkpoints == nil:
-    checkpoints = @[]
   checkpoints.add(msg)
   # TODO: add support for something like SCOPED_TRACE from Google Test
 
@@ -557,7 +552,7 @@ template fail* =
     when declared(stackTrace):
       formatter.failureOccurred(checkpoints, stackTrace)
     else:
-      formatter.failureOccurred(checkpoints, nil)
+      formatter.failureOccurred(checkpoints, "")
 
   when not defined(ECMAScript):
     if abortOnError: quit(programResult)
diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim
index 8cd47aa39..1a9e4ae26 100644
--- a/lib/pure/xmldom.nim
+++ b/lib/pure/xmldom.nim
@@ -217,9 +217,9 @@ proc createAttribute*(doc: PDocument, name: string): PAttr =
   new(attrNode)
   attrNode.fName = name
   attrNode.fNodeName = name
-  attrNode.fLocalName = nil
-  attrNode.prefix = nil
-  attrNode.fNamespaceURI = nil
+  attrNode.fLocalName = ""
+  attrNode.prefix = ""
+  attrNode.fNamespaceURI = ""
   attrNode.value = ""
   attrNode.fSpecified = false
   return attrNode
@@ -254,7 +254,7 @@ proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: str
     attrNode.prefix = qualifiedName.split(':')[0]
     attrNode.fLocalName = qualifiedName.split(':')[1]
   else:
-    attrNode.prefix = nil
+    attrNode.prefix = ""
     attrNode.fLocalName = qualifiedName
   attrNode.value = ""
 
@@ -298,9 +298,9 @@ proc createElement*(doc: PDocument, tagName: string): PElement =
   new(elNode)
   elNode.fTagName = tagName
   elNode.fNodeName = tagName
-  elNode.fLocalName = nil
-  elNode.prefix = nil
-  elNode.fNamespaceURI = nil
+  elNode.fLocalName = ""
+  elNode.prefix = ""
+  elNode.fNamespaceURI = ""
   elNode.childNodes = @[]
   elNode.attributes = @[]
 
@@ -332,7 +332,7 @@ proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: strin
     elNode.prefix = qualifiedName.split(':')[0]
     elNode.fLocalName = qualifiedName.split(':')[1]
   else:
-    elNode.prefix = nil
+    elNode.prefix = ""
     elNode.fLocalName = qualifiedName
   elNode.fNamespaceURI = namespaceURI
   elNode.childNodes = @[]
@@ -893,22 +893,22 @@ proc tagName*(el: PElement): string =
 proc getAttribute*(el: PNode, name: string): string =
   ## Retrieves an attribute value by ``name``
   if isNil(el.attributes):
-    return nil
+    return ""
   var attribute = el.attributes.getNamedItem(name)
   if not isNil(attribute):
     return attribute.value
   else:
-    return nil
+    return ""
 
 proc getAttributeNS*(el: PNode, namespaceURI: string, localName: string): string =
   ## Retrieves an attribute value by ``localName`` and ``namespaceURI``
   if isNil(el.attributes):
-    return nil
+    return ""
   var attribute = el.attributes.getNamedItemNS(namespaceURI, localName)
   if not isNil(attribute):
     return attribute.value
   else:
-    return nil
+    return ""
 
 proc getAttributeNode*(el: PElement, name: string): PAttr =
   ## Retrieves an attribute node by ``name``
diff --git a/lib/pure/xmldomparser.nim b/lib/pure/xmldomparser.nim
index 7c7f7b99c..8d995102e 100644
--- a/lib/pure/xmldomparser.nim
+++ b/lib/pure/xmldomparser.nim
@@ -119,7 +119,7 @@ proc loadXMLStream*(stream: Stream): PDocument =
   ## a ``PDocument``
 
   var x: XmlParser
-  open(x, stream, nil, {reportComments})
+  open(x, stream, "", {reportComments})
 
   var xmlDoc: PDocument
   var dom: PDOMImplementation = getDOM()
@@ -161,7 +161,7 @@ when not defined(testing) and isMainModule:
   #echo(xml.getElementsByTagName("bla:test")[0].namespaceURI)
   #echo(xml.getElementsByTagName("test")[0].namespaceURI)
   for i in items(xml.getElementsByTagName("*")):
-    if i.namespaceURI != nil:
+    if i.namespaceURI.len > 0:
       echo(i.nodeName, "=", i.namespaceURI)
 
 
diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim
index 47658b59b..d536cfed0 100644
--- a/lib/pure/xmltree.nim
+++ b/lib/pure/xmltree.nim
@@ -377,7 +377,6 @@ proc findAll*(n: XmlNode, tag: string, result: var seq[XmlNode]) =
   ##   findAll(html, "img", tags)
   ##   for imgTag in tags:
   ##     process(imgTag)
-  assert isNil(result) == false
   assert n.k == xnElement
   for child in n.items():
     if child.k != xnElement:
diff --git a/lib/system.nim b/lib/system.nim
index c033a632b..52ed524be 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -497,141 +497,103 @@ type
     raise_id: uint # set when exception is raised
     up: ref Exception # used for stacking exceptions. Not exported!
 
-  SystemError* = object of Exception ## \
-    ## Abstract class for exceptions that the runtime system raises.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  IOError* = object of SystemError ## \
+  Defect* = object of Exception ## \
+    ## Abstract base class for all exceptions that Nim's runtime raises
+    ## but that are strictly uncatchable as they can also be mapped to
+    ## a ``quit`` / ``trap`` / ``exit`` operation.
+
+  CatchableError* = object of Exception ## \
+    ## Abstract class for all exceptions that are catchable.
+  IOError* = object of CatchableError ## \
     ## Raised if an IO error occurred.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
   EOFError* = object of IOError ## \
     ## Raised if an IO "end of file" error occurred.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  OSError* = object of SystemError ## \
+  OSError* = object of CatchableError ## \
     ## Raised if an operating system service failed.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
     errorCode*: int32 ## OS-defined error code describing this error.
   LibraryError* = object of OSError ## \
     ## Raised if a dynamic library could not be loaded.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  ResourceExhaustedError* = object of SystemError ## \
+  ResourceExhaustedError* = object of CatchableError ## \
     ## Raised if a resource request could not be fulfilled.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  ArithmeticError* = object of Exception ## \
+  ArithmeticError* = object of Defect ## \
     ## Raised if any kind of arithmetic error occurred.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
   DivByZeroError* = object of ArithmeticError ## \
     ## Raised for runtime integer divide-by-zero errors.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
 
   OverflowError* = object of ArithmeticError ## \
     ## Raised for runtime integer overflows.
     ##
     ## This happens for calculations whose results are too large to fit in the
-    ## provided bits.  See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  AccessViolationError* = object of Exception ## \
+    ## provided bits.
+  AccessViolationError* = object of Defect ## \
     ## Raised for invalid memory access errors
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  AssertionError* = object of Exception ## \
+  AssertionError* = object of Defect ## \
     ## Raised when assertion is proved wrong.
     ##
-    ## Usually the result of using the `assert() template <#assert>`_.  See the
-    ## full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  ValueError* = object of Exception ## \
+    ## Usually the result of using the `assert() template <#assert>`_.
+  ValueError* = object of Defect ## \
     ## Raised for string and object conversion errors.
   KeyError* = object of ValueError ## \
     ## Raised if a key cannot be found in a table.
     ##
     ## Mostly used by the `tables <tables.html>`_ module, it can also be raised
     ## by other collection modules like `sets <sets.html>`_ or `strtabs
-    ## <strtabs.html>`_. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  OutOfMemError* = object of SystemError ## \
+    ## <strtabs.html>`_.
+  OutOfMemError* = object of Defect ## \
     ## Raised for unsuccessful attempts to allocate memory.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  IndexError* = object of Exception ## \
+  IndexError* = object of Defect ## \
     ## Raised if an array index is out of bounds.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
 
-  FieldError* = object of Exception ## \
+  FieldError* = object of Defect ## \
     ## Raised if a record field is not accessible because its dicriminant's
     ## value does not fit.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  RangeError* = object of Exception ## \
+  RangeError* = object of Defect ## \
     ## Raised if a range check error occurred.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  StackOverflowError* = object of SystemError ## \
+  StackOverflowError* = object of Defect ## \
     ## Raised if the hardware stack used for subroutine calls overflowed.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  ReraiseError* = object of Exception ## \
+  ReraiseError* = object of Defect ## \
     ## Raised if there is no exception to reraise.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  ObjectAssignmentError* = object of Exception ## \
+  ObjectAssignmentError* = object of Defect ## \
     ## Raised if an object gets assigned to its parent's object.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  ObjectConversionError* = object of Exception ## \
+  ObjectConversionError* = object of Defect ## \
     ## Raised if an object is converted to an incompatible object type.
     ## You can use ``of`` operator to check if conversion will succeed.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  FloatingPointError* = object of Exception ## \
+  FloatingPointError* = object of Defect ## \
     ## Base class for floating point exceptions.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
   FloatInvalidOpError* = object of FloatingPointError ## \
     ## Raised by invalid operations according to IEEE.
     ##
-    ## Raised by ``0.0/0.0``, for example.  See the full `exception
-    ## hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
+    ## Raised by ``0.0/0.0``, for example.
   FloatDivByZeroError* = object of FloatingPointError ## \
     ## Raised by division by zero.
     ##
-    ## Divisor is zero and dividend is a finite nonzero number.  See the full
-    ## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
+    ## Divisor is zero and dividend is a finite nonzero number.
   FloatOverflowError* = object of FloatingPointError ## \
     ## Raised for overflows.
     ##
     ## The operation produced a result that exceeds the range of the exponent.
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
   FloatUnderflowError* = object of FloatingPointError ## \
     ## Raised for underflows.
     ##
     ## The operation produced a result that is too small to be represented as a
-    ## normal number. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
+    ## normal number.
   FloatInexactError* = object of FloatingPointError ## \
     ## Raised for inexact results.
     ##
     ## The operation produced a result that cannot be represented with infinite
     ## precision -- for example: ``2.0 / 3.0, log(1.1)``
     ##
-    ## **NOTE**: Nim currently does not detect these!  See the full
-    ## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  DeadThreadError* = object of Exception ## \
+    ## **NOTE**: Nim currently does not detect these!
+  DeadThreadError* = object of Defect ## \
     ## Raised if it is attempted to send a message to a dead thread.
-    ##
-    ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-  NilAccessError* = object of SystemError ## \
+  NilAccessError* = object of Defect ## \
     ## Raised on dereferences of ``nil`` pointers.
     ##
     ## This is only raised if the ``segfaults.nim`` module was imported!
 
 when defined(nimNewRuntime):
   type
-    MoveError* = object of SystemError ## \
+    MoveError* = object of Defect ## \
       ## Raised on attempts to re-sink an already consumed ``sink`` parameter.
 
 when defined(js) or defined(nimdoc):
@@ -985,6 +947,23 @@ else:
   proc `shl`*(x, y: int32): int32 {.magic: "ShlI", noSideEffect.}
   proc `shl`*(x, y: int64): int64 {.magic: "ShlI", noSideEffect.}
 
+when defined(nimAshr):
+  proc ashr*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.}
+  proc ashr*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.}
+  proc ashr*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.}
+  proc ashr*(x: int32, y: SomeInteger): int32 {.magic: "AshrI", noSideEffect.}
+  proc ashr*(x: int64, y: SomeInteger): int64 {.magic: "AshrI", noSideEffect.}
+    ## Shifts right by pushing copies of the leftmost bit in from the left,
+    ## and let the rightmost bits fall off.
+    ##
+    ## .. code-block:: Nim
+    ##   0b0001_0000'i8 shr 2 == 0b0000_0100'i8
+    ##   0b1000_0000'i8 shr 8 == 0b1111_1111'i8
+    ##   0b1000_0000'i8 shr 1 == 0b1100_0000'i8
+else:
+  # used for bootstrapping the compiler
+  proc ashr*[T](x: T, y: SomeInteger): T = discard
+
 proc `and`*(x, y: int): int {.magic: "BitandI", noSideEffect.}
 proc `and`*(x, y: int8): int8 {.magic: "BitandI", noSideEffect.}
 proc `and`*(x, y: int16): int16 {.magic: "BitandI", noSideEffect.}
@@ -1362,7 +1341,7 @@ const
   hostOS* {.magic: "HostOS".}: string = ""
     ## a string that describes the host operating system. Possible values:
     ## "windows", "macosx", "linux", "netbsd", "freebsd", "openbsd", "solaris",
-    ## "aix", "standalone".
+    ## "aix", "haiku", "standalone".
 
   hostCPU* {.magic: "HostCPU".}: string = ""
     ## a string that describes the host CPU. Possible values:
@@ -1987,7 +1966,7 @@ when sizeof(int) <= 2:
 else:
   type IntLikeForCount = int|int8|int16|int32|char|bool|uint8|uint16|enum
 
-iterator countdown*[T](a, b: T, step = 1): T {.inline.} =
+iterator countdown*[T](a, b: T, step: Positive = 1): T {.inline.} =
   ## Counts from ordinal value `a` down to `b` (inclusive) with the given
   ## step count. `T` may be any ordinal type, `step` may only
   ## be positive. **Note**: This fails to count to ``low(int)`` if T = int for
@@ -2010,7 +1989,7 @@ iterator countdown*[T](a, b: T, step = 1): T {.inline.} =
       dec(res, step)
 
 when defined(nimNewRoof):
-  iterator countup*[T](a, b: T, step = 1): T {.inline.} =
+  iterator countup*[T](a, b: T, step: Positive = 1): T {.inline.} =
     ## Counts from ordinal value `a` up to `b` (inclusive) with the given
     ## step count. `S`, `T` may be any ordinal type, `step` may only
     ## be positive. **Note**: This fails to count to ``high(int)`` if T = int for
@@ -2027,7 +2006,7 @@ when defined(nimNewRoof):
         inc(res, step)
 
   iterator `..`*[T](a, b: T): T {.inline.} =
-    ## An alias for `countup`.
+    ## An alias for `countup(a, b, 1)`.
     when T is IntLikeForCount:
       var res = int(a)
       while res <= int(b):
@@ -2313,9 +2292,9 @@ iterator mpairs*(a: var cstring): tuple[key: int, val: var char] {.inline.} =
     inc(i)
 
 
-proc isNil*[T](x: seq[T]): bool {.noSideEffect, magic: "IsNil".}
+proc isNil*[T](x: seq[T]): bool {.noSideEffect, magic: "IsNil", deprecated.}
 proc isNil*[T](x: ref T): bool {.noSideEffect, magic: "IsNil".}
-proc isNil*(x: string): bool {.noSideEffect, magic: "IsNil".}
+proc isNil*(x: string): bool {.noSideEffect, magic: "IsNil", deprecated.}
 proc isNil*[T](x: ptr T): bool {.noSideEffect, magic: "IsNil".}
 proc isNil*(x: pointer): bool {.noSideEffect, magic: "IsNil".}
 proc isNil*(x: cstring): bool {.noSideEffect, magic: "IsNil".}
@@ -2374,8 +2353,12 @@ proc `==`*[T](x, y: seq[T]): bool {.noSideEffect.} =
   ## Generic equals operator for sequences: relies on a equals operator for
   ## the element type `T`.
   when nimvm:
-    if x.isNil and y.isNil:
-      return true
+    when not defined(nimNoNil):
+      if x.isNil and y.isNil:
+        return true
+    else:
+      if x.len == 0 and y.len == 0:
+        return true
   else:
     when not defined(JS):
       proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} =
@@ -2512,7 +2495,7 @@ proc `$`*[T: tuple|object](x: T): string =
     result.add(name)
     result.add(": ")
     when compiles($value):
-      when compiles(value.isNil):
+      when value isnot string and value isnot seq and compiles(value.isNil):
         if value.isNil: result.add "nil"
         else: result.addQuoted(value)
       else:
@@ -2531,7 +2514,7 @@ proc collectionToString[T](x: T, prefix, separator, suffix: string): string =
     else:
       result.add(separator)
 
-    when compiles(value.isNil):
+    when value isnot string and value isnot seq and compiles(value.isNil):
       # this branch should not be necessary
       if value.isNil:
         result.add "nil"
@@ -2556,10 +2539,7 @@ proc `$`*[T](x: seq[T]): string =
   ##
   ## .. code-block:: nim
   ##   $(@[23, 45]) == "@[23, 45]"
-  if x.isNil:
-    "nil"
-  else:
-    collectionToString(x, "@[", ", ", "]")
+  collectionToString(x, "@[", ", ", "]")
 
 # ----------------- GC interface ---------------------------------------------
 
@@ -4001,19 +3981,28 @@ when hasAlloc:
   proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect, deprecated.} =
     ## Adds ``y`` to ``x`` unless ``x`` is not yet initialized; in that case,
     ## ``x`` becomes ``@[y]``
-    if x == nil: x = @[y]
-    else: x.add(y)
+    when defined(nimNoNilSeqs):
+      x.add(y)
+    else:
+      if x == nil: x = @[y]
+      else: x.add(y)
 
   proc safeAdd*(x: var string, y: char) {.noSideEffect, deprecated.} =
     ## Adds ``y`` to ``x``. If ``x`` is ``nil`` it is initialized to ``""``
-    if x == nil: x = ""
-    x.add(y)
+    when defined(nimNoNilSeqs):
+      x.add(y)
+    else:
+      if x == nil: x = ""
+      x.add(y)
 
   proc safeAdd*(x: var string, y: string) {.noSideEffect, deprecated.} =
     ## Adds ``y`` to ``x`` unless ``x`` is not yet initalized; in that
     ## case, ``x`` becomes ``y``
-    if x == nil: x = y
-    else: x.add(y)
+    when defined(nimNoNilSeqs):
+      x.add(y)
+    else:
+      if x == nil: x = y
+      else: x.add(y)
 
 proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} =
   ## generates a tuple constructor expression listing all the local variables
@@ -4075,6 +4064,11 @@ proc `==`*(x, y: cstring): bool {.magic: "EqCString", noSideEffect,
   elif x.isNil or y.isNil: result = false
   else: result = strcmp(x, y) == 0
 
+when defined(nimNoNilSeqs2):
+  when not compileOption("nilseqs"):
+    proc `==`*(x: string; y: type(nil)): bool {.error.} = discard
+    proc `==`*(x: type(nil); y: string): bool {.error.} = discard
+
 template closureScope*(body: untyped): untyped =
   ## Useful when creating a closure in a loop to capture local loop variables by
   ## their current iteration values. Example:
@@ -4189,8 +4183,13 @@ when defined(cpp) and appType != "lib" and
 
     let ex = getCurrentException()
     let trace = ex.getStackTrace()
-    stderr.write trace & "Error: unhandled exception: " & ex.msg &
-                 " [" & $ex.name & "]\n"
+    when defined(genode):
+      # stderr not available by default, use the LOG session
+      echo trace & "Error: unhandled exception: " & ex.msg &
+                   " [" & $ex.name & "]\n"
+    else:
+      stderr.write trace & "Error: unhandled exception: " & ex.msg &
+                   " [" & $ex.name & "]\n"
     quit 1
 
 when not defined(js):
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index ffb7aaf86..b090117a9 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -454,10 +454,10 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
   a.addHeapLink(result, size)
   when defined(debugHeapLinks):
     cprintf("owner: %p; result: %p; next pointer %p; size: %ld\n", addr(a),
-      result, result.heapLink, result.origSize)
+      result, result.heapLink, result.size)
 
   when defined(memtracker):
-    trackLocation(addr result.origSize, sizeof(int))
+    trackLocation(addr result.size, sizeof(int))
 
   sysAssert((cast[ByteAddress](result) and PageMask) == 0, "requestOsChunks 1")
   #zeroMem(result, size)
@@ -527,7 +527,7 @@ proc updatePrevSize(a: var MemRegion, c: PBigChunk,
 proc splitChunk2(a: var MemRegion, c: PBigChunk, size: int): PBigChunk =
   result = cast[PBigChunk](cast[ByteAddress](c) +% size)
   result.size = c.size - size
-  track("result.origSize", addr result.origSize, sizeof(int))
+  track("result.size", addr result.size, sizeof(int))
   # XXX check if these two nil assignments are dead code given
   # addChunkToMatrix's implementation:
   result.next = nil
@@ -602,7 +602,7 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
       splitChunk(a, result, size)
   # set 'used' to to true:
   result.prevSize = 1
-  track("setUsedToFalse", addr result.origSize, sizeof(int))
+  track("setUsedToFalse", addr result.size, sizeof(int))
 
   incl(a, a.chunkStarts, pageIndex(result))
   dec(a.freeMem, size)
@@ -968,7 +968,7 @@ proc deallocOsPages(a: var MemRegion) =
       let (p, size) = it.chunks[i]
       when defined(debugHeapLinks):
         cprintf("owner %p; dealloc A: %p size: %ld; next: %p\n", addr(a),
-          it, it.origSize, next)
+          it, it.size, next)
       sysAssert size >= PageSize, "origSize too small"
       osDeallocPages(p, size)
     it = next
diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim
index f593d4c99..67e42c0af 100644
--- a/lib/system/ansi_c.nim
+++ b/lib/system/ansi_c.nim
@@ -50,7 +50,7 @@ when defined(windows):
     SIGTERM = cint(15)
 elif defined(macosx) or defined(linux) or defined(freebsd) or
      defined(openbsd) or defined(netbsd) or defined(solaris) or
-     defined(dragonfly) or defined(nintendoswitch):
+     defined(dragonfly) or defined(nintendoswitch) or defined(genode):
   const
     SIGABRT = cint(6)
     SIGFPE = cint(8)
@@ -59,6 +59,15 @@ elif defined(macosx) or defined(linux) or defined(freebsd) or
     SIGSEGV = cint(11)
     SIGTERM = cint(15)
     SIGPIPE = cint(13)
+elif defined(haiku):
+  const
+    SIGABRT = cint(6)
+    SIGFPE = cint(8)
+    SIGILL = cint(4)
+    SIGINT = cint(2)
+    SIGSEGV = cint(11)
+    SIGTERM = cint(15)
+    SIGPIPE = cint(7)
 else:
   when NoFakeVars:
     {.error: "SIGABRT not ported to your platform".}
@@ -74,6 +83,8 @@ else:
 
 when defined(macosx):
   const SIGBUS = cint(10)
+elif defined(haiku):
+  const SIGBUS = cint(30)
 else:
   template SIGBUS: untyped = SIGSEGV
 
diff --git a/lib/system/channels.nim b/lib/system/channels.nim
index 254b87dfc..14d3a3005 100644
--- a/lib/system/channels.nim
+++ b/lib/system/channels.nim
@@ -233,7 +233,7 @@ proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) {.inline.} =
 proc trySend*[TMsg](c: var Channel[TMsg], msg: TMsg): bool {.inline.} =
   ## Tries to send a message to a thread. `msg` is deeply copied. Doesn't block.
   ## Returns `false` if the message was not sent because number of pending items
-  ## in the cannel exceeded `maxItems`.
+  ## in the channel exceeded `maxItems`.
   sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), true)
 
 proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index 1e590965a..7b4979cfa 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -31,7 +31,11 @@ proc showErrorMessage(data: cstring) {.gcsafe.} =
   if errorMessageWriter != nil:
     errorMessageWriter($data)
   else:
-    writeToStdErr(data)
+    when defined(genode):
+      # stderr not available by default, use the LOG session
+      echo data
+    else:
+      writeToStdErr(data)
 
 proc quitOrDebug() {.inline.} =
   when not defined(endb):
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index dcea0c4cc..88e150cd1 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -37,22 +37,28 @@ when defined(nimTypeNames):
         a[j] = v
       if h == 1: break
 
-  proc dumpNumberOfInstances* =
-    # also add the allocated strings to the list of known types:
+  iterator dumpHeapInstances*(): tuple[name: cstring; count: int; sizes: int] =
+    ## Iterate over summaries of types on heaps.
+    ## This data may be inaccurate if allocations
+    ## are made by the iterator body.
     if strDesc.nextType == nil:
       strDesc.nextType = nimTypeRoot
       strDesc.name = "string"
       nimTypeRoot = addr strDesc
+    var it = nimTypeRoot
+    while it != nil:
+      if (it.instances > 0 or it.sizes != 0):
+        yield (it.name, it.instances, it.sizes)
+      it = it.nextType
+
+  proc dumpNumberOfInstances* =
     var a: InstancesInfo
     var n = 0
-    var it = nimTypeRoot
     var totalAllocated = 0
-    while it != nil:
-      if (it.instances > 0 or it.sizes != 0) and n < a.len:
-        a[n] = (it.name, it.instances, it.sizes)
-        inc n
+    for it in dumpHeapInstances():
+      a[n] = it
+      inc n
       inc totalAllocated, it.sizes
-      it = it.nextType
     sortInstances(a, n)
     for i in 0 .. n-1:
       c_fprintf(stdout, "[Heap] %s: #%ld; bytes: %ld\n", a[i][0], a[i][1], a[i][2])
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index e500444ea..152b48c24 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -108,7 +108,7 @@ proc getStackTrace*(e: ref Exception): string = e.trace
 proc unhandledException(e: ref Exception) {.
     compilerproc, asmNoStackFrame.} =
   var buf = ""
-  if e.msg != nil and e.msg[0] != '\0':
+  if e.msg.len != 0:
     add(buf, "Error: unhandled exception: ")
     add(buf, e.msg)
   else:
diff --git a/lib/system/memtracker.nim b/lib/system/memtracker.nim
index ae0297438..1b1f18039 100644
--- a/lib/system/memtracker.nim
+++ b/lib/system/memtracker.nim
@@ -73,12 +73,12 @@ proc addEntry(entry: LogEntry) =
       let x = cast[proc() {.nimcall, tags: [], gcsafe, locks: 0.}](writeStackTrace)
       x()
       quit 1
-      if gLog.count > high(gLog.data):
-        gLogger(gLog)
-        gLog.count = 0
-      gLog.data[gLog.count] = entry
-      inc gLog.count
-      gLog.disabled = false
+      #if gLog.count > high(gLog.data):
+      #  gLogger(gLog)
+      #  gLog.count = 0
+      #gLog.data[gLog.count] = entry
+      #inc gLog.count
+      #gLog.disabled = false
 
 proc memTrackerWrite(address: pointer; size: int; file: cstring; line: int) {.compilerProc.} =
   addEntry LogEntry(op: "write", address: address,
diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim
index 85c796ba0..06e89f130 100644
--- a/lib/system/osalloc.nim
+++ b/lib/system/osalloc.nim
@@ -207,6 +207,9 @@ elif defined(posix):
     # some arches like mips and alpha use different values
     const MAP_ANONYMOUS = 0x20
     const MAP_PRIVATE = 0x02        # Changes are private
+  elif defined(haiku):
+    const MAP_ANONYMOUS = 0x08
+    const MAP_PRIVATE = 0x02
   else:
     var
       MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 951435972..6438a0541 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -66,6 +66,7 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} =
   # This is not used by most recent versions of the compiler anymore, but
   # required for bootstrapping purposes.
   let start = max(start, 0)
+  if s == nil: return nil
   let len = min(last, s.len-1) - start + 1
   if len > 0:
     result = rawNewStringNoInit(len)
@@ -78,6 +79,7 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} =
 proc copyStr(s: NimString, start: int): NimString {.compilerProc.} =
   # This is not used by most recent versions of the compiler anymore, but
   # required for bootstrapping purposes.
+  if s == nil: return nil
   result = copyStrLast(s, start, s.len-1)
 
 proc nimToCStringConv(s: NimString): cstring {.compilerProc, inline.} =
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index 3bfaa1dc2..2434ea21e 100644
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -162,10 +162,12 @@ elif defined(genode):
       mainTls
 
 else:
-  when not defined(macosx):
+  when not (defined(macosx) or defined(haiku)):
     {.passL: "-pthread".}
 
-  {.passC: "-pthread".}
+  when not defined(haiku):
+    {.passC: "-pthread".}
+
   const
     schedh = "#define _GNU_SOURCE\n#include <sched.h>"
     pthreadh = "#define _GNU_SOURCE\n#include <pthread.h>"
@@ -714,3 +716,13 @@ elif defined(solaris):
     if threadId == 0:
       threadId = int(thr_self())
     result = threadId
+
+elif defined(haiku):
+  type thr_id {.importc: "thread_id", header: "<OS.h>".} = distinct int32
+  proc find_thread(name: cstring): thr_id {.importc, header: "<OS.h>".}
+
+  proc getThreadId*(): int =
+    ## get the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(find_thread(nil))
+    result = threadId
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index 3263f1d37..60a6e5d9b 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -396,7 +396,7 @@ else:
 
   proc moveFileA*(lpExistingFileName, lpNewFileName: cstring): WINBOOL {.
     importc: "MoveFileA", stdcall, dynlib: "kernel32".}
-  proc moveFileExA*(lpExistingFileName, lpNewFileName: WideCString,
+  proc moveFileExA*(lpExistingFileName, lpNewFileName: cstring,
                     flags: DWORD): WINBOOL {.
     importc: "MoveFileExA", stdcall, dynlib: "kernel32".}
 
diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim
index 3ba1120b2..4b5ce0a57 100644
--- a/nimsuggest/nimsuggest.nim
+++ b/nimsuggest/nimsuggest.nim
@@ -109,7 +109,7 @@ proc sexp(s: IdeCmd|TSymKind|PrefixMatch): SexpNode = sexp($s)
 proc sexp(s: Suggest): SexpNode =
   # If you change the order here, make sure to change it over in
   # nim-mode.el too.
-  let qp = if s.qualifiedPath.isNil: @[] else: s.qualifiedPath
+  let qp = if s.qualifiedPath.len == 0: @[] else: s.qualifiedPath
   result = convertSexp([
     s.section,
     TSymKind s.symkind,
@@ -176,7 +176,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
   let dirtyIdx = fileInfoIdx(conf, file, isKnownFile)
 
   if dirtyfile.len != 0: msgs.setDirtyFile(conf, dirtyIdx, dirtyfile)
-  else: msgs.setDirtyFile(conf, dirtyIdx, nil)
+  else: msgs.setDirtyFile(conf, dirtyIdx, "")
 
   conf.m.trackPos = newLineInfo(dirtyIdx, line, col)
   conf.m.trackPosAttached = false
@@ -209,7 +209,7 @@ proc executeEpc(cmd: IdeCmd, args: SexpNode;
     column = args[2].getNum
   var dirtyfile = ""
   if len(args) > 3:
-    dirtyfile = args[3].getStr(nil)
+    dirtyfile = args[3].getStr("")
   execute(cmd, file, dirtyfile, int(line), int(column), graph)
 
 proc returnEpc(socket: Socket, uid: BiggestInt, s: SexpNode|string,
diff --git a/nimsuggest/tester.nim b/nimsuggest/tester.nim
index 4cda272af..b962a9f83 100644
--- a/nimsuggest/tester.nim
+++ b/nimsuggest/tester.nim
@@ -70,22 +70,22 @@ proc parseCmd(c: string): seq[string] =
   result = @[]
   var i = 0
   var a = ""
-  while true:
+  while i < c.len:
     setLen(a, 0)
     # eat all delimiting whitespace
-    while c[i] in {' ', '\t', '\l', '\r'}: inc(i)
+    while i < c.len and c[i] in {' ', '\t', '\l', '\r'}: inc(i)
+    if i >= c.len: break
     case c[i]
     of '"': raise newException(ValueError, "double quotes not yet supported: " & c)
     of '\'':
       var delim = c[i]
       inc(i) # skip ' or "
-      while c[i] != '\0' and c[i] != delim:
+      while i < c.len and c[i] != delim:
         add a, c[i]
         inc(i)
-      if c[i] != '\0': inc(i)
-    of '\0': break
+      if i < c.len: inc(i)
     else:
-      while c[i] > ' ':
+      while i < c.len and c[i] > ' ':
         add(a, c[i])
         inc(i)
     add(result, a)
diff --git a/nimsuggest/tests/tdot4.nim b/nimsuggest/tests/tdot4.nim
index 25e77ed04..8510162ac 100644
--- a/nimsuggest/tests/tdot4.nim
+++ b/nimsuggest/tests/tdot4.nim
@@ -2,7 +2,7 @@ discard """
 $nimsuggest --tester --maxresults:2 $file
 >sug $1
 sug;;skProc;;tdot4.main;;proc (inp: string): string;;$file;;10;;5;;"";;100;;None
-sug;;skProc;;strutils.replace;;proc (s: string, sub: string, by: string): string{.noSideEffect, gcsafe, locks: 0.};;$lib/pure/strutils.nim;;1575;;5;;"Replaces `sub` in `s` by the string `by`.";;100;;None
+sug;;skProc;;strutils.replace;;proc (s: string, sub: string, by: string): string{.noSideEffect, gcsafe, locks: 0.};;$lib/pure/strutils.nim;;1506;;5;;"Replaces `sub` in `s` by the string `by`.";;100;;None
 """
 
 import strutils
diff --git a/nimsuggest/tests/tinclude.nim b/nimsuggest/tests/tinclude.nim
index 12d40d1e7..0616b7164 100644
--- a/nimsuggest/tests/tinclude.nim
+++ b/nimsuggest/tests/tinclude.nim
@@ -1,7 +1,7 @@
 discard """
 $nimsuggest --tester compiler/nim.nim
->def compiler/semexprs.nim:13:50
-def;;skType;;ast.PSym;;PSym;;*ast.nim;;691;;2;;"";;100
->def compiler/semexprs.nim:13:50
-def;;skType;;ast.PSym;;PSym;;*ast.nim;;691;;2;;"";;100
+>def compiler/semexprs.nim:25:50
+def;;skType;;ast.PSym;;PSym;;*ast.nim;;707;;2;;"";;100
+>def compiler/semexprs.nim:25:50
+def;;skType;;ast.PSym;;PSym;;*ast.nim;;707;;2;;"";;100
 """
diff --git a/readme.md b/readme.md
index bdc7c7549..e4fb52bd0 100644
--- a/readme.md
+++ b/readme.md
@@ -50,24 +50,33 @@ Next, to build from source you will need:
     other distros as well). 
 
 Then, if you are on a \*nix system or Windows, the following steps should compile
-Nim from source using ``gcc``, ``git`` and the ``koch`` build tool (in the place
-of ``sh build.sh`` you should substitute ``build.bat`` on x86 Windows or
-``build64.bat`` on x86_64 Windows):
+Nim from source using ``gcc``, ``git`` and the ``koch`` build tool.
 
 **Note: The following commands are for the development version of the compiler.**
 For most users, installing the latest stable version is enough. Check out
 the installation instructions on the website to do so: https://nim-lang.org/install.html.
 
 ```
+# step 1:
 git clone https://github.com/nim-lang/Nim.git
 cd Nim
+
+# step 2 (posix) clones `csources.git`, bootstraps Nim compiler and compiles tools
+sh build_all.sh
+
+# step 2 (windows)
 git clone --depth 1 https://github.com/nim-lang/csources.git
+
 cd csources
-sh build.sh
-cd ../
-bin/nim c koch
-./koch boot -d:release
-./koch tools # Compile Nimble and other tools.
+# requires `gcc` in your PATH, see also https://nim-lang.org/install_windows.html
+build.bat # x86 Windows
+build64.bat # x86_64 Windows
+cd ..
+
+bin\nim c koch
+koch boot -d:release
+koch tools # Compile Nimble and other tools
+# end of step 2 (windows)
 ```
 
 Finally, once you have finished the build steps (on Windows, Mac or Linux) you
diff --git a/tests/arithm/tashr.nim b/tests/arithm/tashr.nim
new file mode 100644
index 000000000..aeb3b6843
--- /dev/null
+++ b/tests/arithm/tashr.nim
@@ -0,0 +1,46 @@
+discard """
+  output: ''''''
+  targets: '''c js'''
+"""
+
+# issue #6255, feature request
+# arithmetic right shift
+
+var x1 = -123'i8
+var x2 = -123'i16
+var x3 = -123'i32
+var x4 = -123'i64
+var x5 = -123
+
+block codegen_test:
+  doAssert ashr(x1, 1) == -62
+  doAssert ashr(x2, 1) == -62
+  doAssert ashr(x3, 1) == -62
+  doAssert ashr(x4, 1) == -62
+  doAssert ashr(x5, 1) == -62
+
+block semfold_test:
+  doAssert ashr(-123'i8 , 1) == -62
+  doAssert ashr(-123'i16, 1) == -62
+  doAssert ashr(-123'i32, 1) == -62
+  doAssert ashr(-123'i64, 1) == -62
+  doAssert ashr(-123    , 1) == -62
+
+static: # VM test
+  doAssert ashr(-123'i8 , 1) == -62
+  doAssert ashr(-123'i16, 1) == -62
+  doAssert ashr(-123'i32, 1) == -62
+  doAssert ashr(-123'i64, 1) == -62
+  doAssert ashr(-123    , 1) == -62
+
+  var y1 = -123'i8
+  var y2 = -123'i16
+  var y3 = -123'i32
+  var y4 = -123'i64
+  var y5 = -123
+
+  doAssert ashr(y1, 1) == -62
+  doAssert ashr(y2, 1) == -62
+  doAssert ashr(y3, 1) == -62
+  doAssert ashr(y4, 1) == -62
+  doAssert ashr(y5, 1) == -62
diff --git a/tests/async/tasync_misc.nim b/tests/async/tasync_misc.nim
index 695dcd98a..dbc2ea434 100644
--- a/tests/async/tasync_misc.nim
+++ b/tests/async/tasync_misc.nim
@@ -37,8 +37,7 @@ block: #8399
       case line[0]
       of '+', '-': @[]
       of '$': (let x = await bar(); @[""])
-      else:
-        nil
+      else: @[]
 
     doAssert(res == @[""])
 
diff --git a/tests/async/tasyncssl.nim b/tests/async/tasyncssl.nim
index 3dc131036..0607cf3c6 100644
--- a/tests/async/tasyncssl.nim
+++ b/tests/async/tasyncssl.nim
@@ -15,11 +15,11 @@ when defined(ssl):
   var clientCount = 0
 
   proc sendMessages(client: AsyncSocket) {.async.} =
-    for i in 0 .. <messagesToSend:
+    for i in 0 ..< messagesToSend:
       await send(client, "Message " & $i & "\c\L")
 
   proc launchSwarm(port: Port) {.async.} =
-    for i in 0 .. <swarmSize:
+    for i in 0 ..< swarmSize:
       var sock = newAsyncSocket()
       var clientContext = newContext(verifyMode = CVerifyNone)
       clientContext.wrapSocket(sock)
diff --git a/tests/async/tioselectors.nim b/tests/async/tioselectors.nim
index d2e4cfec1..a556b6dd2 100644
--- a/tests/async/tioselectors.nim
+++ b/tests/async/tioselectors.nim
@@ -11,7 +11,9 @@ template processTest(t, x: untyped) =
   #stdout.flushFile()
   if not x: echo(t & " FAILED\r\n")
 
-when not defined(windows):
+when defined(macosx):
+  echo "All tests passed!"
+elif not defined(windows):
   import os, posix, nativesockets, times
 
   when ioselSupportedPlatform:
diff --git a/tests/casestmt/tcasestm.nim b/tests/casestmt/tcasestm.nim
index 4d32d023f..ff912ffab 100644
--- a/tests/casestmt/tcasestm.nim
+++ b/tests/casestmt/tcasestm.nim
@@ -41,16 +41,16 @@ let str2 = "NN"
 let a = case str1:
   of "Y": true
   of "N": false
-  else: 
+  else:
     echo "no good"
     quit("quiting")
 
-proc toBool(s: string): bool = 
+proc toBool(s: string): bool =
   case s:
-    of nil, "": raise newException(ValueError, "Invalid boolean")
-    elif s[0] == 'Y': true
-    elif s[0] == 'N': false
-    else: "error".quit(2)
+  of "": raise newException(ValueError, "Invalid boolean")
+  elif s[0] == 'Y': true
+  elif s[0] == 'N': false
+  else: "error".quit(2)
 
 
 let b = "NN".toBool()
@@ -66,7 +66,7 @@ static:
 var bb: bool
 doassert(not compiles(
   bb = case str2:
-    of nil, "": raise newException(ValueError, "Invalid boolean")
+    of "": raise newException(ValueError, "Invalid boolean")
     elif str.startsWith("Y"): true
     elif str.startsWith("N"): false
 ))
@@ -94,7 +94,7 @@ doassert(not compiles(
   bb = case str2:
     of "Y":
       raise newException(ValueError, "Invalid Y")
-      true    
+      true
     else: raise newException(ValueError, "Invalid")
 ))
 
@@ -103,6 +103,6 @@ doassert(not compiles(
   bb = case str2:
     of "Y":
       "invalid Y".quit(3)
-      true    
+      true
     else: raise newException(ValueError, "Invalid")
 ))
\ No newline at end of file
diff --git a/tests/ccgbugs/pkg8616/rtarray.nim b/tests/ccgbugs/pkg8616/rtarray.nim
new file mode 100644
index 000000000..286dbb8cd
--- /dev/null
+++ b/tests/ccgbugs/pkg8616/rtarray.nim
@@ -0,0 +1,2 @@
+proc head*[T](pp: var array[1,T]): var T =
+  result = pp[0]
diff --git a/tests/ccgbugs/pkg8616/scheduler.nim b/tests/ccgbugs/pkg8616/scheduler.nim
new file mode 100644
index 000000000..0730000c4
--- /dev/null
+++ b/tests/ccgbugs/pkg8616/scheduler.nim
@@ -0,0 +1,10 @@
+import rtarray
+
+type
+  T = tuple[x:int]
+
+var
+  arr: array[1,T]
+
+proc init*() =
+  discard head(arr)
diff --git a/tests/ccgbugs/t8616.nim b/tests/ccgbugs/t8616.nim
new file mode 100644
index 000000000..54068652a
--- /dev/null
+++ b/tests/ccgbugs/t8616.nim
@@ -0,0 +1,4 @@
+import pkg8616 / scheduler
+
+when isMainModule:
+  init()
diff --git a/tests/ccgbugs/topenarraycast.nim b/tests/ccgbugs/topenarraycast.nim
new file mode 100644
index 000000000..7d1bc8d03
--- /dev/null
+++ b/tests/ccgbugs/topenarraycast.nim
@@ -0,0 +1,8 @@
+proc foo[T](s: var openArray[T]): T =
+  for x in s: result += x
+
+proc bar(xyz: var seq[int]) =
+  doAssert 6 == (seq[int](xyz)).foo()
+
+var t = @[1,2,3]
+bar(t)
diff --git a/tests/closure/t8550.nim b/tests/closure/t8550.nim
new file mode 100644
index 000000000..153246f08
--- /dev/null
+++ b/tests/closure/t8550.nim
@@ -0,0 +1,12 @@
+discard """
+  output: "@[\"42\"]"
+"""
+
+proc chk_fail(): seq[string] =
+  iterator x(): int {.closure.} = yield 42
+  proc f(cl: iterator(): int {.closure.}): seq[string] =
+    result = @[]
+    for i in cl(): result.add($i)
+  result = f(x)
+
+echo(chk_fail())
diff --git a/tests/compiles/t8630.nim b/tests/compiles/t8630.nim
new file mode 100644
index 000000000..aa2be11cd
--- /dev/null
+++ b/tests/compiles/t8630.nim
@@ -0,0 +1,13 @@
+discard """
+  output: '''
+foo
+bar
+'''
+"""
+
+proc test(strings: seq[string]) =
+  for s in strings:
+    var p3 = unsafeAddr(s)
+    echo p3[]
+
+test(@["foo", "bar"])
diff --git a/tests/concepts/t3330.nim b/tests/concepts/t3330.nim
index 78dd876e2..8021db827 100644
--- a/tests/concepts/t3330.nim
+++ b/tests/concepts/t3330.nim
@@ -13,15 +13,11 @@ proc add(result: var string; x: float)
   first type mismatch at position: 1
   required type: var string
   but expression 'k' is of type: Alias
-proc add(x: var string; y: cstring)
-  first type mismatch at position: 1
-  required type: var string
-  but expression 'k' is of type: Alias
-proc add(x: var string; y: char)
+proc add(x: var string; y: string)
   first type mismatch at position: 1
   required type: var string
   but expression 'k' is of type: Alias
-proc add(x: var string; y: string)
+proc add(x: var string; y: cstring)
   first type mismatch at position: 1
   required type: var string
   but expression 'k' is of type: Alias
@@ -33,6 +29,10 @@ proc add(result: var string; x: int64)
   first type mismatch at position: 1
   required type: var string
   but expression 'k' is of type: Alias
+proc add(x: var string; y: char)
+  first type mismatch at position: 1
+  required type: var string
+  but expression 'k' is of type: Alias
 
 t3330.nim(48, 8) template/generic instantiation from here
 t3330.nim(55, 6) Foo: 'bar.value' cannot be assigned to
diff --git a/tests/converter/tconverter_with_constraint.nim b/tests/converter/tconverter_with_constraint.nim
new file mode 100644
index 000000000..793264434
--- /dev/null
+++ b/tests/converter/tconverter_with_constraint.nim
@@ -0,0 +1,20 @@
+
+discard """
+  file: "tconverter_with_constraint.nim"
+  line: 20
+  errormsg: "type mismatch: got <int>"
+"""
+
+type
+  MyType = distinct int
+
+converter to_mytype(m: int{lit}): MyType =
+  m.MyType
+ 
+proc myproc(m: MyType) =
+  echo m.int, ".MyType"
+
+myproc(1) # call by literal is ok 
+
+var x: int = 12
+myproc(x) # should fail
\ No newline at end of file
diff --git a/tests/cpp/tasync_cpp.nim b/tests/cpp/tasync_cpp.nim
index a5e3374b6..50bc1853c 100644
--- a/tests/cpp/tasync_cpp.nim
+++ b/tests/cpp/tasync_cpp.nim
@@ -1,6 +1,7 @@
 discard """
   targets: "cpp"
   output: "hello"
+  cmd: "nim cpp --nilseqs:on $file"
 """
 
 # bug #3299
diff --git a/tests/distinct/tnil.nim b/tests/distinct/tnil.nim
index 759a14657..16de38f60 100644
--- a/tests/distinct/tnil.nim
+++ b/tests/distinct/tnil.nim
@@ -9,7 +9,6 @@ discard """
 type
   MyPointer = distinct pointer
   MyString = distinct string
-  MyStringNotNil = distinct (string not nil)
   MyInt = distinct int
 
 proc foo(a: MyPointer) =
@@ -26,20 +25,6 @@ p = cast[MyPointer](nil)
 p = nil.MyPointer
 p = nil
 
-var c: MyString
-c = "Test".MyString
-c = nil.MyString
-c = nil
-
-p = nil
-doAssert(compiles(c = p) == false)
-
-var n: MyStringNotNil = "Test".MyStringNotNil # Cannot prove warning ...
-n = "Test".MyStringNotNil
-doAssert(compiles(n = nil.MyStringNotNil) == false)
-doAssert(compiles(n = nil.MyStringNotNil) == false)
-doAssert(compiles(n = nil) == false)
-
 var i: MyInt
 i = 1.MyInt
 doAssert(compiles(i = nil) == false)
diff --git a/tests/exception/tdont_overwrite_typename.nim b/tests/exception/tdont_overwrite_typename.nim
index 6e3ff816f..d6dca0990 100644
--- a/tests/exception/tdont_overwrite_typename.nim
+++ b/tests/exception/tdont_overwrite_typename.nim
@@ -7,10 +7,10 @@ Check passed'''
 # bug #5628
 
 proc checkException(ex: ref Exception) =
-  doAssert(ex.name == "ValueError")
+  doAssert(ex.name == cstring"ValueError")
   doAssert(ex.msg == "SecondException")
   doAssert(ex.parent != nil)
-  doAssert(ex.parent.name == "KeyError")
+  doAssert(ex.parent.name == cstring"KeyError")
   doAssert(ex.parent.msg == "FirstException")
   echo "Check passed"
 
diff --git a/tests/exprs/tstmtexprs.nim b/tests/exprs/tstmtexprs.nim
index 61089b694..615c36024 100644
--- a/tests/exprs/tstmtexprs.nim
+++ b/tests/exprs/tstmtexprs.nim
@@ -87,7 +87,7 @@ proc parseResponse(): JsonNode =
     var excMsg = key & "("
     if (var n=result["key2"]; n != nil):
       excMsg &= n.str
-    raise newException(SystemError, excMsg)
+    raise newException(CatchableError, excMsg)
 
 
 
diff --git a/tests/generics/t6137.nim b/tests/generics/t6137.nim
new file mode 100644
index 000000000..639675f35
--- /dev/null
+++ b/tests/generics/t6137.nim
@@ -0,0 +1,29 @@
+discard """
+  action: "reject"
+  line: 29
+  errormsg: "\'vectFunc\' doesn't have a concrete type, due to unspecified generic parameters."
+"""
+
+type
+  # simple vector of declared fixed length
+  vector[N : static[int]] = array[0..N-1, float]
+
+proc `*`[T](x: float, a: vector[T]): vector[T] =
+  # multiplication by scalar
+  for ii in 0..high(a):
+    result[ii] = a[ii]*x
+
+let
+  # define a vector of length 3
+  x: vector[3] = [1.0, 3.0, 5.0]
+
+proc vectFunc[T](x: vector[T]): vector[T] {.procvar.} =
+  # Define a vector function
+  result = 2.0*x
+
+proc passVectFunction[T](g: proc(x: vector[T]): vector[T], x: vector[T]): vector[T] =
+  # pass a vector function as input in another procedure
+  result = g(x)
+
+let
+  xNew = passVectFunction(vectFunc,x)
diff --git a/tests/generics/t7141.nim b/tests/generics/t7141.nim
new file mode 100644
index 000000000..8a128d828
--- /dev/null
+++ b/tests/generics/t7141.nim
@@ -0,0 +1,10 @@
+discard """
+  action: "reject"
+  line: 7
+  errormsg: "cannot instantiate: \'T\'"
+"""
+
+proc foo[T](x: T) =
+  discard
+
+var fun = if true: foo else: foo
diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim
index d014eb998..6897d9de2 100644
--- a/tests/generics/tgeneric3.nim
+++ b/tests/generics/tgeneric3.nim
@@ -103,7 +103,7 @@ proc DeleteItem[T,D] (n: PNode[T,D], x: int): PNode[T,D] {.inline.} =
 
   else :
     result = n.left
-    n.slots = nil
+    n.slots = @[]
     n.left = nil
 
 proc internalDelete[T,D] (ANode: PNode[T,D], key: T, Avalue: var D): PNode[T,D] =
@@ -200,7 +200,7 @@ proc traceTree[T,D](root: PNode[T,D]) =
       traceln(space)
       write stdout, "left: "
       doTrace(n.left, level+1)
-    for i, el in n.slots :
+    for i, el in n.slots:
       if el != nil and not isClean(el):
         traceln(space)
         traceX(i)
diff --git a/tests/generics/tobjecttyperel.nim b/tests/generics/tobjecttyperel.nim
index 8c8f90098..6c2184cc2 100644
--- a/tests/generics/tobjecttyperel.nim
+++ b/tests/generics/tobjecttyperel.nim
@@ -2,8 +2,8 @@ discard """
   output: '''(peel: 0, color: 15)
 (color: 15)
 17
-(width: 0.0, taste: nil, color: 13)
-(width: 0.0, taste: nil, color: 15)
+(width: 0.0, taste: "", color: 13)
+(width: 0.0, taste: "", color: 15)
 cool'''
 """
 
@@ -11,16 +11,16 @@ cool'''
 type
   BaseFruit[T] = object of RootObj
     color: T
-    
+
   MidLevel[T] = object of BaseFruit[T]
-  
+
   Mango = object of MidLevel[int]
     peel: int
-    
+
   Peach[X, T, Y] = object of T
     width: X
     taste: Y
-    
+
 proc setColor[T](self: var BaseFruit[T]) =
   self.color = 15
 
diff --git a/tests/js/tclosures.nim b/tests/js/tclosures.nim
index 67243c937..659c60092 100644
--- a/tests/js/tclosures.nim
+++ b/tests/js/tclosures.nim
@@ -48,4 +48,4 @@ for i in 1 .. 10:
 
 let results = runCallbacks()
 
-doAssert(expected == results)
+doAssert(expected == $results)
diff --git a/tests/js/testobjs.nim b/tests/js/testobjs.nim
index dd66825ec..78f0b4766 100644
--- a/tests/js/testobjs.nim
+++ b/tests/js/testobjs.nim
@@ -34,7 +34,7 @@ var
   recurse1 = Recurse[int](data: 1, next: recurse2)
 
 
-doAssert test.name == "Jorden"
+doAssert test.name == cstring"Jorden"
 doAssert knight.age == 19
 doAssert knight.item.price == 50
 doAssert recurse1.next.next.data == 3
diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim
index 325ab6366..156ca89e3 100644
--- a/tests/js/tjsffi.nim
+++ b/tests/js/tjsffi.nim
@@ -64,7 +64,7 @@ block:
   proc test(): bool =
     let obj = newJsObject()
     obj.`?!$` = proc(x, y, z: int, t: cstring): cstring = t & $(x + y + z)
-    obj.`?!$`(1, 2, 3, "Result is: ").to(cstring) == "Result is: 6"
+    obj.`?!$`(1, 2, 3, "Result is: ").to(cstring) == cstring"Result is: 6"
   echo test()
 
 # Test JsObject []()
diff --git a/tests/js/ttimes.nim b/tests/js/ttimes.nim
index bd599a7ae..ad7fdd211 100644
--- a/tests/js/ttimes.nim
+++ b/tests/js/ttimes.nim
@@ -21,17 +21,17 @@ doAssert b - a == initDuration(seconds = 500_000_000)
 
 # Because we can't change the timezone JS uses, we define a simple static timezone for testing.
 
-proc staticZoneInfoFromUtc(time: Time): ZonedTime =
+proc zonedTimeFromTime(time: Time): ZonedTime =
   result.utcOffset = -7200
   result.isDst = false
-  result.adjTime = time + 7200.seconds
+  result.time = time
 
-proc staticZoneInfoFromTz(adjTime: Time): ZonedTIme =
+proc zonedTimeFromAdjTime(adjTime: Time): ZonedTIme =
   result.utcOffset = -7200
   result.isDst = false
-  result.adjTime = adjTime
+  result.time = adjTime + initDuration(seconds = -7200)
 
-let utcPlus2 = Timezone(zoneInfoFromUtc: staticZoneInfoFromUtc, zoneInfoFromTz: staticZoneInfoFromTz, name: "")
+let utcPlus2 = newTimezone("", zonedTimeFromTime, zonedTimeFromAdjTime)
 
 block timezoneTests:
   let dt = initDateTime(01, mJan, 2017, 12, 00, 00, utcPlus2)
diff --git a/tests/macros/tforloop_macro1.nim b/tests/macros/tforloop_macro1.nim
index a8f45c7ac..49918563d 100644
--- a/tests/macros/tforloop_macro1.nim
+++ b/tests/macros/tforloop_macro1.nim
@@ -12,7 +12,7 @@ discard """
 """
 
 import macros
-
+{.experimental: "forLoopMacros".}
 macro mymacro(): untyped =
   result = newLit([1, 2, 3])
 
diff --git a/tests/macros/tlineinfo.nim b/tests/macros/tlineinfo.nim
new file mode 100644
index 000000000..2ab0e1ee8
--- /dev/null
+++ b/tests/macros/tlineinfo.nim
@@ -0,0 +1,14 @@
+# issue #5617, feature request
+# Ability to set a NimNode's lineinfo
+import macros
+
+type
+  Test = object
+
+macro mixer(n: typed): untyped =
+  let x = newIdentNode("echo")
+  x.copyLineInfo(n)
+  result = newLit(x.lineInfo == n.lineInfo)
+
+var z = mixer(Test)
+doAssert z
diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim
index 1095a893e..985139f0a 100644
--- a/tests/manyloc/argument_parser/argument_parser.nim
+++ b/tests/manyloc/argument_parser/argument_parser.nim
@@ -74,14 +74,14 @@ type
     ## nothing prevents you from accessing directly the type of field you want
     ## if you expect only one kind.
     case kind*: Tparam_kind
-    of PK_EMPTY: nil
+    of PK_EMPTY: discard
     of PK_INT: int_val*: int
     of PK_BIGGEST_INT: big_int_val*: BiggestInt
     of PK_FLOAT: float_val*: float
     of PK_BIGGEST_FLOAT: big_float_val*: BiggestFloat
     of PK_STRING: str_val*: string
     of PK_BOOL: bool_val*: bool
-    of PK_HELP: nil
+    of PK_HELP: discard
 
   Tcommandline_results* = object of RootObj ## \
     ## Contains the results of the parsing.
@@ -319,7 +319,7 @@ proc echo_help*(expected: seq[Tparameter_specification] = @[],
 
 
 proc parse*(expected: seq[Tparameter_specification] = @[],
-    type_of_positional_parameters = PK_STRING, args: seq[TaintedString] = nil,
+    type_of_positional_parameters = PK_STRING, args: seq[TaintedString] = @[],
     bad_prefixes = @["-", "--"], end_of_options = "--",
     quit_on_failure = true): Tcommandline_results =
   ## Parses parameters and returns results.
@@ -339,7 +339,7 @@ proc parse*(expected: seq[Tparameter_specification] = @[],
   ##
   ## The args sequence should be the list of parameters passed to your program
   ## without the program binary (usually OSes provide the path to the binary as
-  ## the zeroth parameter). If args is nil, the list will be retrieved from the
+  ## the zeroth parameter). If args is empty, the list will be retrieved from the
   ## OS.
   ##
   ## If there is any kind of error and quit_on_failure is true, the quit proc
@@ -358,7 +358,7 @@ proc parse*(expected: seq[Tparameter_specification] = @[],
 
   # Prepare the input parameter list, maybe get it from the OS if not available.
   var args = args
-  if args == nil:
+  if args.len == 0:
     let total_params = paramCount()
     #echo "Got no explicit args, retrieving from OS. Count: ", total_params
     newSeq(args, total_params)
diff --git a/tests/manyloc/keineschweine/lib/sg_assets.nim b/tests/manyloc/keineschweine/lib/sg_assets.nim
index 1e8a99c83..90f0a54e9 100644
--- a/tests/manyloc/keineschweine/lib/sg_assets.nim
+++ b/tests/manyloc/keineschweine/lib/sg_assets.nim
@@ -145,7 +145,7 @@ proc importHandling(data: JsonNode): THandlingRecord
 proc importBullet(data: JsonNode; errors: var seq[string]): PBulletRecord
 proc importSoul(data: JsonNode): TSoulRecord
 proc importExplosion(data: JsonNode; errors: var seq[string]): TExplosionRecord
-proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord
+proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = ""): PSoundRecord
 
 ## this is the only pipe between lobby and main.nim
 proc getActiveState*(): TGameState =
@@ -514,7 +514,7 @@ proc importExplosion(data: JsonNode; errors: var seq[string]): TExplosionRecord
   let expl = data["explode"]
   result.anim = importAnim(expl, errors)
   result.sound = importSound(expl, errors, "sound")
-proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord =
+proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = ""): PSoundRecord =
   if data.kind == JObject:
     checkKey(data, fieldName)
     result = newSound(data[fieldName].str, errors)
diff --git a/tests/manyloc/keineschweine/lib/sg_gui.nim b/tests/manyloc/keineschweine/lib/sg_gui.nim
index 95cef1b24..074e0604c 100644
--- a/tests/manyloc/keineschweine/lib/sg_gui.nim
+++ b/tests/manyloc/keineschweine/lib/sg_gui.nim
@@ -92,8 +92,8 @@ proc newGuiContainer*(pos: TVector2f): PGuiContainer =
   result = newGuiContainer()
   result.setPosition pos
 proc free*(container: PGuiContainer) =
-  container.widgets = nil
-  container.buttons = nil
+  container.widgets = @[]
+  container.buttons = @[]
 proc add*(container: PGuiContainer; widget: PGuiObject) =
   container.widgets.add(widget)
 proc add*(container: PGuiContainer; button: PButton) =
diff --git a/tests/metatype/ttypeselectors.nim b/tests/metatype/ttypeselectors.nim
index 2a2455adb..eb857271d 100644
--- a/tests/metatype/ttypeselectors.nim
+++ b/tests/metatype/ttypeselectors.nim
@@ -99,3 +99,15 @@ echo sizeof(a)
 echo sizeof(b)
 echo sizeof(c)
 
+# This is the same example but using a proc instead of a macro
+# Instead of type mismatch for macro, proc just failed with internal error: getTypeDescAux(tyNone)
+# https://github.com/nim-lang/Nim/issues/7231
+
+proc getBase2*(bits: static[int]): typedesc =
+  if bits == 128:
+    result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint64"))
+  else:
+    result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint32"))
+
+type
+  MpUint2*[bits: static[int]] = getbase2(bits)
diff --git a/tests/notnil/tmust_compile.nim b/tests/notnil/tmust_compile.nim
index a32c6c7ec..d09dda057 100644
--- a/tests/notnil/tmust_compile.nim
+++ b/tests/notnil/tmust_compile.nim
@@ -44,16 +44,16 @@ import json
 type
 
   foo = object
-    thing: string not nil
+    thing: ptr int not nil
 
   CTS = ref object
     subs_by_sid: Table[int, foo]
 
 
 proc parse(cts: CTS, jn: JsonNode) =
-
+  var y = jn.getInt(4523)
   let ces = foo(
-    thing: jn.getStr("thing")
+    thing: addr y
   )
 
   cts.subs_by_sid[0] = ces
@@ -63,17 +63,3 @@ proc parse(cts: CTS, jn: JsonNode) =
 
 proc p(x: proc(){.closure.} not nil) = discard
 p(proc(){.closure.} = discard)
-
-# bug #3993
-
-type
-  List[T] = seq[T] not nil
-
-proc `^^`[T](v: T, lst: List[T]): List[T] =
-  result = @[v]
-  result.add(lst)
-
-proc Nil[T](): List[T] = @[]
-
-when isMainModule:
-  let lst = 1 ^^ 2 ^^ Nil[int]()
diff --git a/tests/notnil/tnotnil.nim b/tests/notnil/tnotnil.nim
index e392b155c..aff3f8959 100644
--- a/tests/notnil/tnotnil.nim
+++ b/tests/notnil/tnotnil.nim
@@ -1,5 +1,5 @@
 discard """
-  line: 22
+  line: 13
   errormsg: "type mismatch"
 """
 {.experimental: "notnil".}
@@ -8,16 +8,6 @@ type
   TObj = object
     x: int
 
-  MyString = string not nil
-
-#var x: PObj = nil
-
-proc p(x: string not nil): int =
-  result = 45
-
-proc q(x: MyString) = discard
 proc q2(x: string) = discard
 
 q2(nil)
-q(nil)
-
diff --git a/tests/notnil/tnotnil1.nim b/tests/notnil/tnotnil1.nim
index 7f9d02295..60666d64d 100644
--- a/tests/notnil/tnotnil1.nim
+++ b/tests/notnil/tnotnil1.nim
@@ -1,6 +1,6 @@
 discard """
   errormsg: "'y' is provably nil"
-  line:38
+  line:25
 """
 
 import strutils
@@ -10,19 +10,6 @@ type
   TObj = object
     x, y: int
 
-type
-  superstring = string not nil
-
-
-proc q(s: superstring) =
-  echo s
-
-proc p2() =
-  var a: string = "I am not nil"
-  q(a) # but this should and does not
-
-p2()
-
 proc q(x: pointer not nil) =
   discard
 
diff --git a/tests/notnil/tnotnil_in_objconstr.nim b/tests/notnil/tnotnil_in_objconstr.nim
index d33709906..f0d5c1ae2 100644
--- a/tests/notnil/tnotnil_in_objconstr.nim
+++ b/tests/notnil/tnotnil_in_objconstr.nim
@@ -6,9 +6,9 @@ discard """
 # bug #2355
 type
   Foo = object
-    foo: string not nil
-    bar: string not nil
-
+    foo: ref int
+    bar: ref int not nil
+var x: ref int = new(int)
 # Create instance without initializaing the `bar` field
-var f = Foo(foo: "foo")
+var f = Foo(foo: x)
 echo f.bar.isNil # true
diff --git a/tests/osproc/texitsignal.nim b/tests/osproc/texitsignal.nim
index c0bed70ee..fbf5068ea 100644
--- a/tests/osproc/texitsignal.nim
+++ b/tests/osproc/texitsignal.nim
@@ -28,9 +28,12 @@ if paramCount() == 0:
     # windows kill happens using TerminateProcess(h, 0), so we should get a
     # 0 here
     echo p.waitForExit() == 0
+  elif defined(haiku):
+    # on Haiku, the program main thread receive SIGKILLTHR
+    echo p.waitForExit() == 128 + SIGKILLTHR
   else:
     # on posix (non-windows), kill sends SIGKILL
     echo p.waitForExit() == 128 + SIGKILL
 
 else:
-  sleep(5000)  # should get killed before this
\ No newline at end of file
+  sleep(5000)  # should get killed before this
diff --git a/tests/osproc/tworkingdir.nim b/tests/osproc/tworkingdir.nim
index eeed9240d..7d58d5ae6 100644
--- a/tests/osproc/tworkingdir.nim
+++ b/tests/osproc/tworkingdir.nim
@@ -12,6 +12,8 @@ else:
   var process: Process
   when defined(android):
     process = startProcess("/system/bin/env", "/system/bin", ["true"])
+  elif defined(haiku):
+    process = startProcess("/bin/env", "/bin", ["true"])
   else:
     process = startProcess("/usr/bin/env", "/usr/bin", ["true"])
   let dir2 = getCurrentDir()
diff --git a/tests/parallel/tptr_to_ref.nim b/tests/parallel/tptr_to_ref.nim
index fee210dcd..ae02c16e5 100644
--- a/tests/parallel/tptr_to_ref.nim
+++ b/tests/parallel/tptr_to_ref.nim
@@ -11,7 +11,7 @@ type
   Killer* = object
     lock:                      Lock
     bailed    {.guard: lock.}: bool
-    processes {.guard: lock.}: array[0..MAX_WORKERS-1, foreign ptr Process]
+    processes {.guard: lock.}: array[0..MAX_WORKERS-1, ptr Process]
 
 # Hold a lock for a statement.
 template hold(lock: Lock, body: untyped) =
@@ -32,7 +32,7 @@ proc initKiller*(): Killer =
 var killer = initKiller()
 
 # remember that a process has been launched, killing it if we have bailed.
-proc launched*(process: foreign ptr Process): int {.gcsafe.} =
+proc launched*(process: ptr Process): int {.gcsafe.} =
   result = killer.processes.high + 1
   killer.lock.hold:
     if killer.bailed:
diff --git a/tests/parallel/tsendtwice.nim b/tests/parallel/tsendtwice.nim
index 0700fc4da..0c923177a 100644
--- a/tests/parallel/tsendtwice.nim
+++ b/tests/parallel/tsendtwice.nim
@@ -1,11 +1,11 @@
 discard """
-  output: '''obj2 nil
-obj nil
-obj3 nil
+  output: '''obj2 @[]
+obj @[]
+obj3 @[]
 3
-obj2 nil
-obj nil
-obj3 nil'''
+obj2 @[]
+obj @[]
+obj3 @[]'''
   cmd: "nim c -r --threads:on $file"
 """
 
diff --git a/tests/parallel/twaitany.nim b/tests/parallel/twaitany.nim
index 69136a3b6..fcabf691e 100644
--- a/tests/parallel/twaitany.nim
+++ b/tests/parallel/twaitany.nim
@@ -18,12 +18,12 @@ var results: seq[int] = @[]
 for i in 0 .. durations.high:
   tasks.add spawn timer(durations[i])
 
-var index = awaitAny(tasks)
+var index = blockUntilAny(tasks)
 while index != -1:
   results.add ^cast[FlowVar[int]](tasks[index])
   tasks.del(index)
   #echo repr results
-  index = awaitAny(tasks)
+  index = blockUntilAny(tasks)
 
 doAssert results.len == 5
 doAssert 1000 in results
diff --git a/tests/pragmas/custom_pragma.nim b/tests/pragmas/custom_pragma.nim
index 9e8f51deb..d2fc969d0 100644
--- a/tests/pragmas/custom_pragma.nim
+++ b/tests/pragmas/custom_pragma.nim
@@ -2,4 +2,4 @@
 
 template serializationKey*(s: string) {.pragma.}
 template defaultValue*(V: typed) {.pragma.}
-template alternativeKey*(s: string = nil, V: typed) {.pragma.}
\ No newline at end of file
+template alternativeKey*(s: string = "", V: typed) {.pragma.}
diff --git a/tests/stdlib/nre/captures.nim b/tests/stdlib/nre/captures.nim
index 19c344a8d..31de71154 100644
--- a/tests/stdlib/nre/captures.nim
+++ b/tests/stdlib/nre/captures.nim
@@ -27,7 +27,7 @@ suite "captures":
 
     let ex2 = "foo".find(re("(?<foo>foo)(?<bar>bar)?"))
     check(ex2.captures["foo"] == "foo")
-    check(ex2.captures["bar"] == nil)
+    check(ex2.captures["bar"] == "")
 
   test "named capture bounds":
     let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?"))
@@ -41,7 +41,7 @@ suite "captures":
 
   test "named capture table":
     let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?"))
-    check(ex1.captures.toTable == {"foo" : "foo", "bar" : nil}.toTable())
+    check(ex1.captures.toTable == {"foo" : "foo", "bar" : ""}.toTable())
     check(ex1.captureBounds.toTable == {"foo" : some(0..2), "bar" : none(Slice[int])}.toTable())
     check(ex1.captures.toTable("") == {"foo" : "foo", "bar" : ""}.toTable())
 
@@ -50,7 +50,7 @@ suite "captures":
 
   test "capture sequence":
     let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?"))
-    check(ex1.captures.toSeq == @["foo", nil])
+    check(ex1.captures.toSeq == @["foo", ""])
     check(ex1.captureBounds.toSeq == @[some(0..2), none(Slice[int])])
     check(ex1.captures.toSeq("") == @["foo", ""])
 
diff --git a/tests/stdlib/nre/replace.nim b/tests/stdlib/nre/replace.nim
index 516fd4328..b762271a2 100644
--- a/tests/stdlib/nre/replace.nim
+++ b/tests/stdlib/nre/replace.nim
@@ -16,5 +16,5 @@ suite "replace":
     check("123".replace(re"(?<foo>\d)(\d)", "${foo}$#$#") == "1123")
 
   test "replacing missing captures should throw instead of segfaulting":
-    expect ValueError: discard "ab".replace(re"(a)|(b)", "$1$2")
-    expect ValueError: discard "b".replace(re"(a)?(b)", "$1$2")
+    discard "ab".replace(re"(a)|(b)", "$1$2")
+    discard "b".replace(re"(a)?(b)", "$1$2")
diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim
index c0b4d5f78..bf0bb3ea7 100644
--- a/tests/stdlib/tjsonmacro.nim
+++ b/tests/stdlib/tjsonmacro.nim
@@ -40,7 +40,7 @@ when isMainModule:
     test: 18827361,
     test2: "hello world",
     test3: true,
-    testNil: nil
+    testNil: "nil"
   )
 
   let node = %x
@@ -53,7 +53,7 @@ when isMainModule:
   doAssert y.test == 18827361
   doAssert y.test2 == "hello world"
   doAssert y.test3
-  doAssert y.testNil.isNil
+  doAssert y.testNil == "nil"
 
   # Test for custom object variants (without an enum) and with an else branch.
   block:
diff --git a/tests/stdlib/tnilecho.nim b/tests/stdlib/tnilecho.nim
index 147b5e492..ec8d71dab 100644
--- a/tests/stdlib/tnilecho.nim
+++ b/tests/stdlib/tnilecho.nim
@@ -1,2 +1,6 @@
-var x = @["1", nil, "3"]
-doAssert $x == "@[1, nil, 3]"
+discard """
+  output: ""
+"""
+
+var x = @["1", "", "3"]
+doAssert $x == """@["1", "", "3"]"""
diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim
index 4d4081d39..f0ee755f7 100644
--- a/tests/stdlib/tstrutil.nim
+++ b/tests/stdlib/tstrutil.nim
@@ -199,6 +199,12 @@ proc testRFind =
   assert "0123456789ABCDEFGAH".rfind({'A'..'C'}, 13) == 12
   assert "0123456789ABCDEFGAH".rfind({'G'..'H'}, 13) == -1
 
+proc testSplitLines() =
+  let fixture = "a\nb\rc\r\nd"
+  assert len(fixture.splitLines) == 4
+  assert splitLines(fixture) == @["a", "b", "c", "d"]
+  assert splitLines(fixture, keepEol=true) == @["a\n", "b\r", "c\r\n", "d"]
+
 proc testCountLines =
   proc assertCountLines(s: string) = assert s.countLines == s.splitLines.len
   assertCountLines("")
@@ -229,7 +235,7 @@ proc testParseInts =
   assert "72".parseHexInt == 114
   assert "FF".parseHexInt == 255
   assert "ff".parseHexInt == 255
-  assert "fF".parseHexInt == 255  
+  assert "fF".parseHexInt == 255
   assert "0x7_2".parseHexInt == 114
   rejectParse "".parseHexInt
   rejectParse "_".parseHexInt
@@ -252,6 +258,7 @@ proc testParseInts =
 testDelete()
 testFind()
 testRFind()
+testSplitLines()
 testCountLines()
 testParseInts()
 
diff --git a/tests/stdlib/tsugar.nim b/tests/stdlib/tsugar.nim
new file mode 100644
index 000000000..a870bf6fe
--- /dev/null
+++ b/tests/stdlib/tsugar.nim
@@ -0,0 +1,29 @@
+discard """
+  file: "tsugar.nim"
+  output: ""
+"""
+import sugar
+import macros
+
+block distinctBase:
+  block:
+    type
+      Foo[T] = distinct seq[T]
+    var a: Foo[int]
+    doAssert a.type.distinctBase is seq[int]
+
+  block:
+    # simplified from https://github.com/nim-lang/Nim/pull/8531#issuecomment-410436458
+    macro uintImpl(bits: static[int]): untyped =
+      if bits >= 128:
+        let inner = getAST(uintImpl(bits div 2))
+        result = newTree(nnkBracketExpr, ident("UintImpl"), inner)
+      else:
+        result = ident("uint64")
+
+    type
+      BaseUint = UintImpl or SomeUnsignedInt
+      UintImpl[Baseuint] = object
+      Uint[bits: static[int]] = distinct uintImpl(bits)
+
+    doAssert Uint[128].distinctBase is UintImpl[uint64]
diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim
index e3f61ff77..e2fdf2a1f 100644
--- a/tests/stdlib/ttimes.nim
+++ b/tests/stdlib/ttimes.nim
@@ -11,20 +11,17 @@ import
 proc staticTz(hours, minutes, seconds: int = 0): Timezone {.noSideEffect.} =
   let offset = hours * 3600 + minutes * 60 + seconds
 
-  proc zoneInfoFromTz(adjTime: Time): ZonedTime {.locks: 0.} =
+  proc zonedTimeFromAdjTime(adjTime: Time): ZonedTime {.locks: 0.} =
     result.isDst = false
     result.utcOffset = offset
-    result.adjTime = adjTime
+    result.time = adjTime + initDuration(seconds = offset)
 
-  proc zoneInfoFromUtc(time: Time): ZonedTime {.locks: 0.}=
+  proc zonedTimeFromTime(time: Time): ZonedTime {.locks: 0.}=
     result.isDst = false
     result.utcOffset = offset
-    result.adjTime = fromUnix(time.toUnix - offset)
-
-  result.name = ""
-  result.zoneInfoFromTz = zoneInfoFromTz
-  result.zoneInfoFromUtc = zoneInfoFromUtc
+    result.time = time
 
+  newTimezone("", zonedTimeFromTime, zonedTImeFromAdjTime)
 
 # $ date --date='@2147483647'
 # Tue 19 Jan 03:14:07 GMT 2038
@@ -322,22 +319,7 @@ suite "ttimes":
     parseTestExcp("-1 BC", "UUUU g")
 
   test "dynamic timezone":
-    proc staticOffset(offset: int): Timezone =
-      proc zoneInfoFromTz(adjTime: Time): ZonedTime =
-          result.isDst = false
-          result.utcOffset = offset
-          result.adjTime = adjTime
-
-      proc zoneInfoFromUtc(time: Time): ZonedTime =
-          result.isDst = false
-          result.utcOffset = offset
-          result.adjTime = fromUnix(time.toUnix - offset)
-
-      result.name = ""
-      result.zoneInfoFromTz = zoneInfoFromTz
-      result.zoneInfoFromUtc = zoneInfoFromUtc
-
-    let tz = staticOffset(-9000)
+    let tz = staticTz(seconds = -9000)
     let dt = initDateTime(1, mJan, 2000, 12, 00, 00, tz)
     check dt.utcOffset == -9000
     check dt.isDst == false
diff --git a/tests/system/tnilconcats.nim b/tests/system/tnilconcats.nim
index ce059b7b0..5e4a1b317 100644
--- a/tests/system/tnilconcats.nim
+++ b/tests/system/tnilconcats.nim
@@ -1,5 +1,5 @@
 discard """
-  output: '''@[nil, nil, nil, nil, nil, nil, nil, "meh"]'''
+  output: '''@["", "", "", "", "", "", "", "meh"]'''
   exitcode: "0"
 """
 
diff --git a/tests/system/tostring.nim b/tests/system/tostring.nim
index 9a6b83f95..42c07c0a4 100644
--- a/tests/system/tostring.nim
+++ b/tests/system/tostring.nim
@@ -24,10 +24,10 @@ doAssert "nan" == $(0.0/0.0)
 # nil tests
 # maybe a bit inconsistent in types
 var x: seq[string]
-doAssert "nil" == $(x)
+doAssert "@[]" == $(x)
 
-var y: string = nil
-doAssert nil == $(y)
+var y: string
+doAssert "" == $(y)
 
 type
   Foo = object
@@ -54,7 +54,7 @@ doAssert $arr == "['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '
 doAssert $cstring(unsafeAddr arr) == "Hello World!"
 
 proc takes(c: cstring) =
-  doAssert c == ""
+  doAssert c == cstring""
 
 proc testm() =
   var x: string
@@ -90,12 +90,8 @@ proc stringCompare() =
 
   doAssert e == ""
   doAssert "" == e
-  doAssert nil == e
-  doAssert e == nil
   doAssert f == g
   doAssert "" == ""
-  doAssert "" == nil
-  doAssert nil == ""
 
   g.setLen(10)
   doAssert g == "\0\0\0\0\0\0\0\0\0\0"
diff --git a/tests/template/mgensym_generic_cross_module.nim b/tests/template/mgensym_generic_cross_module.nim
index 80b681db4..ea88f67e6 100644
--- a/tests/template/mgensym_generic_cross_module.nim
+++ b/tests/template/mgensym_generic_cross_module.nim
@@ -1,6 +1,6 @@
 
-template makeDomElement(x: untyped, name: string = nil) =
-  const tag {.gensym.} = if name == nil: astToStr(x) else: name
+template makeDomElement(x: untyped, name: string = "") =
+  const tag {.gensym.} = if name.len == 0: astToStr(x) else: name
 
   proc x*(p: int|float) =
     echo tag, p
diff --git a/tests/template/tdefault_nil.nim b/tests/template/tdefault_nil.nim
index c5c372d9e..311a6c090 100644
--- a/tests/template/tdefault_nil.nim
+++ b/tests/template/tdefault_nil.nim
@@ -2,7 +2,7 @@
 # bug #2629
 import sequtils, os
 
-template glob_rst(basedir: string = nil): untyped =
+template glob_rst(basedir: string = ""): untyped =
   if baseDir.isNil:
     to_seq(walk_files("*.rst"))
   else:
diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim
index cde8880b3..2c55bd0bd 100644
--- a/tests/testament/categories.nim
+++ b/tests/testament/categories.nim
@@ -76,7 +76,7 @@ proc flagTests(r: var TResults, cat: Category, options: string) =
     testExec r, makeTest(filename, " cmd /c cd " & nimcache &
                          " && compile_tgenscript.bat", cat)
 
-  when defined(linux):
+  elif defined(posix):
     testExec r, makeTest(filename, " sh -c \"cd " & nimcache &
                          " && sh compile_tgenscript.sh\"", cat)
 
@@ -109,10 +109,14 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
     safeCopyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll)
   else:
     # posix relies on crappy LD_LIBRARY_PATH (ugh!):
-    var libpath = getEnv"LD_LIBRARY_PATH".string
+    const libpathenv = when defined(haiku):
+                         "LIBRARY_PATH"
+                       else:
+                         "LD_LIBRARY_PATH"
+    var libpath = getEnv(libpathenv).string
     # Temporarily add the lib directory to LD_LIBRARY_PATH:
-    putEnv("LD_LIBRARY_PATH", "tests/dll" & (if libpath.len > 0: ":" & libpath else: ""))
-    defer: putEnv("LD_LIBRARY_PATH", libpath)
+    putEnv(libpathenv, "tests/dll" & (if libpath.len > 0: ":" & libpath else: ""))
+    defer: putEnv(libpathenv, libpath)
     var nimrtlDll = DynlibFormat % "nimrtl"
     safeCopyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll)
 
@@ -256,6 +260,8 @@ proc jsTests(r: var TResults, cat: Category, options: string) =
 # ------------------------- nim in action -----------
 
 proc testNimInAction(r: var TResults, cat: Category, options: string) =
+  let options = options & " --nilseqs:on"
+
   template test(filename: untyped, action: untyped) =
     testSpec r, makeTest(filename, options, cat, action)
 
@@ -482,8 +488,12 @@ proc processCategory(r: var TResults, cat: Category, options: string) =
       compileRodFiles(r, cat, options)
       runRodFiles(r, cat, options)
   of "js":
-    # XXX JS doesn't need to be special anymore
-    jsTests(r, cat, options)
+    # only run the JS tests on Windows or Linux because Travis is bad
+    # and other OSes like Haiku might lack nodejs:
+    if not defined(linux) and isTravis:
+      discard
+    else:
+      jsTests(r, cat, options)
   of "dll":
     dllTests(r, cat, options)
   of "flags":
@@ -521,5 +531,9 @@ proc processCategory(r: var TResults, cat: Category, options: string) =
     # We can't test it because it depends on a third party.
     discard # TODO: Move untestable tests to someplace else, i.e. nimble repo.
   else:
+    var testsRun = 0
     for name in os.walkFiles("tests" & DirSep &.? cat.string / "t*.nim"):
       testSpec r, makeTest(name, options, cat)
+      inc testsRun
+    if testsRun == 0:
+      echo "[Warning] - Invalid category specified \"", cat.string, "\", no tests were run"
diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim
index ac79e3942..fbe930a4f 100644
--- a/tests/testament/specs.nim
+++ b/tests/testament/specs.nim
@@ -12,8 +12,8 @@ import parseutils, strutils, os, osproc, streams, parsecfg
 
 var compilerPrefix* = "compiler" / "nim "
 
-let isTravis = existsEnv("TRAVIS")
-let isAppVeyor = existsEnv("APPVEYOR")
+let isTravis* = existsEnv("TRAVIS")
+let isAppVeyor* = existsEnv("APPVEYOR")
 
 proc cmdTemplate*(): string =
   compilerPrefix & "$target --lib:lib --hints:on -d:testing $options $file"
diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim
index 136a509e4..566338559 100644
--- a/tests/testament/tester.nim
+++ b/tests/testament/tester.nim
@@ -22,7 +22,7 @@ const
 
 Command:
   all                         run all tests
-  c|category <category>       run all the tests of a certain category
+  c|cat|category <category>   run all the tests of a certain category
   r|run <test>                run single test file
   html                        generate $1 from the database
 Arguments:
@@ -262,6 +262,7 @@ proc codegenCheck(test: TTest, target: TTarget, spec: TSpec, expectedMsg: var st
     echo getCurrentExceptionMsg()
   except IOError:
     given.err = reCodeNotFound
+    echo getCurrentExceptionMsg()
 
 proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec) =
   let exp = expectedNimout.strip.replace("\C\L", "\L")
@@ -509,7 +510,8 @@ proc main() =
   backend.close()
   var failed = r.total - r.passed - r.skipped
   if failed != 0:
-    echo "FAILURE! total: ", r.total, " passed: ", r.passed, " skipped: ", r.skipped
+    echo "FAILURE! total: ", r.total, " passed: ", r.passed, " skipped: ",
+      r.skipped, " failed: ", failed
     quit(QuitFailure)
 
 if paramCount() == 0:
diff --git a/tests/threads/t8535.nim b/tests/threads/t8535.nim
new file mode 100644
index 000000000..a8d69657b
--- /dev/null
+++ b/tests/threads/t8535.nim
@@ -0,0 +1,16 @@
+discard """
+  output: "0"
+"""
+
+type
+  CircAlloc* [Size: static[int] , T]  =  tuple
+    baseArray           : array[Size,T]
+    index               : uint16
+
+type
+  Job = object of RootObj
+
+var foo {.threadvar.}: CircAlloc[1,Job]
+
+when isMainModule:
+  echo foo.index
diff --git a/tests/threads/tactors.nim b/tests/threads/tactors.nim
index 45a03ebb7..ea052b9bd 100644
--- a/tests/threads/tactors.nim
+++ b/tests/threads/tactors.nim
@@ -5,9 +5,9 @@ discard """
 import actors
 
 var
-  pool: TActorPool[int, void]
+  pool: ActorPool[int, void]
 createActorPool(pool)
-for i in 0 .. < 300:
+for i in 0 ..< 300:
   pool.spawn(i, proc (x: int) {.thread.} = echo x)
 pool.join()
 
diff --git a/tests/tuples/tuple_with_nil.nim b/tests/tuples/tuple_with_nil.nim
index ec48337bd..e09c53407 100644
--- a/tests/tuples/tuple_with_nil.nim
+++ b/tests/tuples/tuple_with_nil.nim
@@ -73,7 +73,7 @@ type
 
 const
   DefaultPrec = 6 ## Default precision for floating point numbers.
-  DefaultFmt: Format = (ftDefault, -1, -1, nil, faDefault, fsMinus, false, false, false, nil)
+  DefaultFmt: Format = (ftDefault, -1, -1, "", faDefault, fsMinus, false, false, false, "")
     ## Default format corresponding to the empty format string, i.e.
     ##   `x.format("") == x.format(DefaultFmt)`.
   round_nums = [0.5, 0.05, 0.005, 0.0005, 0.00005, 0.000005, 0.0000005, 0.00000005]
@@ -124,7 +124,7 @@ proc parse(fmt: string): Format {.nosideeffect.} =
   if fmt.rawmatch(p, 0, caps) < 0:
     raise newException(FormatError, "Invalid format string")
 
-  result.fill = fmt.get(caps, 0, nil)
+  result.fill = fmt.get(caps, 0, "")
 
   case fmt.get(caps, 1, 0.char)
   of '<': result.align = faLeft
@@ -144,7 +144,7 @@ proc parse(fmt: string): Format {.nosideeffect.} =
   result.width = fmt.get(caps, 4, -1)
 
   if caps.has(4) and fmt[caps.bounds(4).first] == '0':
-    if result.fill != nil:
+    if result.fill != "":
       raise newException(FormatError, "Leading 0 in with not allowed with explicit fill character")
     if result.align != faDefault:
       raise newException(FormatError, "Leading 0 in with not allowed with explicit alignment")
@@ -171,7 +171,7 @@ proc parse(fmt: string): Format {.nosideeffect.} =
   of '%': result.typ = ftPercent
   else: result.typ = ftDefault
 
-  result.arysep = fmt.get(caps, 8, nil, 1)
+  result.arysep = fmt.get(caps, 8, "", 1)
 
 proc getalign(fmt: Format; defalign: FmtAlign; slen: int) : tuple[left, right:int] {.nosideeffect.} =
   ## Returns the number of left and right padding characters for a
@@ -218,7 +218,7 @@ proc writefill(o: var Writer; fmt: Format; n: int; signum: int = 0) =
     elif fmt.sign == fsPlus: write(o, '+')
     elif fmt.sign == fsSpace: write(o, ' ')
 
-  if fmt.fill == nil:
+  if fmt.fill.len == 0:
     for i in 1..n: write(o, ' ')
   else:
     for i in 1..n:
@@ -626,19 +626,19 @@ proc splitfmt(s: string): seq[Part] {.compiletime, nosideeffect.} =
       else:
         lvl.dec
     let clpos = pos
-    var fmtpart = Part(kind: pkFmt, arg: -1, fmt: s.substr(oppos+1, clpos-1), field: nil, index: int.high, nested: nested)
+    var fmtpart = Part(kind: pkFmt, arg: -1, fmt: s.substr(oppos+1, clpos-1), field: "", index: int.high, nested: nested)
     if fmtpart.fmt.len > 0:
       var m: array[0..3, string]
       if not fmtpart.fmt.match(subpeg, m):
         error("invalid format string")
 
-      if m[1] != nil and m[1].len > 0:
+      if m[1].len > 0:
         fmtpart.field = m[1].substr(1)
-      if m[2] != nil and m[2].len > 0:
+      if m[2].len > 0:
         discard parseInt(m[2].substr(1, m[2].len-2), fmtpart.index)
 
       if m[0].len > 0: discard parseInt(m[0], fmtpart.arg)
-      if m[3] == nil or m[3].len == 0:
+      if m[3].len == 0:
         fmtpart.fmt = ""
       elif m[3][0] == ':':
         fmtpart.fmt = m[3].substr(1)
@@ -650,7 +650,7 @@ proc splitfmt(s: string): seq[Part] {.compiletime, nosideeffect.} =
 proc literal(s: string): NimNode {.compiletime, nosideeffect.} =
   ## Return the nim literal of string `s`. This handles the case if
   ## `s` is nil.
-  result = if s == nil: newNilLit() else: newLit(s)
+  result = newLit(s)
 
 proc literal(b: bool): NimNode {.compiletime, nosideeffect.} =
   ## Return the nim literal of boolean `b`. This is either `true`
@@ -713,7 +713,7 @@ proc generatefmt(fmtstr: string;
           args[arg].cnt = args[arg].cnt + 1
           arg.inc
         # possible field access
-        if part.field != nil and part.field.len > 0:
+        if part.field.len > 0:
           argexpr = newDotExpr(argexpr, part.field.ident)
         # possible array access
         if part.index < int.high:
@@ -724,7 +724,7 @@ proc generatefmt(fmtstr: string;
           # nested format string. Compute the format string by
           # concatenating the parts of the substring.
           for e in generatefmt(part.fmt, args, arg):
-            var newexpr = if part.fmt == nil: e.val else: newCall(bindsym"format", e.val, e.fmt)
+            var newexpr = if part.fmt.len == 0: e.val else: newCall(bindsym"format", e.val, e.fmt)
             if fmtexpr != nil and fmtexpr.kind != nnkNilLit:
               fmtexpr = infix(fmtexpr, "&", newexpr)
             else:
diff --git a/tests/tuples/tuple_with_seq.nim b/tests/tuples/tuple_with_seq.nim
index 39edb500f..00b07dd2c 100644
--- a/tests/tuples/tuple_with_seq.nim
+++ b/tests/tuples/tuple_with_seq.nim
@@ -3,8 +3,8 @@ discard """
 @[1, 2, 3]'''
 """
 
-template foo(s: string = nil) =
-  if isNil(s):
+template foo(s: string = "") =
+  if s.len == 0:
     echo "it's nil"
   else:
     echo s
diff --git a/tests/types/temptyseqs.nim b/tests/types/temptyseqs.nim
index 834f63729..28c3b87b8 100644
--- a/tests/types/temptyseqs.nim
+++ b/tests/types/temptyseqs.nim
@@ -22,7 +22,7 @@ when true:
   import os
 
   type
-    In_out = tuple[src, dest, options: string]
+    In_out = tuple[src, dest: string, options: ref int]
 
   let
     nil_var: In_out = ("hey"/"there", "something", nil)
diff --git a/tests/vm/meta.nim b/tests/vm/meta.nim
index 2aa01b5b3..32a441f27 100644
--- a/tests/vm/meta.nim
+++ b/tests/vm/meta.nim
@@ -39,7 +39,7 @@ proc newIdent*(node: NimNode): Ident =
       raise newException(ValueError, msg)
 
 proc render*(i: Ident): NimNode {.compileTime.} =
-  if i.name == nil:
+  if i.name == "":
     return newNimNode(nnkEmpty)
 
   if i.exported:
@@ -67,7 +67,7 @@ proc newBracket*(node: NimNode): Bracket =
       raise newException(ValueError, msg)
 
 # Field procs
-proc newField*(identifier: Ident, type_name: string, default: string = nil): Field =
+proc newField*(identifier: Ident, type_name: string, default: string = ""): Field =
   result.identifier = identifier
   result.type_name = type_name
   result.default = default
@@ -84,7 +84,7 @@ proc newField*(node: NimNode): Field =
         of nnkIdent:
           result.default = $(node[2])
         else:
-          result.default = nil
+          result.default = ""
     else:
       let msg = "newField cannot initialize from node kind: " & $(node.kind)
       raise newException(ValueError, msg)
@@ -102,7 +102,7 @@ proc newFieldSeq*(node: NimNode): FieldSeq =
         of nnkIdent:
           default = $(default_node)
         else:
-          default = nil
+          default = ""
       for i in 0..node.len - 3:
         let name = newIdent(node[i])
         result.add(newField(name, type_name, default))
@@ -115,8 +115,8 @@ proc newFieldSeq*(node: NimNode): FieldSeq =
 
 proc render*(f: Field): NimNode {.compileTime.} =
   let identifier = f.identifier.render()
-  let type_name = if f.type_name != nil: ident(f.type_name) else: newEmptyNode()
-  let default = if f.default != nil: ident(f.default) else: newEmptyNode()
+  let type_name = if f.type_name != "": ident(f.type_name) else: newEmptyNode()
+  let default = if f.default != "": ident(f.default) else: newEmptyNode()
   newIdentDefs(identifier, type_name, default)
 
 proc render*(fs: FieldSeq): NimNode {.compileTime.} =
@@ -127,7 +127,7 @@ proc render*(fs: FieldSeq): NimNode {.compileTime.} =
 # TypeDef procs
 proc newTypeDef*(identifier: Ident, is_ref = false,
                 object_type = "object",
-                base_type: string = nil): TypeDef {.compileTime.} =
+                base_type: string = ""): TypeDef {.compileTime.} =
   result.identifier = identifier
   result.fields = @[]
   result.is_ref = is_ref
@@ -165,7 +165,7 @@ proc render*(typedef: TypeDef): NimNode {.compileTime.} =
   result.add(newEmptyNode())
   let object_node = newNimNode(nnkObjectTy)
   object_node.add(newEmptyNode())
-  if typedef.base_type == nil:
+  if typedef.base_type == "":
     object_node.add(newEmptyNode())
   else:
     var base_type = newNimNode(nnkOfInherit)
@@ -195,8 +195,8 @@ proc render*(typeseq: TypeDefSeq): NimNode {.compileTime.} =
   for typedef in typeseq:
     result.add(typedef.render())
 
-proc newProc*(identifier: Ident, params: FieldSeq = nil,
-              returns: Ident, generics: FieldSeq = nil): Proc =
+proc newProc*(identifier: Ident, params: FieldSeq = @[],
+              returns: Ident, generics: FieldSeq = @[]): Proc =
   result.identifier = identifier
   result.params = params
   result.returns = returns
@@ -209,7 +209,7 @@ proc newProc*(node: NimNode): Proc =
       case node[2].kind:
         of nnkGenericParams:
           result.generics = newFieldSeq(node[2])
-        else: result.generics = nil
+        else: result.generics = @[]
       let formal_params = node[3]
       case formal_params[0].kind:
         of nnkIdent:
diff --git a/tests/vm/tcomponent.nim b/tests/vm/tcomponent.nim
index e7962e7ab..b509bc5d7 100644
--- a/tests/vm/tcomponent.nim
+++ b/tests/vm/tcomponent.nim
@@ -11,7 +11,7 @@ FOO: blah'''
 
 import macros, sequtils, tables
 import strutils
-import future, meta
+import sugar, meta
 
 type
   Component = object
diff --git a/tests/vm/tseq_badinit.nim b/tests/vm/tseq_badinit.nim
index 15889d60e..5fa223c85 100644
--- a/tests/vm/tseq_badinit.nim
+++ b/tests/vm/tseq_badinit.nim
@@ -22,14 +22,14 @@ template test(typename, default: untyped) =
     result = newSeq[typename]()
     result.add(default)
     result.setLen(3)
-    for i in 0 .. <2:
+    for i in 0 ..< 2:
       result[i] = default
 
   const constval = `abc typename`()
   doAssert(constval == `abc typename`())
 
   proc `arr typename`(): array[4, typename] =
-    for i in 0 .. <2:
+    for i in 0 ..< 2:
       result[i] = default
   const constarr = `arr typename`()
   doAssert(constarr == `arr typename`())
diff --git a/tests/vm/tstringnil.nim b/tests/vm/tstringnil.nim
index 39e4040dc..df408910e 100644
--- a/tests/vm/tstringnil.nim
+++ b/tests/vm/tstringnil.nim
@@ -20,16 +20,16 @@ proc buildSuiteContents(suiteName, suiteDesc, suiteBloc: NimNode): tuple[tests:
 
       var testObj = SuiteTest()
       if suiteName.kind == nnkNilLit:
-        testObj.suiteName = nil
+        testObj.suiteName = ""
       else:
         testObj.suiteName = $suiteName
       if suiteDesc.kind == nnkNilLit:
-        testObj.suiteDesc = nil
+        testObj.suiteDesc = ""
       else:
         testObj.suiteDesc = suiteDesc.strVal
       testObj.testName = $child[1] # should not ever be nil
       if child[2].kind == nnkNilLit:
-        testObj.testDesc = nil
+        testObj.testDesc = ""
       else:
         testObj.testDesc = child[2].strVal
       testObj.testBlock = child[1]
@@ -46,5 +46,5 @@ macro suite(suiteName, suiteDesc, suiteBloc: untyped): typed =
 
 # Test above
 suite basics, "Description of such":
-  test(t5, nil):
+  test(t5, ""):
     assert false
diff --git a/tools/dochack/dochack.nim b/tools/dochack/dochack.nim
index 8cc27b6eb..61c61225d 100644
--- a/tools/dochack/dochack.nim
+++ b/tools/dochack/dochack.nim
@@ -28,7 +28,7 @@ proc parentWith(x: Element; tag: cstring): Element =
 
 proc extractItems(x: Element; items: var seq[Element]) =
   if x == nil: return
-  if x.nodeName == "A":
+  if x.nodeName == cstring"A":
     items.add x
   else:
     for i in 0..<x.len:
@@ -99,19 +99,19 @@ proc isWhitespace(text: cstring): bool {.asmNoStackFrame.} =
   """.}
 
 proc isWhitespace(x: Element): bool =
-  x.nodeName == "#text" and x.textContent.isWhitespace or
-    x.nodeName == "#comment"
+  x.nodeName == cstring"#text" and x.textContent.isWhitespace or
+    x.nodeName == cstring"#comment"
 
 proc toToc(x: Element; father: TocEntry) =
-  if x.nodeName == "UL":
+  if x.nodeName == cstring"UL":
     let f = TocEntry(heading: nil, kids: @[], sortId: father.kids.len)
     var i = 0
     while i < x.len:
       var nxt = i+1
       while nxt < x.len and x[nxt].isWhitespace:
         inc nxt
-      if nxt < x.len and x[i].nodeName == "LI" and x[i].len == 1 and
-          x[nxt].nodeName == "UL":
+      if nxt < x.len and x[i].nodeName == cstring"LI" and x[i].len == 1 and
+          x[nxt].nodeName == cstring"UL":
         let e = TocEntry(heading: x[i][0], kids: @[], sortId: f.kids.len)
         let it = x[nxt]
         for j in 0..<it.len:
@@ -124,11 +124,11 @@ proc toToc(x: Element; father: TocEntry) =
     father.kids.add f
   elif isWhitespace(x):
     discard
-  elif x.nodeName == "LI":
+  elif x.nodeName == cstring"LI":
     var idx: seq[int] = @[]
     for i in 0 ..< x.len:
       if not x[i].isWhitespace: idx.add i
-    if idx.len == 2 and x[idx[1]].nodeName == "UL":
+    if idx.len == 2 and x[idx[1]].nodeName == cstring"UL":
       let e = TocEntry(heading: x[idx[0]], kids: @[],
                        sortId: father.kids.len)
       let it = x[idx[1]]
@@ -147,9 +147,9 @@ proc tocul(x: Element): Element =
   result = tree("UL")
   for i in 0..<x.len:
     let it = x[i]
-    if it.nodeName == "LI":
+    if it.nodeName == cstring"LI":
       result.add it.clone
-    elif it.nodeName == "UL":
+    elif it.nodeName == cstring"UL":
       result.add tocul(it)
 
 proc getSection(toc: Element; name: cstring): Element =
@@ -223,7 +223,7 @@ proc groupBy*(value: cstring) {.exportc.} =
     let ntoc = buildToc(tt, types, procs)
     let x = toHtml(ntoc, isRoot=true)
     alternative = tree("DIV", x)
-  if value == "type":
+  if value == cstring"type":
     replaceById("tocRoot", alternative)
   else:
     replaceById("tocRoot", tree("DIV"))
@@ -236,7 +236,7 @@ var
 template normalize(x: cstring): cstring = x.toLower.replace("_", "")
 
 proc dosearch(value: cstring): Element =
-  if db.isNil:
+  if db.len == 0:
     var stuff: Element
     {.emit: """
     var request = new XMLHttpRequest();
@@ -261,7 +261,7 @@ proc dosearch(value: cstring): Element =
   var matches: seq[(Element, int)] = @[]
   for i in 0..<db.len:
     let c = contents[i]
-    if c == "Examples" or c == "PEG construction":
+    if c == cstring"Examples" or c == cstring"PEG construction":
     # Some manual exclusions.
     # Ideally these should be fixed in the index to be more
     # descriptive of what they are.
@@ -288,7 +288,7 @@ proc search*() {.exportc.} =
   proc wrapper() =
     let elem = getElementById("searchInput")
     let value = elem.value
-    if value != "":
+    if value.len != 0:
       if oldtoc.isNil:
         oldtoc = getElementById("tocRoot")
       let results = dosearch(value)
diff --git a/tools/dochack/karax.nim b/tools/dochack/karax.nim
index d9619992b..020fd37d1 100644
--- a/tools/dochack/karax.nim
+++ b/tools/dochack/karax.nim
@@ -112,7 +112,8 @@ proc tree*(tag: string; attrs: openarray[(string, string)];
 proc text*(s: string): Element = cast[Element](document.createTextNode(s))
 proc text*(s: cstring): Element = cast[Element](document.createTextNode(s))
 proc add*(parent, kid: Element) =
-  if parent.nodeName == "TR" and (kid.nodeName == "TD" or kid.nodeName == "TH"):
+  if parent.nodeName == cstring"TR" and (
+      kid.nodeName == cstring"TD" or kid.nodeName == cstring"TH"):
     let k = document.createElement("TD")
     appendChild(k, kid)
     appendChild(parent, k)
@@ -260,7 +261,7 @@ proc table*(class="", kids: varargs[Element]): Element =
 proc tr*(kids: varargs[Element]): Element =
   result = tag("tr")
   for k in kids:
-    if k.nodeName == "TD" or k.nodeName == "TH":
+    if k.nodeName == cstring"TD" or k.nodeName == cstring"TH":
       result.add k
     else:
       result.add td(k)
diff --git a/tools/niminst/deinstall.tmpl b/tools/niminst/deinstall.tmpl
index bba310f76..8b4477369 100644
--- a/tools/niminst/deinstall.tmpl
+++ b/tools/niminst/deinstall.tmpl
@@ -17,7 +17,7 @@ if [ $# -eq 1 ] ; then
       ;;
     "/usr/bin")
       bindir=/usr/bin
-      configdir=/etc
+      configdir=/etc/?proj
       libdir=/usr/lib/?proj
       docdir=/usr/share/?proj/doc
       datadir=/usr/share/?proj/data
@@ -25,7 +25,7 @@ if [ $# -eq 1 ] ; then
       ;;
     "/usr/local/bin")
       bindir=/usr/local/bin
-      configdir=/etc
+      configdir=/etc/?proj
       libdir=/usr/local/lib/?proj
       docdir=/usr/local/share/?proj/doc
       datadir=/usr/local/share/?proj/data
diff --git a/tools/niminst/install.tmpl b/tools/niminst/install.tmpl
index 91504891d..a78914ecd 100644
--- a/tools/niminst/install.tmpl
+++ b/tools/niminst/install.tmpl
@@ -30,7 +30,7 @@ if [ $# -eq 1 ] ; then
       ;;
     "/usr/bin")
       bindir=/usr/bin
-      configdir=/etc
+      configdir=/etc/?proj
       libdir=/usr/lib/?proj
       docdir=/usr/share/?proj/doc
       datadir=/usr/share/?proj/data
@@ -38,7 +38,7 @@ if [ $# -eq 1 ] ; then
       ;;
     "/usr/local/bin")
       bindir=/usr/local/bin
-      configdir=/etc
+      configdir=/etc/?proj
       libdir=/usr/local/lib/?proj
       docdir=/usr/local/share/?proj/doc
       datadir=/usr/local/share/?proj/data
@@ -70,6 +70,7 @@ if [ $# -eq 1 ] ; then
 
   mkdir -p $libdir
   mkdir -p $docdir
+  mkdir -p $configdir
   mkdir -p $nimbleDir/
   echo "copying files..."
 #var createdDirs = newStringTable()
diff --git a/tools/nimweb.nim b/tools/nimweb.nim
index e74b081ea..61cae5170 100644
--- a/tools/nimweb.nim
+++ b/tools/nimweb.nim
@@ -303,14 +303,10 @@ proc mexec(cmds: openarray[string], processors: int) =
 proc buildDocSamples(c: var TConfigData, destPath: string) =
   ## Special case documentation sample proc.
   ##
-  ## The docgen sample needs to be generated twice with different commands, so
-  ## it didn't make much sense to integrate into the existing generic
-  ## documentation builders.
-  const src = "doc"/"docgen_sample.nim"
+  ## TODO: consider integrating into the existing generic documentation builders
+  ## now that we have a single `doc` command.
   exec(findNim(c) & " doc $# -o:$# $#" %
-    [c.nimArgs, destPath / "docgen_sample.html", src])
-  exec(findNim(c) & " doc2 $# -o:$# $#" %
-    [c.nimArgs, destPath / "docgen_sample2.html", src])
+    [c.nimArgs, destPath / "docgen_sample.html", "doc" / "docgen_sample.nim"])
 
 proc pathPart(d: string): string = splitFile(d).dir.replace('\\', '/')
 
diff --git a/tools/vccenv/vccenv.nim b/tools/vccenv/vccenv.nim
index a335efd10..a5af7994e 100644
--- a/tools/vccenv/vccenv.nim
+++ b/tools/vccenv/vccenv.nim
@@ -19,7 +19,7 @@ proc getVsComnToolsPath*(): TaintedString =
       return vsComnToolsEnvVal
 
 proc getVccEnv*(platform: string, windowsStoreSdk: bool = false,
-                sdkVersion: string = nil): StringTableRef =
+                sdkVersion: string = ""): StringTableRef =
   var comSpecCommandString = getEnv comSpecEnvKey
   if comSpecCommandString.len == 0:
     comSpecCommandString = "cmd"
diff --git a/tools/vccenv/vccexe.nim b/tools/vccenv/vccexe.nim
index 892246830..e86e14c0f 100644
--- a/tools/vccenv/vccexe.nim
+++ b/tools/vccenv/vccexe.nim
@@ -5,14 +5,14 @@ when defined(release):
 else:
   let vccOptions = {poEchoCmd, poParentStreams}
 
-const 
+const
   platformPrefix = "--platform"
   winstorePrefix = "--winstore"
   sdkversionPrefix = "--sdkversion"
 
   platformSepIdx = platformPrefix.len
   sdkversionSepIdx = sdkversionPrefix.len
-  
+
   HelpText = """
 +-----------------------------------------------------------------+
 |         Microsoft C/C++ compiler wrapper for Nim                |
@@ -26,7 +26,7 @@ Options:
                       <arch>: x86 | amd64 | arm | x86_amd64 | x86_arm | amd64_x86 | amd64_arm
   --winstore          Use Windows Store (rather than desktop) development tools
   --sdkversion:<v>    Use a specific Windows SDK version:
-                      <v> is either the full Windows 10 SDK version number or 
+                      <v> is either the full Windows 10 SDK version number or
                       "8.1" to use the windows 8.1 SDK
 
 Other command line arguments are passed on to the
@@ -34,8 +34,8 @@ Microsoft C/C++ compiler for the specified SDK toolset
 """
 
 when isMainModule:
-  var platformArg: string = nil
-  var sdkVersionArg: string = nil
+  var platformArg: string = ""
+  var sdkVersionArg: string = ""
   var storeArg: bool = false
 
   var clArgs: seq[TaintedString] = @[]
@@ -54,7 +54,7 @@ when isMainModule:
         echo HelpText
       clArgs.add(wargv)
 
-  var vccEnvStrTab = getVccEnv(platformArg, storeArg, sdkVersionArg)  
+  var vccEnvStrTab = getVccEnv(platformArg, storeArg, sdkVersionArg)
   if vccEnvStrTab != nil:
     for vccEnvKey, vccEnvVal in vccEnvStrTab:
       putEnv(vccEnvKey, vccEnvVal)
diff --git a/tools/website.tmpl b/tools/website.tmpl
index f9b1a219a..9e5eb2460 100644
--- a/tools/website.tmpl
+++ b/tools/website.tmpl
@@ -203,7 +203,7 @@ runForever()
 #  if currentTab == "index":
   <script src="${rootDir}assets/index.js"></script>
 # end if
-#  if c.gaId != nil:
+#  if c.gaId.len != 0:
   <script>
     (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
     (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),