summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--.travis.yml1
-rw-r--r--appveyor.yml1
-rw-r--r--compiler/ast.nim2
-rw-r--r--compiler/ccgexprs.nim126
-rw-r--r--compiler/ccgstmts.nim6
-rw-r--r--compiler/ccgtrav.nim16
-rw-r--r--compiler/ccgtypes.nim43
-rw-r--r--compiler/cgen.nim19
-rw-r--r--compiler/commands.nim9
-rw-r--r--compiler/condsyms.nim5
-rw-r--r--compiler/docgen.nim14
-rw-r--r--compiler/evalffi.nim16
-rw-r--r--compiler/extccomp.nim15
-rw-r--r--compiler/installer.ini5
-rw-r--r--compiler/jsgen.nim2
-rw-r--r--compiler/lambdalifting.nim2
-rw-r--r--compiler/lowerings.nim20
-rw-r--r--compiler/msgs.nim2
-rw-r--r--compiler/nimblecmd.nim112
-rw-r--r--compiler/nimconf.nim2
-rw-r--r--compiler/options.nim6
-rw-r--r--compiler/parser.nim13
-rw-r--r--compiler/passes.nim6
-rw-r--r--compiler/platform.nim15
-rw-r--r--compiler/pragmas.nim9
-rw-r--r--compiler/renderer.nim2
-rw-r--r--compiler/reorder.nim102
-rw-r--r--compiler/ropes.nim1
-rw-r--r--compiler/semexprs.nim65
-rw-r--r--compiler/semmagic.nim36
-rw-r--r--compiler/semobjconstr.nim7
-rw-r--r--compiler/semtempl.nim4
-rw-r--r--compiler/semtypes.nim36
-rw-r--r--compiler/sigmatch.nim4
-rw-r--r--compiler/testability.nim5
-rw-r--r--compiler/vm.nim62
-rw-r--r--compiler/vmdef.nim5
-rw-r--r--compiler/vmdeps.nim3
-rw-r--r--compiler/vmgen.nim36
-rw-r--r--compiler/wordrecg.nim4
-rw-r--r--config/nim.cfg55
-rw-r--r--doc/advopt.txt2
-rw-r--r--doc/lib.rst3
-rw-r--r--doc/manual/ffi.txt6
-rw-r--r--doc/manual/lexing.txt2
-rw-r--r--doc/manual/procs.txt12
-rw-r--r--doc/nimc.rst27
-rw-r--r--doc/tut1.rst9
-rw-r--r--doc/tut2.rst10
-rw-r--r--examples/allany.nim4
-rw-r--r--koch.nim88
-rw-r--r--lib/core/locks.nim5
-rw-r--r--lib/core/macros.nim150
-rw-r--r--lib/core/typeinfo.nim2
-rw-r--r--lib/deprecated/pure/sockets.nim37
-rw-r--r--lib/impure/nre/private/util.nim2
-rw-r--r--lib/js/jsffi.nim7
-rw-r--r--lib/packages/docutils/rstgen.nim4
-rw-r--r--lib/posix/posix.nim2
-rw-r--r--lib/posix/posix_other.nim11
-rw-r--r--lib/posix/termios.nim2
-rw-r--r--lib/pure/asyncdispatch.nim36
-rw-r--r--lib/pure/asyncfile.nim21
-rw-r--r--lib/pure/asyncfutures.nim (renamed from lib/pure/includes/asyncfutures.nim)214
-rw-r--r--lib/pure/asynchttpserver.nim4
-rw-r--r--lib/pure/asyncmacro.nim14
-rw-r--r--lib/pure/asyncnet.nim4
-rw-r--r--lib/pure/asyncstreams.nim105
-rw-r--r--lib/pure/basic2d.nim6
-rw-r--r--lib/pure/basic3d.nim4
-rw-r--r--lib/pure/collections/critbits.nim7
-rw-r--r--lib/pure/collections/deques.nim2
-rw-r--r--lib/pure/collections/intsets.nim183
-rw-r--r--lib/pure/collections/rtarrays.nim2
-rw-r--r--lib/pure/collections/sequtils.nim2
-rw-r--r--lib/pure/collections/sharedstrings.nim4
-rw-r--r--lib/pure/collections/sharedtables.nim2
-rw-r--r--lib/pure/collections/tableimpl.nim4
-rw-r--r--lib/pure/collections/tables.nim18
-rw-r--r--lib/pure/colors.nim12
-rw-r--r--lib/pure/concurrency/threadpool.nim13
-rw-r--r--lib/pure/httpclient.nim2
-rw-r--r--lib/pure/includes/osenv.nim159
-rw-r--r--lib/pure/includes/oserr.nim135
-rw-r--r--lib/pure/ioselectors.nim2
-rw-r--r--lib/pure/memfiles.nim6
-rw-r--r--lib/pure/nativesockets.nim2
-rw-r--r--lib/pure/net.nim2
-rw-r--r--lib/pure/os.nim336
-rw-r--r--lib/pure/ospaths.nim1130
-rw-r--r--lib/pure/osproc.nim34
-rw-r--r--lib/pure/parsecfg.nim10
-rw-r--r--lib/pure/parsesql.nim88
-rw-r--r--lib/pure/pegs.nim22
-rw-r--r--lib/pure/random.nim4
-rw-r--r--lib/pure/streams.nim4
-rw-r--r--lib/pure/strscans.nim25
-rw-r--r--lib/pure/strutils.nim35
-rw-r--r--lib/pure/subexes.nim4
-rw-r--r--lib/pure/terminal.nim11
-rw-r--r--lib/pure/times.nim17
-rw-r--r--lib/pure/unittest.nim55
-rw-r--r--lib/pure/uri.nim4
-rw-r--r--lib/pure/xmltree.nim2
-rw-r--r--lib/system.nim110
-rw-r--r--lib/system/atomics.nim2
-rw-r--r--lib/system/cellsets.nim5
-rw-r--r--lib/system/channels.nim45
-rw-r--r--lib/system/deepcopy.nim6
-rw-r--r--lib/system/excpt.nim7
-rw-r--r--lib/system/gc.nim36
-rw-r--r--lib/system/gc_common.nim32
-rw-r--r--lib/system/gc_ms.nim26
-rw-r--r--lib/system/gc_regions.nim (renamed from lib/system/gc_stack.nim)298
-rw-r--r--lib/system/jssys.nim6
-rw-r--r--lib/system/mmdisp.nim10
-rw-r--r--lib/system/platforms.nim8
-rw-r--r--lib/system/sysio.nim8
-rw-r--r--lib/system/sysstr.nim11
-rw-r--r--lib/system/threads.nim140
-rw-r--r--lib/system/widestrs.nim2
-rw-r--r--lib/upcoming/asyncdispatch.nim33
-rw-r--r--lib/wrappers/openssl.nim4
-rw-r--r--readme.md34
-rw-r--r--tests/alias/talias.nim8
-rw-r--r--tests/arithm/tshr.nim22
-rw-r--r--tests/array/tunchecked.nim5
-rw-r--r--tests/assert/tuserassert.nim2
-rw-r--r--tests/async/tasync_gcsafe.nim36
-rw-r--r--tests/async/tasync_gcunsafe.nim30
-rw-r--r--tests/async/tasyncrecursion.nim1
-rw-r--r--tests/async/tcallbacks.nim20
-rw-r--r--tests/bind/mbind3.nim2
-rw-r--r--tests/bind/tbind1.nim2
-rw-r--r--tests/bind/tbind2.nim2
-rw-r--r--tests/ccgbugs/t6279.nim9
-rw-r--r--tests/ccgbugs/taddhigh.nim5
-rw-r--r--tests/ccgbugs/tnocodegen_for_compiletime.nim2
-rw-r--r--tests/ccgbugs/tuple_canon.nim2
-rw-r--r--tests/ccgbugs/twrongrefcounting.nim22
-rw-r--r--tests/closure/tboehmdeepcopy.nim18
-rw-r--r--tests/closure/tdonotation.nim11
-rw-r--r--tests/closure/tmacrobust1512.nim182
-rw-r--r--tests/collections/ttableconstr.nim2
-rw-r--r--tests/compiles/tcompiles.nim12
-rw-r--r--tests/concepts/matrix.nim5
-rw-r--r--tests/concepts/t3330.nim8
-rw-r--r--tests/concepts/tmanual.nim5
-rw-r--r--tests/concepts/tmodifiersinplace.nim30
-rw-r--r--tests/concepts/tstackconcept.nim3
-rw-r--r--tests/constructors/t5965_1.nim10
-rw-r--r--tests/constructors/t5965_2.nim10
-rw-r--r--tests/cpp/ttypeinfo.nim17
-rw-r--r--tests/discard/tdiscardable.nim2
-rw-r--r--tests/exception/tdefer1.nim4
-rw-r--r--tests/exception/tfinally3.nim7
-rw-r--r--tests/fields/tfields_in_template.nim2
-rw-r--r--tests/float/tfloat7.nim2
-rw-r--r--tests/generics/tgeneric3.nim2
-rw-r--r--tests/generics/tgenerictmpl.nim2
-rw-r--r--tests/generics/tgenerictmpl2.nim2
-rw-r--r--tests/generics/tunique_type.nim4
-rw-r--r--tests/gensym/tgensym.nim2
-rw-r--r--tests/gensym/tgensymgeneric.nim25
-rw-r--r--tests/iter/tclosureiters.nim10
-rw-r--r--tests/js/tjsffi.nim4
-rw-r--r--tests/js/ttryexceptnewsyntax.nim13
-rw-r--r--tests/lexer/tind1.nim6
-rw-r--r--tests/macros/tbindsym.nim2
-rw-r--r--tests/macros/tbugs.nim12
-rw-r--r--tests/macros/tcomplexecho.nim4
-rw-r--r--tests/macros/tdebugstmt.nim2
-rw-r--r--tests/macros/tdumpast2.nim2
-rw-r--r--tests/macros/tdumpastgen.nim25
-rw-r--r--tests/macros/texprcolonexpr.nim2
-rw-r--r--tests/macros/tgenericparams.nim13
-rw-r--r--tests/macros/tgensym.nim2
-rw-r--r--tests/macros/tgentemplates.nim2
-rw-r--r--tests/macros/tgettype.nim15
-rw-r--r--tests/macros/tgettypeinst.nim2
-rw-r--r--tests/macros/tidgen.nim2
-rw-r--r--tests/macros/tmacro1.nim3
-rw-r--r--tests/macros/tmacro2.nim2
-rw-r--r--tests/macros/tmacro3.nim5
-rw-r--r--tests/macros/tmacro4.nim3
-rw-r--r--tests/macros/tmacro5.nim8
-rw-r--r--tests/macros/tmacro_in_template.nim4
-rw-r--r--tests/macros/tmacroaspragma.nim3
-rw-r--r--tests/macros/tmacros1.nim4
-rw-r--r--tests/macros/tmacrostmt.nim4
-rw-r--r--tests/macros/tmacrotypes.nim2
-rw-r--r--tests/macros/tnewlit.nim9
-rw-r--r--tests/macros/tnodecompare.nim6
-rw-r--r--tests/macros/tquotewords.nim5
-rw-r--r--tests/macros/trecmacro.nim2
-rw-r--r--tests/macros/treturnsempty.nim3
-rw-r--r--tests/macros/tsametype.nim2
-rw-r--r--tests/macros/tstaticparamsmacro.nim10
-rw-r--r--tests/macros/tstringinterp.nim7
-rw-r--r--tests/macros/ttryparseexpr.nim2
-rw-r--r--tests/macros/tvarnimnode.nim2
-rw-r--r--tests/macros/typesapi.nim2
-rw-r--r--tests/macros/typesapi2.nim5
-rw-r--r--tests/manyloc/keineschweine/keineschweine.nim5
-rw-r--r--tests/metatype/tbindtypedesc.nim4
-rw-r--r--tests/metatype/tmatrix.nim2
-rw-r--r--tests/metatype/tsemistatic.nim2
-rw-r--r--tests/metatype/tstatic_overloading.nim2
-rw-r--r--tests/metatype/tstaticparammacro.nim10
-rw-r--r--tests/metatype/tstaticparams.nim2
-rw-r--r--tests/metatype/ttypedesc1.nim2
-rw-r--r--tests/metatype/ttypedesc2.nim3
-rw-r--r--tests/metatype/tymatrix.nim2
-rw-r--r--tests/method/tmapper.nim2
-rw-r--r--tests/misc/thallo.nim5
-rw-r--r--tests/misc/tints.nim2
-rw-r--r--tests/misc/tsimplesort.nim4
-rw-r--r--tests/modules/tmismatchedvisibility.nim2
-rw-r--r--tests/modules/treorder.nim46
-rw-r--r--tests/newconfig/tfoo.nims2
-rw-r--r--tests/nimble/nimbleDir/linkedPkgs/pkgA-0.1.0/pkgA.nimble-link2
-rw-r--r--tests/nimble/nimbleDir/linkedPkgs/pkgB-#head/pkgB.nimble-link2
-rw-r--r--tests/nimble/nimbleDir/linkedPkgs/pkgB-0.1.0/pkgB.nimble-link2
-rw-r--r--tests/nimble/nimbleDir/simplePkgs/pkgA-0.1.0/pkgA.nimble0
-rw-r--r--tests/nimble/nimbleDir/simplePkgs/pkgA-0.1.0/pkgA/module.nim1
-rw-r--r--tests/nimble/nimbleDir/simplePkgs/pkgB-#head/pkgB.nimble0
-rw-r--r--tests/nimble/nimbleDir/simplePkgs/pkgB-#head/pkgB/module.nim1
-rw-r--r--tests/nimble/nimbleDir/simplePkgs/pkgB-0.1.0/pkgB.nimble0
-rw-r--r--tests/nimble/nimbleDir/simplePkgs/pkgB-0.1.0/pkgB/module.nim1
-rw-r--r--tests/nimble/nimbleDir/simplePkgs/pkgC-#aa11/pkgC.nimble0
-rw-r--r--tests/nimble/nimbleDir/simplePkgs/pkgC-#aa11/pkgC/module.nim1
-rw-r--r--tests/nimble/nimbleDir/simplePkgs/pkgC-#head/pkgC.nimble0
-rw-r--r--tests/nimble/nimbleDir/simplePkgs/pkgC-#head/pkgC/module.nim1
-rw-r--r--tests/nimble/readme.md2
-rw-r--r--tests/nimble/tnimblepath.nim11
-rw-r--r--tests/nimble/tnimblepathlink.nim9
-rw-r--r--tests/objects/tobjconstr.nim38
-rw-r--r--tests/objects/tobject3.nim51
-rw-r--r--tests/objects/toop1.nim2
-rw-r--r--tests/osproc/tworkingdir.nim6
-rw-r--r--tests/overload/tparams_after_varargs.nim8
-rw-r--r--tests/overload/tspec.nim2
-rw-r--r--tests/overload/tstmtoverload.nim6
-rw-r--r--tests/parallel/tblocking_channel.nim37
-rw-r--r--tests/parallel/tconvexhull.nim4
-rw-r--r--tests/parallel/tguard1.nim2
-rw-r--r--tests/parallel/tguard2.nim2
-rw-r--r--tests/parallel/tptr_to_ref.nim2
-rw-r--r--tests/parser/toprprec.nim11
-rw-r--r--tests/parser/tpostexprblocks.nim394
-rw-r--r--tests/parser/tstrongspaces.nim10
-rw-r--r--tests/pragmas/tsym_as_pragma.nim2
-rw-r--r--tests/pragmas/tused.nim2
-rw-r--r--tests/range/compilehelpers.nim5
-rw-r--r--tests/readme.md62
-rw-r--r--tests/readme.txt13
-rw-r--r--tests/showoff/tdrdobbs_examples.nim6
-rw-r--r--tests/showoff/tformatopt.nim6
-rw-r--r--tests/showoff/thtml1.nim2
-rw-r--r--tests/showoff/thtml2.nim10
-rw-r--r--tests/showoff/tonce.nim2
-rw-r--r--tests/showoff/tquasiquote.nim4
-rw-r--r--tests/specialops/tdotops.nim3
-rw-r--r--tests/stdlib/tlocks.nim10
-rw-r--r--tests/stdlib/tmarshal.nim2
-rw-r--r--tests/stdlib/tnre.nim17
-rw-r--r--tests/stdlib/tparsecfg.nim23
-rw-r--r--tests/stdlib/tpegs.nim22
-rw-r--r--tests/stdlib/uselocks.nim11
-rw-r--r--tests/template/annotate.nim2
-rw-r--r--tests/template/mcan_access_hidden_field.nim3
-rw-r--r--tests/template/t2do.nim2
-rw-r--r--tests/template/t_otemplates.nim8
-rw-r--r--tests/template/tdefault_nil.nim2
-rw-r--r--tests/template/thygienictempl.nim4
-rw-r--r--tests/template/tissue909.nim2
-rw-r--r--tests/template/tissue993.nim7
-rw-r--r--tests/template/tit.nim2
-rw-r--r--tests/template/tparams_gensymed.nim2
-rw-r--r--tests/template/tprefer_immediate.nim6
-rw-r--r--tests/template/tscope.nim2
-rw-r--r--tests/template/tstmt_semchecked_twice.nim2
-rw-r--r--tests/template/tsymchoicefield.nim3
-rw-r--r--tests/template/ttempl3.nim12
-rw-r--r--tests/template/ttempl4.nim3
-rw-r--r--tests/template/ttempl5.nim5
-rw-r--r--tests/template/twhen_gensym.nim2
-rw-r--r--tests/template/twrongmapit.nim4
-rw-r--r--tests/template/twrongsymkind.nim2
-rw-r--r--tests/template/utemplates.nim15
-rw-r--r--tests/testament/categories.nim2
-rw-r--r--tests/testament/tester.nim17
-rw-r--r--tests/trmacros/targlist.nim2
-rw-r--r--tests/trmacros/tcse.nim4
-rw-r--r--tests/trmacros/tmatrix.nim2
-rw-r--r--tests/trmacros/tnoalias.nim2
-rw-r--r--tests/trmacros/tnoalias2.nim2
-rw-r--r--tests/trmacros/tnoendlessrec.nim2
-rw-r--r--tests/trmacros/tor.nim8
-rw-r--r--tests/trmacros/tpartial.nim4
-rw-r--r--tests/trmacros/tstar.nim2
-rw-r--r--tests/trmacros/tstmtlist.nim2
-rw-r--r--tests/tuples/tuple_with_nil.nim2
-rw-r--r--tests/typerel/temptynode.nim2
-rw-r--r--tests/typerel/tsymchoice_for_expr.nim2
-rw-r--r--tests/typerel/tvarargsexpr.nim5
-rw-r--r--tests/types/taliasassignment.nim50
-rw-r--r--tests/types/taliasbugs.nim158
-rw-r--r--tests/types/taliasinequality.nim66
-rw-r--r--tests/types/tauto_canbe_void.nim2
-rw-r--r--tests/types/tisopr.nim4
-rw-r--r--tests/types/typeof_produces_alias.nim25
-rw-r--r--tests/untestable/readme.markdown2
-rw-r--r--tests/usingstmt/tusingstatement.nim4
-rw-r--r--tests/vm/tanonproc.nim2
-rw-r--r--tests/vm/tasmparser.nim70
-rw-r--r--tests/vm/tcomponent.nim6
-rw-r--r--tests/vm/teval1.nim4
-rw-r--r--tests/vm/texcl.nim6
-rw-r--r--tests/vm/tmitems.nim18
-rw-r--r--tests/vm/tseq_badinit.nim58
-rw-r--r--tests/vm/tstaticprintseq.nim6
-rw-r--r--tests/vm/tstringnil.nim2
-rw-r--r--tests/vm/ttouintconv.nim14
-rw-r--r--tests/vm/twrongconst.nim2
-rw-r--r--tests/vm/tzero_extend.nim44
-rw-r--r--tools/nim.bash-completion47
-rw-r--r--tools/nim.zsh-completion76
-rw-r--r--tools/niminst/buildsh.tmpl54
-rw-r--r--tools/niminst/makefile.tmpl13
-rw-r--r--tools/start.bat1
-rw-r--r--tools/winrelease.nim9
-rw-r--r--web/nimblepkglist.nim3
334 files changed, 4638 insertions, 2765 deletions
diff --git a/.gitignore b/.gitignore
index 50fa9a431..5476e173f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,10 @@ dnimcache/
 
 *.o
 !/icons/*.o
+*.obj
+*.ilk
+*.pdb
+*.dll
 *.exe
 *.so
 *.dylib
diff --git a/.travis.yml b/.travis.yml
index b46d7c830..f28eebc2d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -42,4 +42,5 @@ script:
   - nimble install niminst
   - nim c --taintMode:on -d:nimCoroutines tests/testament/tester
   - tests/testament/tester --pedantic all -d:nimCoroutines
+  - ./koch web
   - ./koch csource
diff --git a/appveyor.yml b/appveyor.yml
index f40fb3e4c..be2cc50d3 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -43,7 +43,6 @@ install:
 
 build_script:
   - bin\nim c koch
-  - koch boot
   - koch boot -d:release
   - koch nimble
   - nim e tests/test_nimscript.nims
diff --git a/compiler/ast.nim b/compiler/ast.nim
index ac917346f..0cc4daf22 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -292,6 +292,8 @@ const
 
   sfNoForward* = sfRegister
     # forward declarations are not required (per module)
+  sfReorder* = sfForward
+    # reordering pass is enabled
 
   sfCompileToCpp* = sfInfixCall       # compile the module as C++ code
   sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 0ec16710f..f5c793d29 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -744,14 +744,17 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
   addf(r, ".Field$1", [rope(i)])
   putIntoDest(p, d, tupType.sons[i], r, a.s)
 
-proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope): PSym =
+proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope;
+                      resTyp: ptr PType = nil): PSym =
   var ty = ty
   assert r != nil
   while ty != nil:
     ty = ty.skipTypes(skipPtrs)
     assert(ty.kind in {tyTuple, tyObject})
     result = lookupInRecord(ty.n, field.name)
-    if result != nil: break
+    if result != nil:
+      if resTyp != nil: resTyp[] = ty
+      break
     if not p.module.compileToCpp: add(r, ".Sup")
     ty = ty.sons[0]
   if result == nil: internalError(field.info, "genCheckedRecordField")
@@ -768,8 +771,9 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
     addf(r, ".Field$1", [rope(f.position)])
     putIntoDest(p, d, f.typ, r, a.s)
   else:
-    let field = lookupFieldAgain(p, ty, f, r)
-    if field.loc.r == nil: fillObjectFields(p.module, ty)
+    var rtyp: PType
+    let field = lookupFieldAgain(p, ty, f, r, addr rtyp)
+    if field.loc.r == nil and rtyp != nil: fillObjectFields(p.module, rtyp)
     if field.loc.r == nil: internalError(e.info, "genRecordField 3 " & typeToString(ty))
     addf(r, ".$1", [field.loc.r])
     putIntoDest(p, d, field.typ, r, a.s)
@@ -1057,7 +1061,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
                            "$1 = ($2) #incrSeqV2(&($1)->Sup, sizeof($3));$n"
                          else:
                            "$1 = ($2) #incrSeqV2($1, sizeof($3));$n"
-  var a, b, dest: TLoc
+  var a, b, dest, tmpL: TLoc
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
   let bt = skipTypes(e.sons[2].typ, {tyVar})
@@ -1068,9 +1072,10 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
   #if bt != b.t:
   #  echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
   initLoc(dest, locExpr, bt, OnHeap)
-  dest.r = rfmt(nil, "$1->data[$1->$2]", rdLoc(a), lenField(p))
+  getIntTemp(p, tmpL)
+  lineCg(p, cpsStmts, "$1 = $2->$3++;$n", tmpL.r, rdLoc(a), lenField(p))
+  dest.r = rfmt(nil, "$1->data[$2]", rdLoc(a), tmpL.r)
   genAssignment(p, dest, b, {needToCopy, afDestIsNil})
-  lineCg(p, cpsStmts, "++$1->$2;$n", rdLoc(a), lenField(p))
   gcUsage(e)
 
 proc genReset(p: BProc, n: PNode) =
@@ -1096,9 +1101,9 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
   if a.s == OnHeap and usesNativeGC():
     # use newObjRC1 as an optimization
     if canFormAcycle(a.t):
-      linefmt(p, cpsStmts, "if ($1) #nimGCunref($1);$n", a.rdLoc)
+      linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc)
     else:
-      linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", a.rdLoc)
+      linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", a.rdLoc)
     b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", args)
     linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc)
   else:
@@ -1126,9 +1131,9 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
   initLoc(call, locExpr, dest.t, OnHeap)
   if dest.s == OnHeap and usesNativeGC():
     if canFormAcycle(dest.t):
-      linefmt(p, cpsStmts, "if ($1) #nimGCunref($1);$n", dest.rdLoc)
+      linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc)
     else:
-      linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", dest.rdLoc)
+      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)
   else:
@@ -1170,7 +1175,10 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
 
 proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
   #echo rendertree e, " ", e.isDeepConstExpr
-  if handleConstExpr(p, e, d): return
+  # inheritance in C++ does not allow struct initialization so
+  # we skip this step here:
+  if not p.module.compileToCpp:
+    if handleConstExpr(p, e, d): return
   var tmp: TLoc
   var t = e.typ.skipTypes(abstractInst)
   getTemp(p, t, tmp)
@@ -1378,13 +1386,30 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     useStringh(p.module)
     if op == mHigh: unaryExpr(p, e, d, "($1 ? (strlen($1)-1) : -1)")
     else: unaryExpr(p, e, d, "($1 ? strlen($1) : 0)")
-  of tyString, tySequence:
+  of tyString:
     if not p.module.compileToCpp:
       if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->Sup.len-1) : -1)")
       else: unaryExpr(p, e, d, "($1 ? $1->Sup.len : 0)")
     else:
       if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->len-1) : -1)")
       else: unaryExpr(p, e, d, "($1 ? $1->len : 0)")
+  of tySequence:
+    var a, tmp: TLoc
+    initLocExpr(p, e[1], a)
+    getIntTemp(p, tmp)
+    var frmt: FormatStr
+    if not p.module.compileToCpp:
+      if op == mHigh:
+        frmt = "$1 = ($2 ? ($2->Sup.len-1) : -1);$n"
+      else:
+        frmt = "$1 = ($2 ? $2->Sup.len : 0);$n"
+    else:
+      if op == mHigh:
+        frmt = "$1 = ($2 ? ($2->len-1) : -1);$n"
+      else:
+        frmt = "$1 = ($2 ? $2->len : 0);$n"
+    lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a))
+    putIntoDest(p, d, e.typ, tmp.r)
   of tyArray:
     # YYY: length(sideeffect) is optimized away incorrectly?
     if op == mHigh: putIntoDest(p, d, e.typ, rope(lastOrd(typ)))
@@ -1742,11 +1767,23 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mOrd: genOrd(p, e, d)
   of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray:
     genArrayLen(p, e, d, op)
-  of mXLenStr, mXLenSeq:
+  of mXLenStr:
     if not p.module.compileToCpp:
       unaryExpr(p, e, d, "($1->Sup.len)")
     else:
       unaryExpr(p, e, d, "$1->len")
+  of mXLenSeq:
+    # see 'taddhigh.nim' for why we need to use a temporary here:
+    var a, tmp: TLoc
+    initLocExpr(p, e[1], a)
+    getIntTemp(p, tmp)
+    var frmt: FormatStr
+    if not p.module.compileToCpp:
+      frmt = "$1 = $2->Sup.len;$n"
+    else:
+      frmt = "$1 = $2->len;$n"
+    lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a))
+    putIntoDest(p, d, e.typ, tmp.r)
   of mGCref: unaryStmt(p, e, d, "#nimGCref($1);$n")
   of mGCunref: unaryStmt(p, e, d, "#nimGCunref($1);$n")
   of mSetLengthStr: genSetLengthStr(p, e, d)
@@ -2123,17 +2160,19 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
       # See tests/run/tcnstseq3 for an example that would fail otherwise.
       genAsgn(p, n, fastAsgn=p.prc != nil)
   of nkDiscardStmt:
-    if n.sons[0].kind != nkEmpty:
+    let ex = n[0]
+    if ex.kind != nkEmpty:
       genLineDir(p, n)
       var a: TLoc
-      if n[0].kind in nkCallKinds:
+      if ex.kind in nkCallKinds and (ex[0].kind != nkSym or
+                                     ex[0].sym.magic == mNone):
         # bug #6037: do not assign to a temp in C++ mode:
         incl a.flags, lfSingleUse
-        genCall(p, n[0], a)
+        genCall(p, ex, a)
         if lfSingleUse notin a.flags:
           line(p, cpsStmts, a.r & ";" & tnl)
       else:
-        initLocExpr(p, n.sons[0], a)
+        initLocExpr(p, ex, a)
   of nkAsmStmt: genAsmStmt(p, n)
   of nkTryStmt:
     if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions:
@@ -2201,20 +2240,22 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
   else:
     globalError(info, "cannot create null element for: " & $t.kind)
 
-proc getNullValueAux(p: BProc; obj, cons: PNode, result: var Rope) =
+proc getNullValueAux(p: BProc; t: PType; obj, cons: PNode, result: var Rope; count: var int) =
   case obj.kind
   of nkRecList:
-    for i in countup(0, sonsLen(obj) - 1): getNullValueAux(p, obj.sons[i], cons, result)
+    for i in countup(0, sonsLen(obj) - 1):
+      getNullValueAux(p, t, obj.sons[i], cons, result, count)
   of nkRecCase:
-    getNullValueAux(p, obj.sons[0], cons, result)
+    getNullValueAux(p, t, obj.sons[0], cons, result, count)
     for i in countup(1, sonsLen(obj) - 1):
-      getNullValueAux(p, lastSon(obj.sons[i]), cons, result)
+      getNullValueAux(p, t, lastSon(obj.sons[i]), cons, result, count)
   of nkSym:
-    if not result.isNil: result.add ", "
+    if count > 0: result.add ", "
+    inc count
     let field = obj.sym
     for i in 1..<cons.len:
       if cons[i].kind == nkExprColonExpr:
-        if cons[i][0].sym.name == field.name:
+        if cons[i][0].sym.name.id == field.name.id:
           result.add genConstExpr(p, cons[i][1])
           return
       elif i == field.position:
@@ -2225,14 +2266,32 @@ proc getNullValueAux(p: BProc; obj, cons: PNode, result: var Rope) =
   else:
     localError(cons.info, "cannot create null element for: " & $obj)
 
+proc getNullValueAuxT(p: BProc; orig, t: PType; obj, cons: PNode, result: var Rope; count: var int) =
+  var base = t.sons[0]
+  let oldRes = result
+  if not p.module.compileToCpp: result.add "{"
+  let oldcount = count
+  if base != nil:
+    base = skipTypes(base, skipPtrs)
+    getNullValueAuxT(p, orig, base, base.n, cons, result, count)
+  elif not isObjLackingTypeField(t) and not p.module.compileToCpp:
+    addf(result, "$1", [genTypeInfo(p.module, orig)])
+    inc count
+  getNullValueAux(p, t, obj, cons, result, count)
+  # do not emit '{}' as that is not valid C:
+  if oldcount == count: result = oldres
+  elif not p.module.compileToCpp: result.add "}"
+
 proc genConstObjConstr(p: BProc; n: PNode): Rope =
-  var length = sonsLen(n)
   result = nil
   let t = n.typ.skipTypes(abstractInst)
-  if not isObjLackingTypeField(t) and not p.module.compileToCpp:
-    addf(result, "{$1}", [genTypeInfo(p.module, t)])
-  getNullValueAux(p, t.n, n, result)
-  result = "{$1}$n" % [result]
+  var count = 0
+  #if not isObjLackingTypeField(t) and not p.module.compileToCpp:
+  #  addf(result, "{$1}", [genTypeInfo(p.module, t)])
+  #  inc count
+  getNullValueAuxT(p, t, t, t.n, n, result, count)
+  if p.module.compileToCpp:
+    result = "{$1}$n" % [result]
 
 proc genConstSimpleList(p: BProc, n: PNode): Rope =
   var length = sonsLen(n)
@@ -2279,6 +2338,15 @@ proc genConstExpr(p: BProc, n: PNode): Rope =
     var t = skipTypes(n.typ, abstractInst)
     if t.kind == tySequence:
       result = genConstSeq(p, n, n.typ)
+    elif t.kind == tyProc and t.callConv == ccClosure and not n.sons.isNil and
+         n.sons[0].kind == nkNilLit and n.sons[1].kind == nkNilLit:
+      # this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL}
+      # this behaviour is needed since closure_var = nil must be
+      # expanded to {NIM_NIL,NIM_NIL}
+      # in VM closures are initialized with nkPar(nkNilLit, nkNilLit)
+      # leading to duplicate code like this:
+      # "{NIM_NIL,NIM_NIL}, {NIM_NIL,NIM_NIL}"
+      result = ~"{NIM_NIL,NIM_NIL}"
     else:
       result = genConstSimpleList(p, n)
   of nkObjConstr:
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 378951d9d..8796dd729 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -164,11 +164,11 @@ proc genBreakState(p: BProc, n: PNode) =
   if n.sons[0].kind == nkClosure:
     # XXX this produces quite inefficient code!
     initLocExpr(p, n.sons[0].sons[1], a)
-    lineF(p, cpsStmts, "if (((NI*) $1)[0] < 0) break;$n", [rdLoc(a)])
+    lineF(p, cpsStmts, "if (((NI*) $1)[1] < 0) break;$n", [rdLoc(a)])
   else:
     initLocExpr(p, n.sons[0], a)
-    # the environment is guaranteed to contain the 'state' field at offset 0:
-    lineF(p, cpsStmts, "if ((((NI*) $1.ClE_0)[0]) < 0) break;$n", [rdLoc(a)])
+    # the environment is guaranteed to contain the 'state' field at offset 1:
+    lineF(p, cpsStmts, "if ((((NI*) $1.ClE_0)[1]) < 0) break;$n", [rdLoc(a)])
   #  lineF(p, cpsStmts, "if (($1) < 0) break;$n", [rdLoc(a)])
 
 proc genVarPrototypeAux(m: BModule, sym: PSym)
diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim
index 982f88cbd..fa228ff04 100644
--- a/compiler/ccgtrav.nim
+++ b/compiler/ccgtrav.nim
@@ -72,10 +72,16 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) =
     let arraySize = lengthOrd(typ.sons[0])
     var i: TLoc
     getTemp(p, getSysType(tyInt), i)
+    let oldCode = p.s(cpsStmts)
     linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
             i.r, arraySize.rope)
+    let oldLen = p.s(cpsStmts).len
     genTraverseProc(c, rfmt(nil, "$1[$2]", accessor, i.r), typ.sons[1])
-    lineF(p, cpsStmts, "}$n", [])
+    if p.s(cpsStmts).len == oldLen:
+      # do not emit dummy long loops for faster debug builds:
+      p.s(cpsStmts) = oldCode
+    else:
+      lineF(p, cpsStmts, "}$n", [])
   of tyObject:
     for i in countup(0, sonsLen(typ) - 1):
       var x = typ.sons[i]
@@ -99,10 +105,16 @@ proc genTraverseProcSeq(c: var TTraversalClosure, accessor: Rope, typ: PType) =
   assert typ.kind == tySequence
   var i: TLoc
   getTemp(p, getSysType(tyInt), i)
+  let oldCode = p.s(cpsStmts)
   lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n",
       [i.r, accessor, rope(if c.p.module.compileToCpp: "len" else: "Sup.len")])
+  let oldLen = p.s(cpsStmts).len
   genTraverseProc(c, "$1->data[$2]" % [accessor, i.r], typ.sons[0])
-  lineF(p, cpsStmts, "}$n", [])
+  if p.s(cpsStmts).len == oldLen:
+    # do not emit dummy long loops for faster debug builds:
+    p.s(cpsStmts) = oldCode
+  else:
+    lineF(p, cpsStmts, "}$n", [])
 
 proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash;
                      reason: TTypeInfoReason): Rope =
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 0c81ca814..35d73aac0 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -12,6 +12,7 @@
 # ------------------------- Name Mangling --------------------------------
 
 import sighashes
+from lowerings import createObj
 
 proc isKeyword(w: PIdent): bool =
   # Nim and C++ share some keywords
@@ -122,10 +123,11 @@ const
 
 proc typeName(typ: PType): Rope =
   let typ = typ.skipTypes(irrelevantForBackend)
-  result = if typ.sym != nil and typ.kind in {tyObject, tyEnum}:
-             typ.sym.name.s.mangle.rope
-           else:
-             ~"TY"
+  result =
+    if typ.sym != nil and typ.kind in {tyObject, tyEnum}:
+      rope($typ.kind & '_' & typ.sym.name.s.mangle)
+    else:
+      rope($typ.kind)
 
 proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
   var t = typ
@@ -335,6 +337,7 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope =
     if result == nil: result = cacheGetType(m.typeCache, sig)
 
 proc structOrUnion(t: PType): Rope =
+  let t = t.skipTypes({tyAlias})
   (if tfUnion in t.flags: rope("union") else: rope("struct"))
 
 proc getForwardStructFormat(m: BModule): string =
@@ -473,11 +476,14 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
             if tfPacked notin rectype.flags:
               add(unionBody, "struct {")
             else:
-              addf(unionBody, CC[cCompiler].structStmtFmt,
-                [rope"struct", nil, rope(CC[cCompiler].packedPragma)])
-              add(unionBody, "{")
+              if hasAttribute in CC[cCompiler].props:
+                add(unionBody, "struct __attribute__((__packed__)){" )
+              else:
+                addf(unionBody, "#pragma pack(1)$nstruct{", [])
             add(unionBody, a)
             addf(unionBody, "} $1;$n", [sname])
+            if tfPacked in rectype.flags and hasAttribute notin CC[cCompiler].props:
+              addf(unionBody, "#pragma pack(pop)$n", [])
         else:
           add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check))
       else: internalError("genRecordFieldsAux(record case branch)")
@@ -524,12 +530,16 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
   # declare the record:
   var hasField = false
 
-  var attribute: Rope =
-    if tfPacked in typ.flags: rope(CC[cCompiler].packedPragma)
-    else: nil
+  if tfPacked in typ.flags:
+    if hasAttribute in CC[cCompiler].props:
+      result = structOrUnion(typ) & " __attribute__((__packed__))"
+    else:
+      result = "#pragma pack(1)" & tnl & structOrUnion(typ)
+  else:
+    result = structOrUnion(typ)
 
-  result = ropecg(m, CC[cCompiler].structStmtFmt,
-    [structOrUnion(typ), name, attribute])
+  result.add " "
+  result.add name
 
   if typ.kind == tyObject:
 
@@ -537,7 +547,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
       if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
         appcg(m, result, " {$n", [])
       else:
-        appcg(m, result, " {$n#TNimType* m_type;$n", [name, attribute])
+        appcg(m, result, " {$n#TNimType* m_type;$n", [])
         hasField = true
     elif m.compileToCpp:
       appcg(m, result, " : public $1 {$n",
@@ -556,6 +566,8 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
   else:
     add(result, desc)
   add(result, "};" & tnl)
+  if tfPacked in typ.flags and hasAttribute notin CC[cCompiler].props:
+    result.add "#pragma pack(pop)" & tnl
 
 proc getTupleDesc(m: BModule, typ: PType, name: Rope,
                   check: var IntSet): Rope =
@@ -787,7 +799,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
         add(m.s[cfsTypes], recdesc)
       elif tfIncompleteStruct notin t.flags: addAbiCheck(m, t, result)
   of tySet:
-    result = getTypeName(m, t.lastSon, hashType t.lastSon) & "_Set"
+    result = $t.kind & '_' & getTypeName(m, t.lastSon, hashType t.lastSon)
     m.typeCache[sig] = result
     if not isImportedType(t):
       let s = int(getSize(t))
@@ -1083,7 +1095,8 @@ proc fakeClosureType(owner: PSym): PType =
   result = newType(tyTuple, owner)
   result.rawAddSon(newType(tyPointer, owner))
   var r = newType(tyRef, owner)
-  r.rawAddSon(newType(tyTuple, owner))
+  let obj = createObj(owner, owner.info, final=false)
+  r.rawAddSon(obj)
   result.rawAddSon(r)
 
 type
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 3797a92c2..b618837c7 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -209,7 +209,7 @@ proc genLineDir(p: BProc, t: PNode) =
   if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and
       (p.prc == nil or sfPure notin p.prc.flags):
     if freshLineInfo(p, tt.info):
-      linefmt(p, cpsStmts, "#endb($1, $2);$n",
+      linefmt(p, cpsStmts, "#endb($1, $2);$N",
               line.rope, makeCString(toFilename(tt.info)))
   elif ({optLineTrace, optStackTrace} * p.options ==
       {optLineTrace, optStackTrace}) and
@@ -345,6 +345,15 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
   result.flags = {}
   constructLoc(p, result, not needsInit)
 
+proc getIntTemp(p: BProc, result: var TLoc) =
+  inc(p.labels)
+  result.r = "T" & rope(p.labels) & "_"
+  linefmt(p, cpsLocals, "NI $1;$n", result.r)
+  result.k = locTemp
+  result.s = OnStack
+  result.t = getSysType(tyInt)
+  result.flags = {}
+
 proc initGCFrame(p: BProc): Rope =
   if p.gcFrameId > 0: result = "struct {$1} GCFRAME_;$n" % [p.gcFrameType]
 
@@ -385,7 +394,8 @@ proc assignLocalVar(p: BProc, s: PSym) =
   #assert(s.loc.k == locNone) # not yet assigned
   # this need not be fulfilled for inline procs; they are regenerated
   # for each module that uses them!
-  let decl = localVarDecl(p, s) & ";" & tnl
+  let nl = if optLineDir in gOptions: "" else: tnl
+  let decl = localVarDecl(p, s) & ";" & nl
   line(p, cpsLocals, decl)
   localDebugInfo(p, s)
 
@@ -618,11 +628,11 @@ proc initFrame(p: BProc, procname, filename: Rope): Rope =
   discard cgsym(p.module, "nimFrame")
   if p.maxFrameLen > 0:
     discard cgsym(p.module, "VarSlot")
-    result = rfmt(nil, "\tnimfrs_($1, $2, $3, $4)$N",
+    result = rfmt(nil, "\tnimfrs_($1, $2, $3, $4);$n",
                   procname, filename, p.maxFrameLen.rope,
                   p.blocks[0].frameLen.rope)
   else:
-    result = rfmt(nil, "\tnimfr_($1, $2)$N", procname, filename)
+    result = rfmt(nil, "\tnimfr_($1, $2);$n", procname, filename)
 
 proc deinitFrame(p: BProc): Rope =
   result = rfmt(p.module, "\t#popFrame();$n")
@@ -1302,6 +1312,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
   if b == nil or passes.skipCodegen(n): return
   var m = BModule(b)
   m.initProc.options = initProcOptions(m)
+  softRnl = if optLineDir in gOptions: noRnl else: rnl
   genStmts(m.initProc, n)
 
 proc finishModule(m: BModule) =
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 22e4b5a2c..9781a8af4 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -207,7 +207,7 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool =
     of "generational": result = gSelectedGC == gcGenerational
     of "go":           result = gSelectedGC == gcGo
     of "none":         result = gSelectedGC == gcNone
-    of "stack":        result = gSelectedGC == gcStack
+    of "stack", "regions": result = gSelectedGC == gcRegions
     else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
   of "opt":
     case arg.normalize
@@ -429,9 +429,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     of "none":
       gSelectedGC = gcNone
       defineSymbol("nogc")
-    of "stack":
-      gSelectedGC= gcStack
-      defineSymbol("gcstack")
+    of "stack", "regions":
+      gSelectedGC= gcRegions
+      defineSymbol("gcregions")
     else: localError(info, errNoneBoehmRefcExpectedButXFound, arg)
   of "warnings", "w":
     if processOnOffSwitchOrList({optWarns}, arg, pass, info): listWarnings()
@@ -454,6 +454,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     of "native", "gdb":
       incl(gGlobalOptions, optCDebug)
       gOptions = gOptions + {optLineDir} - {optEndb}
+      defineSymbol("nimTypeNames", nil) # type names are used in gdb pretty printing
       undefSymbol("endb")
     else:
       localError(info, "expected endb|gdb but found " & arg)
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index a4f47ac72..dc97e3648 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -41,7 +41,10 @@ proc isDefined*(symbol: string): bool =
       result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
                             osQnx, osAtari, osAix,
                             osHaiku, osVxWorks, osSolaris, osNetbsd,
-                            osFreebsd, osOpenbsd, osDragonfly, osMacosx}
+                            osFreebsd, osOpenbsd, osDragonfly, osMacosx,
+                            osAndroid}
+    of "linux":
+      result = targetOS in {osLinux, osAndroid}
     of "bsd":
       result = targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly}
     of "emulatedthreadvars":
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 26dd889ce..3f4f7b164 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -462,12 +462,14 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
     var path = n.info.toFullPath
     if path.startsWith(cwd):
       path = path[cwd.len+1 .. ^1].replace('\\', '/')
-    var commit = getConfigVar("git.commit")
-    if commit.len == 0: commit = "master"
-    dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc,
-        ["path", "line", "url", "commit"], [rope path,
-        rope($n.info.line), rope getConfigVar("git.url"),
-        rope commit])])
+    let gitUrl = getConfigVar("git.url")
+    if gitUrl.len > 0:
+      var commit = getConfigVar("git.commit")
+      if commit.len == 0: commit = "master"
+      dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc,
+          ["path", "line", "url", "commit"], [rope path,
+          rope($n.info.line), rope gitUrl,
+          rope commit])])
 
   add(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
     ["name", "header", "desc", "itemID", "header_plain", "itemSym",
diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim
index 987cfaf42..6789df87d 100644
--- a/compiler/evalffi.nim
+++ b/compiler/evalffi.nim
@@ -104,9 +104,9 @@ proc mapCallConv(cc: TCallingConvention, info: TLineInfo): TABI =
   else:
     globalError(info, "cannot map calling convention to FFI")
 
-template rd(T, p: expr): expr {.immediate.} = (cast[ptr T](p))[]
-template wr(T, p, v: expr) {.immediate.} = (cast[ptr T](p))[] = v
-template `+!`(x, y: expr): expr {.immediate.} =
+template rd(T, p: untyped): untyped = (cast[ptr T](p))[]
+template wr(T, p, v: untyped): untyped = (cast[ptr T](p))[] = v
+template `+!`(x, y: untyped): untyped =
   cast[pointer](cast[ByteAddress](x) + y)
 
 proc packSize(v: PNode, typ: PType): int =
@@ -171,7 +171,7 @@ const maxPackDepth = 20
 var packRecCheck = 0
 
 proc pack(v: PNode, typ: PType, res: pointer) =
-  template awr(T, v: expr) {.immediate, dirty.} =
+  template awr(T, v: untyped): untyped =
     wr(T, res, v)
 
   case typ.kind
@@ -302,7 +302,7 @@ proc canonNodeKind(k: TNodeKind): TNodeKind =
   else: result = k
 
 proc unpack(x: pointer, typ: PType, n: PNode): PNode =
-  template aw(k, v, field: expr) {.immediate, dirty.} =
+  template aw(k, v, field: untyped): untyped =
     if n.isNil:
       result = newNode(k)
       result.typ = typ
@@ -326,9 +326,9 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
       result.kind = nkNilLit
       result.typ = typ
 
-  template awi(kind, v: expr) {.immediate, dirty.} = aw(kind, v, intVal)
-  template awf(kind, v: expr) {.immediate, dirty.} = aw(kind, v, floatVal)
-  template aws(kind, v: expr) {.immediate, dirty.} = aw(kind, v, strVal)
+  template awi(kind, v: untyped): untyped = aw(kind, v, intVal)
+  template awf(kind, v: untyped): untyped = aw(kind, v, floatVal)
+  template aws(kind, v: untyped): untyped = aw(kind, v, strVal)
 
   case typ.kind
   of tyBool: awi(nkIntLit, rd(bool, x).ord)
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index ca4f621e4..c47e4fb9a 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -53,7 +53,6 @@ type
                          # used on some platforms
     asmStmtFrmt: string, # format of ASM statement
     structStmtFmt: string, # Format for struct statement
-    packedPragma: string,  # Attribute/pragma to make struct packed (1-byte aligned)
     props: TInfoCCProps] # properties of the C compiler
 
 
@@ -86,7 +85,6 @@ compiler gcc:
     pic: "-fPIC",
     asmStmtFrmt: "asm($1);$n",
     structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
-    packedPragma: "__attribute__((__packed__))",
     props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
             hasAttribute})
 
@@ -129,7 +127,6 @@ compiler vcc:
     pic: "",
     asmStmtFrmt: "__asm{$n$1$n}$n",
     structStmtFmt: "$3$n$1 $2",
-    packedPragma: "#pragma pack(1)",
     props: {hasCpp, hasAssume, hasDeclspec})
 
 # Intel C/C++ Compiler
@@ -166,7 +163,6 @@ compiler lcc:
     pic: "",
     asmStmtFrmt: "_asm{$n$1$n}$n",
     structStmtFmt: "$1 $2",
-    packedPragma: "", # XXX: not supported yet
     props: {})
 
 # Borland C Compiler
@@ -191,7 +187,6 @@ compiler bcc:
     pic: "",
     asmStmtFrmt: "__asm{$n$1$n}$n",
     structStmtFmt: "$1 $2",
-    packedPragma: "", # XXX: not supported yet
     props: {hasCpp})
 
 # Digital Mars C Compiler
@@ -216,7 +211,6 @@ compiler dmc:
     pic: "",
     asmStmtFrmt: "__asm{$n$1$n}$n",
     structStmtFmt: "$3$n$1 $2",
-    packedPragma: "#pragma pack(1)",
     props: {hasCpp})
 
 # Watcom C Compiler
@@ -241,7 +235,6 @@ compiler wcc:
     pic: "",
     asmStmtFrmt: "__asm{$n$1$n}$n",
     structStmtFmt: "$1 $2",
-    packedPragma: "", # XXX: not supported yet
     props: {hasCpp})
 
 # Tiny C Compiler
@@ -266,7 +259,6 @@ compiler tcc:
     pic: "",
     asmStmtFrmt: "__asm{$n$1$n}$n",
     structStmtFmt: "$1 $2",
-    packedPragma: "", # XXX: not supported yet
     props: {hasSwitchRange, hasComputedGoto})
 
 # Pelles C Compiler
@@ -292,7 +284,6 @@ compiler pcc:
     pic: "",
     asmStmtFrmt: "__asm{$n$1$n}$n",
     structStmtFmt: "$1 $2",
-    packedPragma: "", # XXX: not supported yet
     props: {})
 
 # Your C Compiler
@@ -317,7 +308,6 @@ compiler ucc:
     pic: "",
     asmStmtFrmt: "__asm{$n$1$n}$n",
     structStmtFmt: "$1 $2",
-    packedPragma: "", # XXX: not supported yet
     props: {})
 
 const
@@ -687,11 +677,14 @@ proc getLinkCmd(projectfile, objfiles: string): string =
     exefile = quoteShell(exefile)
     let linkOptions = getLinkOptions() & " " &
                       getConfigVar(cCompiler, ".options.linker")
+    var linkTmpl = getConfigVar(cCompiler, ".linkTmpl")
+    if linkTmpl.len == 0:
+      linkTmpl = CC[cCompiler].linkTmpl
     result = quoteShell(result % ["builddll", builddll,
         "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
         "exefile", exefile, "nim", getPrefixDir(), "lib", libpath])
     result.add ' '
-    addf(result, CC[cCompiler].linkTmpl, ["builddll", builddll,
+    addf(result, linkTmpl, ["builddll", builddll,
         "buildgui", buildgui, "options", linkOptions,
         "objfiles", objfiles, "exefile", exefile,
         "nim", quoteShell(getPrefixDir()),
diff --git a/compiler/installer.ini b/compiler/installer.ini
index 8cc89da9f..8052121bf 100644
--- a/compiler/installer.ini
+++ b/compiler/installer.ini
@@ -6,7 +6,7 @@ Name: "Nim"
 Version: "$version"
 Platforms: """
   windows: i386;amd64
-  linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;powerpc;powerpc64el;arm64
+  linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64
   macosx: i386;amd64;powerpc64
   solaris: i386;amd64;sparc;sparc64
   freebsd: i386;amd64
@@ -14,6 +14,7 @@ Platforms: """
   openbsd: i386;amd64
   dragonfly: i386;amd64
   haiku: i386;amd64
+  android: i386;arm;arm64
 """
 
 Authors: "Andreas Rumpf"
@@ -47,7 +48,7 @@ Start: "doc/html/overview.html"
 
 
 [Other]
-Files: "readme.txt;copying.txt"
+Files: "readme.txt;copying.txt;install.txt"
 Files: "makefile"
 Files: "koch.nim"
 Files: "install_nimble.nims"
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 46766cfcf..73e6a9948 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -2340,7 +2340,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
   of nkPragma: genPragma(p, n)
   of nkProcDef, nkMethodDef, nkConverterDef:
     var s = n.sons[namePos].sym
-    if sfExportc in s.flags and compilingLib:
+    if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
       genSym(p, n.sons[namePos], r)
       r.res = nil
   of nkGotoState, nkState:
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index cd2ccfe53..986d8c716 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -142,7 +142,7 @@ proc createStateField(iter: PSym): PSym =
 proc createEnvObj(owner: PSym; info: TLineInfo): PType =
   # YYY meh, just add the state field for every closure for now, it's too
   # hard to figure out if it comes from a closure iterator:
-  result = createObj(owner, info)
+  result = createObj(owner, info, final=false)
   rawAddField(result, createStateField(owner))
 
 proc getIterResult(iter: PSym): PSym =
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 4bd54603d..ce76b63a4 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -109,17 +109,19 @@ proc lowerSwap*(n: PNode; owner: PSym): PNode =
   result.add newFastAsgnStmt(n[1], n[2])
   result.add newFastAsgnStmt(n[2], tempAsNode)
 
-proc createObj*(owner: PSym, info: TLineInfo): PType =
+proc createObj*(owner: PSym, info: TLineInfo; final=true): PType =
   result = newType(tyObject, owner)
-  rawAddSon(result, nil)
-  incl result.flags, tfFinal
+  if final:
+    rawAddSon(result, nil)
+    incl result.flags, tfFinal
+  else:
+    rawAddSon(result, getCompilerProc("RootObj").typ)
   result.n = newNodeI(nkRecList, info)
-  when true:
-    let s = newSym(skType, getIdent("Env_" & info.toFilename),
-                   owner, info)
-    incl s.flags, sfAnon
-    s.typ = result
-    result.sym = s
+  let s = newSym(skType, getIdent("Env_" & info.toFilename),
+                  owner, info)
+  incl s.flags, sfAnon
+  s.typ = result
+  result.sym = s
 
 proc rawAddField*(obj: PType; field: PSym) =
   assert field.kind == skField
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 5f4a0caf1..4a1166f51 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -746,7 +746,7 @@ proc toFileLine*(info: TLineInfo): string {.inline.} =
   result = info.toFilename & ":" & $info.line
 
 proc toFileLineCol*(info: TLineInfo): string {.inline.} =
-  result = info.toFilename & "(" & $info.line & "," & $info.col & ")"
+  result = info.toFilename & "(" & $info.line & ", " & $info.col & ")"
 
 proc `$`*(info: TLineInfo): string = toFileLineCol(info)
 
diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim
index 5e6d843de..8042644b0 100644
--- a/compiler/nimblecmd.nim
+++ b/compiler/nimblecmd.nim
@@ -9,7 +9,7 @@
 
 ## Implements some helper procs for Nimble (Nim's package manager) support.
 
-import parseutils, strutils, strtabs, os, options, msgs
+import parseutils, strutils, strtabs, os, options, msgs, sequtils
 
 proc addPath*(path: string, info: TLineInfo) =
   if not options.searchPaths.contains(path):
@@ -21,51 +21,76 @@ proc versionSplitPos(s: string): int =
   while result > 1 and s[result] != '-': dec result
   if s[result] != '-': result = s.len
 
-const
-  latest = ""
-
-proc `<.`(a, b: string): bool =
-  # wether a has a smaller version than b:
-  if a == latest: return true
-  elif b == latest: return false
-  var i = 0
-  var j = 0
-  var verA = 0
-  var verB = 0
-  while true:
-    let ii = parseInt(a, verA, i)
-    let jj = parseInt(b, verB, j)
-    if ii <= 0 or jj <= 0:
-      # if A has no number and B has but A has no number whatsoever ("#head"),
-      # A is preferred:
-      if ii > 0 and jj <= 0 and j == 0: return true
-      if ii <= 0 and jj > 0 and i == 0: return false
-      # if A has no number left, but B has, B is preferred:  0.8 vs 0.8.3
-      return jj > 0
-    if verA < verB: return true
-    elif verA > verB: return false
-    # else: same version number; continue:
-    inc i, ii
-    inc j, jj
-    if a[i] == '.': inc i
-    if b[j] == '.': inc j
+type
+  Version = distinct string
+
+proc `$`(ver: Version): string {.borrow.}
+
+proc newVersion(ver: string): Version =
+  doAssert(ver.len == 0 or ver[0] in {'#', '\0'} + Digits,
+           "Wrong version: " & ver)
+  return Version(ver)
+
+proc isSpecial(ver: Version): bool =
+  return ($ver).len > 0 and ($ver)[0] == '#'
+
+proc `<`(ver: Version, ver2: Version): bool =
+  ## This is synced from Nimble's version module.
+
+  # Handling for special versions such as "#head" or "#branch".
+  if ver.isSpecial or ver2.isSpecial:
+    if ver2.isSpecial and ($ver2).normalize == "#head":
+      return ($ver).normalize != "#head"
+
+    if not ver2.isSpecial:
+      # `#aa111 < 1.1`
+      return ($ver).normalize != "#head"
+
+  # Handling for normal versions such as "0.1.0" or "1.0".
+  var sVer = string(ver).split('.')
+  var sVer2 = string(ver2).split('.')
+  for i in 0..max(sVer.len, sVer2.len)-1:
+    var sVerI = 0
+    if i < sVer.len:
+      discard parseInt(sVer[i], sVerI)
+    var sVerI2 = 0
+    if i < sVer2.len:
+      discard parseInt(sVer2[i], sVerI2)
+    if sVerI < sVerI2:
+      return true
+    elif sVerI == sVerI2:
+      discard
+    else:
+      return false
 
 proc addPackage(packages: StringTableRef, p: string) =
   let x = versionSplitPos(p)
   let name = p.substr(0, x-1)
-  let version = if x < p.len: p.substr(x+1) else: ""
-  if packages.getOrDefault(name) <. version:
-    packages[name] = version
+  let version = newVersion(if x < p.len: p.substr(x+1) else: "")
+  if packages.getOrDefault(name).newVersion < version or
+     (not packages.hasKey(name)):
+    packages[name] = $version
 
 iterator chosen(packages: StringTableRef): string =
   for key, val in pairs(packages):
-    let res = if val == latest: key else: key & '-' & val
+    let res = if val.len == 0: key else: key & '-' & val
     yield res
 
 proc addNimblePath(p: string, info: TLineInfo) =
-  if not contains(options.searchPaths, p):
-    message(info, hintPath, p)
-    options.lazyPaths.insert(p, 0)
+  var path = p
+  let nimbleLinks = toSeq(walkPattern(p / "*.nimble-link"))
+  if nimbleLinks.len > 0:
+    # If the user has more than one .nimble-link file then... we just ignore it.
+    # Spec for these files is available in Nimble's readme:
+    # https://github.com/nim-lang/nimble#nimble-link
+    let nimbleLinkLines = readFile(nimbleLinks[0]).splitLines()
+    path = nimbleLinkLines[1]
+    if not path.isAbsolute():
+      path = p / path
+
+  if not contains(options.searchPaths, path):
+    message(info, hintPath, path)
+    options.lazyPaths.insert(path, 0)
 
 proc addPathRec(dir: string, info: TLineInfo) =
   var packages = newStringTable(modeStyleInsensitive)
@@ -82,7 +107,17 @@ proc nimblePath*(path: string, info: TLineInfo) =
   addNimblePath(path, info)
 
 when isMainModule:
+  proc v(s: string): Version = s.newVersion
+  # #head is special in the sense that it's assumed to always be newest.
+  doAssert v"1.0" < v"#head"
+  doAssert v"1.0" < v"1.1"
+  doAssert v"1.0.1" < v"1.1"
+  doAssert v"1" < v"1.1"
+  doAssert v"#aaaqwe" < v"1.1" # We cannot assume that a branch is newer.
+  doAssert v"#a111" < v"#head"
+
   var rr = newStringTable()
+  addPackage rr, "irc-#a111"
   addPackage rr, "irc-#head"
   addPackage rr, "irc-0.1.0"
   addPackage rr, "irc"
@@ -93,5 +128,6 @@ when isMainModule:
   addPackage rr, "ab-0.1"
   addPackage rr, "justone"
 
-  for p in rr.chosen:
-    echo p
+  doAssert toSeq(rr.chosen) ==
+    @["irc-#head", "another-0.1", "ab-0.1.3", "justone"]
+
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index 808159b8f..c19b41af1 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -152,7 +152,7 @@ proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef) =
     parseDirective(L, tok, config)    # else: give the token to the parser
 
 proc checkSymbol(L: TLexer, tok: TToken) =
-  if tok.tokType notin {tkSymbol..pred(tkIntLit), tkStrLit..tkTripleStrLit}:
+  if tok.tokType notin {tkSymbol..tkInt64Lit, tkStrLit..tkTripleStrLit}:
     lexMessage(L, errIdentifierExpected, tokToStr(tok))
 
 proc parseAssignment(L: var TLexer, tok: var TToken; config: ConfigRef) =
diff --git a/compiler/options.nim b/compiler/options.nim
index bf1b04c2c..40d56aea5 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -94,7 +94,7 @@ type
     cmdRun                    # run the project via TCC backend
   TStringSeq* = seq[string]
   TGCMode* = enum             # the selected GC
-    gcNone, gcBoehm, gcGo, gcStack, gcMarkAndSweep, gcRefc,
+    gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcRefc,
     gcV2, gcGenerational
 
   IdeCmd* = enum
@@ -291,8 +291,8 @@ proc pathSubs*(p, config: string): string =
     "projectpath", options.gProjectPath,
     "projectdir", options.gProjectPath,
     "nimcache", getNimcacheDir()])
-  if '~' in result:
-    result = result.replace("~", home)
+  if "~/" in result:
+    result = result.replace("~/", home & '/')
 
 proc toGeneratedFile*(path, ext: string): string =
   ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 3cd1e4d92..253716247 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -685,6 +685,11 @@ proc namedParams(p: var TParser, callee: PNode,
   # progress guaranteed
   exprColonEqExprListAux(p, endTok, result)
 
+proc commandParam(p: var TParser): PNode =
+  result = parseExpr(p)
+  if p.tok.tokType == tkDo:
+    result = postExprBlocks(p, result)
+
 proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
   #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
   #|       | doBlocks
@@ -733,7 +738,7 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
         when true:
           # progress NOT guaranteed
           p.hasProgress = false
-          addSon result, parseExpr(p)
+          addSon result, commandParam(p)
           if not p.hasProgress: break
         else:
           while p.tok.tokType != tkEof:
@@ -1253,14 +1258,12 @@ proc parseExprStmt(p: var TParser): PNode =
       while true:
         getTok(p)
         optInd(p, result)
-        var e = parseExpr(p)
-        addSon(result, e)
+        addSon(result, commandParam(p))
         if p.tok.tokType != tkComma: break
     elif p.tok.indent < 0 and isExprStart(p):
       result = newNode(nkCommand, a.info, @[a])
       while true:
-        var e = parseExpr(p)
-        addSon(result, e)
+        addSon(result, commandParam(p))
         if p.tok.tokType != tkComma: break
         getTok(p)
         optInd(p, result)
diff --git a/compiler/passes.nim b/compiler/passes.nim
index 7966ee88d..bf6ce1a0a 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -13,7 +13,7 @@
 import
   strutils, options, ast, astalgo, llstream, msgs, platform, os,
   condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
-  nimsets, syntaxes, times, rodread, idgen, modulegraphs
+  nimsets, syntaxes, times, rodread, idgen, modulegraphs, reorder
 
 type
   TPassContext* = object of RootObj # the pass's context
@@ -202,7 +202,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
         if graph.stopCompile(): break
         var n = parseTopLevelStmt(p)
         if n.kind == nkEmpty: break
-        if sfNoForward in module.flags:
+        if {sfNoForward, sfReorder} * module.flags != {}:
           # read everything, no streaming possible
           var sl = newNodeI(nkStmtList, n.info)
           sl.add n
@@ -210,6 +210,8 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
             var n = parseTopLevelStmt(p)
             if n.kind == nkEmpty: break
             sl.add n
+          if sfReorder in module.flags:
+            sl = reorder sl
           discard processTopLevelStmt(sl, a)
           break
         elif not processTopLevelStmt(n, a): break
diff --git a/compiler/platform.nim b/compiler/platform.nim
index eb0aca186..01ddba23e 100644
--- a/compiler/platform.nim
+++ b/compiler/platform.nim
@@ -21,8 +21,8 @@ type
                     # conditionals to condsyms (end of module).
     osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
     osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx,
-    osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osVxworks, osGenode
-    osJS, osNimrodVM, osStandalone
+    osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osAndroid, osVxworks
+    osGenode, osJS, osNimrodVM, osStandalone
 
 type
   TInfoOSProp* = enum
@@ -143,6 +143,10 @@ const
       objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
       scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
       props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}),
+     (name: "Android", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
+      objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
+      scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
+      props: {ospNeedsPIC, ospPosix}),
      (name: "VxWorks", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
       objExt: ".o", newLine: "\x0A", pathSep: ";", dirSep: "\\",
       scriptExt: ".sh", curDir: ".", exeExt: ".vxe", extSep: ".",
@@ -171,7 +175,8 @@ type
                      # alias conditionals to condsyms (end of module).
     cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64,
     cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel,
-    cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430, cpuSparc64
+    cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430, cpuSparc64,
+    cpuMips64, cpuMips64el
 
 type
   TEndian* = enum
@@ -200,7 +205,9 @@ const
     (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
     (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
     (name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
-    (name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64)]
+    (name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
+    (name: "mips64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
+    (name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)]
 
 var
   targetCPU*, hostCPU*: TSystemCPU
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 7e1db5b29..bc3771700 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -45,7 +45,7 @@ const
     wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop,
     wBreakpoint, wWatchPoint, wPassl, wPassc, wDeadCodeElim, wDeprecated,
     wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
-    wLinearScanEnd, wPatterns, wEffects, wNoForward, wComputedGoto,
+    wLinearScanEnd, wPatterns, wEffects, wNoForward, wReorder, wComputedGoto,
     wInjectStmt, wDeprecated, wExperimental, wThis}
   lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
     wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
@@ -210,9 +210,9 @@ proc pragmaDeadCodeElim(c: PContext, n: PNode) =
   if isTurnedOn(c, n): incl(c.module.flags, sfDeadCodeElim)
   else: excl(c.module.flags, sfDeadCodeElim)
 
-proc pragmaNoForward(c: PContext, n: PNode) =
-  if isTurnedOn(c, n): incl(c.module.flags, sfNoForward)
-  else: excl(c.module.flags, sfNoForward)
+proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
+  if isTurnedOn(c, n): incl(c.module.flags, flag)
+  else: excl(c.module.flags, flag)
 
 proc processCallConv(c: PContext, n: PNode) =
   if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
@@ -726,6 +726,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
         incl(sym.flags, sfThread)
       of wDeadCodeElim: pragmaDeadCodeElim(c, it)
       of wNoForward: pragmaNoForward(c, it)
+      of wReorder: pragmaNoForward(c, it, sfReorder)
       of wMagic: processMagic(c, it, sym)
       of wCompileTime:
         noVal(it)
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 7d9536625..220693f68 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -1327,7 +1327,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     
     if n.hasExplicitParams:
       put(g, tkBracketLe, "[")
-      gcomma(g, n)
+      gsemicolon(g, n)
       put(g, tkBracketRi, "]")
   of nkFormalParams:
     put(g, tkParLe, "(")
diff --git a/compiler/reorder.nim b/compiler/reorder.nim
new file mode 100644
index 000000000..a9ad1fd97
--- /dev/null
+++ b/compiler/reorder.nim
@@ -0,0 +1,102 @@
+
+import intsets, tables, ast, idents, renderer
+
+const
+  nfTempMark = nfTransf
+  nfPermMark = nfNoRewrite
+
+proc accQuoted(n: PNode): PIdent =
+  var id = ""
+  for i in 0 .. <n.len:
+    let x = n[i]
+    case x.kind
+    of nkIdent: id.add(x.ident.s)
+    of nkSym: id.add(x.sym.name.s)
+    else: discard
+  result = getIdent(id)
+
+proc addDecl(n: PNode; declares: var IntSet) =
+  case n.kind
+  of nkPostfix: addDecl(n[1], declares)
+  of nkPragmaExpr: addDecl(n[0], declares)
+  of nkIdent:
+    declares.incl n.ident.id
+  of nkSym:
+    declares.incl n.sym.name.id
+  of nkAccQuoted:
+    declares.incl accQuoted(n).id
+  else: discard
+
+proc computeDeps(n: PNode, declares, uses: var IntSet; topLevel: bool) =
+  template deps(n) = computeDeps(n, declares, uses, false)
+  template decl(n) =
+    if topLevel: addDecl(n, declares)
+  case n.kind
+  of procDefs:
+    decl(n[0])
+    for i in 1..bodyPos: deps(n[i])
+  of nkLetSection, nkVarSection, nkUsingStmt:
+    for a in n:
+      if a.kind in {nkIdentDefs, nkVarTuple}:
+        for j in countup(0, a.len-3): decl(a[j])
+        for j in a.len-2..a.len-1: deps(a[j])
+  of nkConstSection, nkTypeSection:
+    for a in n:
+      if a.len >= 3:
+        decl(a[0])
+        for i in 1..<a.len: deps(a[i])
+  of nkIdent: uses.incl n.ident.id
+  of nkSym: uses.incl n.sym.name.id
+  of nkAccQuoted: uses.incl accQuoted(n).id
+  of nkOpenSymChoice, nkClosedSymChoice:
+    uses.incl n.sons[0].sym.name.id
+  of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse:
+    for i in 0..<len(n): computeDeps(n[i], declares, uses, topLevel)
+  else:
+    for i in 0..<safeLen(n): deps(n[i])
+
+proc visit(i: int; all, res: PNode; deps: var seq[(IntSet, IntSet)]): bool =
+  let n = all[i]
+  if nfTempMark in n.flags:
+    # not a DAG!
+    return true
+  if nfPermMark notin n.flags:
+    incl n.flags, nfTempMark
+    var uses = deps[i][1]
+    for j in 0..<all.len:
+      if j != i:
+        let declares = deps[j][0]
+        for d in declares:
+          if uses.contains(d):
+            let oldLen = res.len
+            if visit(j, all, res, deps):
+              result = true
+              # rollback what we did, it turned out to be a dependency that caused
+              # trouble:
+              for k in oldLen..<res.len:
+                res.sons[k].flags = res.sons[k].flags - {nfPermMark, nfTempMark}
+              if oldLen != res.len: res.sons.setLen oldLen
+            break
+    n.flags = n.flags + {nfPermMark} - {nfTempMark}
+    res.add n
+
+proc reorder*(n: PNode): PNode =
+  result = newNodeI(nkStmtList, n.info)
+  var deps = newSeq[(IntSet, IntSet)](n.len)
+  for i in 0..<n.len:
+    deps[i][0] = initIntSet()
+    deps[i][1] = initIntSet()
+    computeDeps(n[i], deps[i][0], deps[i][1], true)
+
+  for i in 0 .. n.len-1:
+    discard visit(i, n, result, deps)
+  for i in 0..<result.len:
+    result.sons[i].flags = result.sons[i].flags - {nfTempMark, nfPermMark}
+  when false:
+    # reverse the result:
+    let L = result.len-1
+    for i in 0 .. result.len div 2:
+      result.sons[i].flags = result.sons[i].flags - {nfTempMark, nfPermMark}
+      result.sons[L - i].flags = result.sons[L - i].flags - {nfTempMark, nfPermMark}
+      swap(result.sons[i], result.sons[L - i])
+  #echo result
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index d84b59f78..358ce8a53 100644
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -228,6 +228,7 @@ proc prepend*(a: var Rope, b: string) = a = b & a
 var
   rnl* = tnl.newRope
   softRnl* = tnl.newRope
+  noRnl* = "".newRope
 
 proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
   var i = 0
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 5f48e2fc5..74b074f61 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -47,11 +47,9 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     #raiseRecoverableError("")
     result = errorNode(c, n)
   if result.typ == nil or result.typ == enforceVoidContext:
-    if n.kind != nkStmtList:
-      # we cannot check for 'void' in macros ...
-      localError(n.info, errExprXHasNoType,
-                 renderTree(result, {renderNoComments}))
-      result.typ = errorType(c)
+    localError(n.info, errExprXHasNoType,
+                renderTree(result, {renderNoComments}))
+    result.typ = errorType(c)
   else:
     if efNoProcvarCheck notin flags: semProcvarCheck(c, result)
     if result.typ.kind == tyVar: result = newDeref(result)
@@ -276,41 +274,6 @@ proc semSizeof(c: PContext, n: PNode): PNode =
   n.typ = getSysType(tyInt)
   result = n
 
-proc semOf(c: PContext, n: PNode): PNode =
-  if sonsLen(n) == 3:
-    n.sons[1] = semExprWithType(c, n.sons[1])
-    n.sons[2] = semExprWithType(c, n.sons[2], {efDetermineType})
-    #restoreOldStyleType(n.sons[1])
-    #restoreOldStyleType(n.sons[2])
-    let a = skipTypes(n.sons[1].typ, abstractPtrs)
-    let b = skipTypes(n.sons[2].typ, abstractPtrs)
-    let x = skipTypes(n.sons[1].typ, abstractPtrs-{tyTypeDesc})
-    let y = skipTypes(n.sons[2].typ, abstractPtrs-{tyTypeDesc})
-
-    if x.kind == tyTypeDesc or y.kind != tyTypeDesc:
-      localError(n.info, errXExpectsObjectTypes, "of")
-    elif b.kind != tyObject or a.kind != tyObject:
-      localError(n.info, errXExpectsObjectTypes, "of")
-    else:
-      let diff = inheritanceDiff(a, b)
-      # | returns: 0 iff `a` == `b`
-      # | returns: -x iff `a` is the x'th direct superclass of `b`
-      # | returns: +x iff `a` is the x'th direct subclass of `b`
-      # | returns: `maxint` iff `a` and `b` are not compatible at all
-      if diff <= 0:
-        # optimize to true:
-        message(n.info, hintConditionAlwaysTrue, renderTree(n))
-        result = newIntNode(nkIntLit, 1)
-        result.info = n.info
-        result.typ = getSysType(tyBool)
-        return result
-      elif diff == high(int):
-        localError(n.info, errXcanNeverBeOfThisSubtype, typeToString(a))
-  else:
-    localError(n.info, errXExpectsTwoArguments, "of")
-  n.typ = getSysType(tyBool)
-  result = n
-
 proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
   internalAssert n.sonsLen == 3 and
     n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
@@ -1121,9 +1084,11 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
       if ty.n != nil and ty.n.kind == nkRecList:
         let field = lookupInRecord(ty.n, i)
         if field != nil:
-          n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.typ])
-          n.typ.n = copyTree(n)
+          n.typ = makeTypeDesc(c, field.typ)
           return n
+          #n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.typ])
+          #n.typ.n = copyTree(n)
+          #return n
     else:
       tryReadingGenericParam(ty)
       return
@@ -1490,6 +1455,9 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
   var t = skipTypes(restype, {tyGenericInst, tyAlias})
   case t.kind
   of tyVar:
+    if n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv}:
+      n.sons[0] = n.sons[0].sons[1]
+
     n.sons[0] = takeImplicitAddr(c, n.sons[0])
   of tyTuple:
     for i in 0.. <t.sonsLen:
@@ -1829,11 +1797,11 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   of mDefined: result = semDefined(c, setMs(n, s), false)
   of mDefinedInScope: result = semDefined(c, setMs(n, s), true)
   of mCompiles: result = semCompiles(c, setMs(n, s), flags)
-  of mLow: result = semLowHigh(c, setMs(n, s), mLow)
-  of mHigh: result = semLowHigh(c, setMs(n, s), mHigh)
+  #of mLow: result = semLowHigh(c, setMs(n, s), mLow)
+  #of mHigh: result = semLowHigh(c, setMs(n, s), mHigh)
   of mSizeOf: result = semSizeof(c, setMs(n, s))
   of mIs: result = semIs(c, setMs(n, s), flags)
-  of mOf: result = semOf(c, setMs(n, s))
+  #of mOf: result = semOf(c, setMs(n, s))
   of mShallowCopy: result = semShallowCopy(c, n, flags)
   of mExpandToAst: result = semExpandToAst(c, n, s, flags)
   of mQuoteAst: result = semQuoteAst(c, n)
@@ -2207,9 +2175,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     message(n.info, warnDeprecated, "bind")
     result = semExpr(c, n.sons[0], flags)
   of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
+    if c.matchedConcept != nil and n.len == 1:
+      let modifier = n.modifierTypeKindOfNode
+      if modifier != tyNone:
+        var baseType = semExpr(c, n[0]).typ.skipTypes({tyTypeDesc})
+        result.typ = c.makeTypeDesc(c.newTypeWithSons(modifier, @[baseType]))
+        return
     var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
     result.typ = makeTypeDesc(c, typ)
-    #result = symNodeFromType(c, typ, n.info)
   of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
     # check if it is an expression macro:
     checkMinSonsLen(n, 1)
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index c664f735c..8b3d9c014 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -200,6 +200,41 @@ proc isStrangeArray(t: PType): bool =
   let t = t.skipTypes(abstractInst)
   result = t.kind == tyArray and t.firstOrd != 0
 
+proc semOf(c: PContext, n: PNode): PNode =
+  if sonsLen(n) == 3:
+    n.sons[1] = semExprWithType(c, n.sons[1])
+    n.sons[2] = semExprWithType(c, n.sons[2], {efDetermineType})
+    #restoreOldStyleType(n.sons[1])
+    #restoreOldStyleType(n.sons[2])
+    let a = skipTypes(n.sons[1].typ, abstractPtrs)
+    let b = skipTypes(n.sons[2].typ, abstractPtrs)
+    let x = skipTypes(n.sons[1].typ, abstractPtrs-{tyTypeDesc})
+    let y = skipTypes(n.sons[2].typ, abstractPtrs-{tyTypeDesc})
+
+    if x.kind == tyTypeDesc or y.kind != tyTypeDesc:
+      localError(n.info, errXExpectsObjectTypes, "of")
+    elif b.kind != tyObject or a.kind != tyObject:
+      localError(n.info, errXExpectsObjectTypes, "of")
+    else:
+      let diff = inheritanceDiff(a, b)
+      # | returns: 0 iff `a` == `b`
+      # | returns: -x iff `a` is the x'th direct superclass of `b`
+      # | returns: +x iff `a` is the x'th direct subclass of `b`
+      # | returns: `maxint` iff `a` and `b` are not compatible at all
+      if diff <= 0:
+        # optimize to true:
+        message(n.info, hintConditionAlwaysTrue, renderTree(n))
+        result = newIntNode(nkIntLit, 1)
+        result.info = n.info
+        result.typ = getSysType(tyBool)
+        return result
+      elif diff == high(int):
+        localError(n.info, errXcanNeverBeOfThisSubtype, typeToString(a))
+  else:
+    localError(n.info, errXExpectsTwoArguments, "of")
+  n.typ = getSysType(tyBool)
+  result = n
+
 proc magicsAfterOverloadResolution(c: PContext, n: PNode,
                                    flags: TExprFlags): PNode =
   case n[0].sym.magic
@@ -219,6 +254,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
     result.typ = getSysType(tyString)
   of mInstantiationInfo: result = semInstantiationInfo(c, n)
   of mOrd: result = semOrd(c, n)
+  of mOf: result = semOf(c, n)
   of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic)
   of mShallowCopy: result = semShallowCopy(c, n, flags)
   of mNBindSym: result = semBindSym(c, n)
diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim
index f4c225526..b331d05a1 100644
--- a/compiler/semobjconstr.nim
+++ b/compiler/semobjconstr.nim
@@ -44,7 +44,9 @@ proc locateFieldInInitExpr(field: PSym, initExpr: PNode): PNode =
   let fieldId = field.name.id
   for i in 1 .. <initExpr.len:
     let assignment = initExpr[i]
-    internalAssert assignment.kind == nkExprColonExpr
+    if assignment.kind != nkExprColonExpr:
+      localError(initExpr.info, "incorrect object construction syntax")
+      continue
 
     if fieldId == considerQuotedIdent(assignment[0]).id:
       return assignment
@@ -278,6 +280,9 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
   for i in 1.. <result.len:
     let field = result[i]
     if nfSem notin field.flags:
+      if field.kind != nkExprColonExpr:
+        localError(n.info, "incorrect object construction syntax")
+        continue
       let id = considerQuotedIdent(field[0])
       # This node was not processed. There are two possible reasons:
       # 1) It was shadowed by a field with the same name on the left
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 5ac2e678a..8ad8a6288 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -12,14 +12,14 @@
 discard """
   hygienic templates:
 
-    template `||` (a, b: expr): expr =
+    template `||` (a, b: untyped): untyped =
       let aa = a
       if aa: aa else: b
 
     var
       a, b: T
 
-    a || b || a
+    echo a || b || a
 
   Each evaluation context has to be different and we need to perform
   some form of preliminary symbol lookup in template definitions. Hygiene is
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index de71f1632..a7c9244cc 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1202,6 +1202,15 @@ proc freshType(res, prev: PType): PType {.inline.} =
   else:
     result = res
 
+template modifierTypeKindOfNode(n: PNode): TTypeKind =
+  case n.kind
+  of nkVarTy: tyVar
+  of nkRefTy: tyRef
+  of nkPtrTy: tyPtr
+  of nkStaticTy: tyStatic
+  of nkTypeOfExpr: tyTypeDesc
+  else: tyNone
+
 proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
   # if n.sonsLen == 0: return newConstraint(c, tyTypeClass)
   if nfBase2 in n.flags:
@@ -1227,13 +1236,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
       dummyName: PNode
       dummyType: PType
 
-    let modifier = case param.kind
-      of nkVarTy: tyVar
-      of nkRefTy: tyRef
-      of nkPtrTy: tyPtr
-      of nkStaticTy: tyStatic
-      of nkTypeOfExpr: tyTypeDesc
-      else: tyNone
+    let modifier = param.modifierTypeKindOfNode
 
     if modifier != tyNone:
       dummyName = param[0]
@@ -1509,9 +1512,26 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     dec c.inTypeContext
 
 proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
+  # source : https://en.wikipedia.org/wiki/Data_structure_alignment#x86
   m.typ.kind = kind
-  m.typ.align = size.int16
   m.typ.size = size
+  # this usually works for most basic types
+  # Assuming that since ARM, ARM64  don't support unaligned access
+  # data is aligned to type size
+  m.typ.align = size.int16
+
+  # FIXME: proper support for clongdouble should be added.
+  # long double size can be 8, 10, 12, 16 bytes depending on platform & compiler
+  if targetCPU == cpuI386 and size == 8:
+    #on Linux/BSD i386, double are aligned to 4bytes (except with -malign-double)
+    if kind in {tyFloat64, tyFloat} and
+       targetOS in {osLinux, osAndroid, osNetbsd, osFreebsd, osOpenbsd, osDragonfly}:
+      m.typ.align = 4
+    # on i386, all known compiler, 64bits ints are aligned to 4bytes (except with -malign-double)
+    elif kind in {tyInt, tyUInt, tyInt64, tyUInt64}:
+      m.typ.align = 4
+  else:
+    discard
 
 proc processMagicType(c: PContext, m: PSym) =
   case m.magic
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 6084e11c0..41596f05c 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -297,7 +297,9 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1;
         n.sons[i].typ = arg.typ
         n.sons[i].sons[1] = arg
     else:
-      if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
+      if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse,
+                                           nkOfBranch, nkElifBranch,
+                                           nkExceptBranch}:
         arg = c.semOperand(c, n.sons[i])
         n.sons[i] = arg
     if arg.typ != nil and arg.typ.kind == tyError: return
diff --git a/compiler/testability.nim b/compiler/testability.nim
deleted file mode 100644
index 4587a5344..000000000
--- a/compiler/testability.nim
+++ /dev/null
@@ -1,5 +0,0 @@
-template tests*(body: stmt) {.immediate.} =
-  when defined(selftest):
-    when not declared(unittest): import unittest
-    body
-
diff --git a/compiler/vm.nim b/compiler/vm.nim
index b8e6467b5..93cf66c05 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -409,6 +409,28 @@ proc recSetFlagIsRef(arg: PNode) =
   for i in 0 ..< arg.safeLen:
     arg.sons[i].recSetFlagIsRef
 
+proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) =
+  # FIXME: this doesn't attempt to solve incomplete
+  # support of tyPtr, tyRef in VM.
+  let typ = node.typ.skipTypes(abstractInst+{tyRange}-{tyTypeDesc})
+  let typeEntry = typ.sons[0].skipTypes(abstractInst+{tyRange}-{tyTypeDesc})
+  let typeKind = case typeEntry.kind
+  of tyUInt..tyUInt64: nkUIntLit
+  of tyRange, tyEnum, tyBool, tyChar, tyInt..tyInt64: nkIntLit
+  of tyFloat..tyFloat128: nkFloatLit
+  of tyString: nkStrLit
+  of tyObject: nkObjConstr
+  of tySequence: nkNilLit
+  of tyProc, tyTuple: nkPar
+  else: nkEmpty
+
+  let oldLen = node.len
+  setLen(node.sons, newLen)
+  if oldLen < newLen:
+    # TODO: This is still not correct for tyPtr, tyRef default value
+    for i in oldLen .. <newLen:
+      node.sons[i] = newNodeI(typeKind, info)
+
 proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
   var pc = start
   var tos = tos
@@ -1118,14 +1140,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeB(rkNode)
       let newLen = regs[rb].intVal.int
       if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess)
-      else:
-        let oldLen = regs[ra].node.len
-        setLen(regs[ra].node.sons, newLen)
-        if oldLen < newLen:
-          # XXX This is still not entirely correct
-          # set to default value:
-          for i in oldLen .. <newLen:
-            regs[ra].node.sons[i] = newNodeI(nkEmpty, c.debug[pc])
+      else: c.setLenSeq(regs[ra].node, newLen, c.debug[pc])
     of opcReset:
       internalError(c.debug[pc], "too implement")
     of opcNarrowS:
@@ -1307,12 +1322,24 @@ 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 opcNLineInfo:
+    of opcNGetFile:
       decodeB(rkNode)
       let n = regs[rb].node
-      createStr regs[ra]
-      regs[ra].node.strVal = n.info.toFileLineCol
-      regs[ra].node.info = c.debug[pc]
+      regs[ra].node = newStrNode(nkStrLit, n.info.toFilename)
+      regs[ra].node.info = n.info
+      regs[ra].node.typ = n.typ
+    of opcNGetLine:
+      decodeB(rkNode)
+      let n = regs[rb].node
+      regs[ra].node = newIntNode(nkIntLit, n.info.line)
+      regs[ra].node.info = n.info
+      regs[ra].node.typ = n.typ
+    of opcNGetColumn:
+      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
     of opcEqIdent:
       decodeBC(rkInt)
       if regs[rb].node.kind == nkIdent and regs[rc].node.kind == nkIdent:
@@ -1471,6 +1498,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       createStrKeepNode(regs[ra])
       if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000)
       storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode)
+    of opcToNarrowInt:
+      decodeBC(rkInt)
+      let mask = (1'i64 shl rc) - 1 # 0xFF
+      let signbit = 1'i64 shl (rc - 1) # 0x80
+      let toggle = mask - signbit # 0x7F
+      # algorithm: -((i8 and 0xFF) xor 0x7F) + 0x7F
+      # mask off higher bits.
+      # uses two's complement to sign-extend integer.
+      # reajust integer into desired range.
+      regs[ra].intVal = -((regs[rb].intVal and mask) xor toggle) + toggle
+
     inc pc
 
 proc execute(c: PCtx, start: int): PNode =
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 263ec8378..7e1309e0a 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -98,7 +98,7 @@ type
     opcNError,
     opcNWarning,
     opcNHint,
-    opcNLineInfo,
+    opcNGetLine, opcNGetColumn, opcNGetFile,
     opcEqIdent,
     opcStrToIdent,
     opcIdentToStr,
@@ -136,7 +136,8 @@ type
     opcNBindSym,
     opcSetType,   # dest.typ = types[Bx]
     opcTypeTrait,
-    opcMarshalLoad, opcMarshalStore
+    opcMarshalLoad, opcMarshalStore,
+    opcToNarrowInt
 
   TBlock* = object
     label*: PSym
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index b2b1ec92b..b9bbba551 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -224,12 +224,13 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
       result.add copyTree(c)
   of tyTuple:
     if inst:
-      result = newNodeX(nkTupleTy)
       # only named tuples have a node, unnamed tuples don't
       if t.n.isNil:
+        result = newNodeX(nkPar)
         for subType in t.sons:
           result.add mapTypeToAst(subType, info)
       else:
+        result = newNodeX(nkTupleTy)
         for s in t.n.sons:
           result.add newIdentDefs(s)
     else:
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index ba89f88d4..dbb8c9dcd 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -656,16 +656,17 @@ proc genNarrow(c: PCtx; n: PNode; dest: TDest) =
   let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
   # uint is uint64 in the VM, we we only need to mask the result for
   # other unsigned types:
-  if t.kind in {tyUInt8..tyUInt32}:
+  if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8):
     c.gABC(n, opcNarrowU, dest, TRegister(t.size*8))
-  elif t.kind in {tyInt8..tyInt32}:
+  elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and t.size < 8):
     c.gABC(n, opcNarrowS, dest, TRegister(t.size*8))
 
 proc genNarrowU(c: PCtx; n: PNode; dest: TDest) =
   let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
   # uint is uint64 in the VM, we we only need to mask the result for
   # other unsigned types:
-  if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}:
+  if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32} or
+    (t.kind in {tyUInt, tyInt} and t.size < 8):
     c.gABC(n, opcNarrowU, dest, TRegister(t.size*8))
 
 proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
@@ -875,11 +876,25 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mBitnotI:
     genUnaryABC(c, n, dest, opcBitnotInt)
     genNarrowU(c, n, dest)
-  of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64,
-     mToU8, mToU16, mToU32, mToFloat, mToBiggestFloat, mToInt,
+  of mToFloat, mToBiggestFloat, mToInt,
      mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr,
      mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr:
     genConv(c, n, n.sons[1], dest)
+  of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
+    #genNarrowU modified
+    let t = skipTypes(n.sons[1].typ, abstractVar-{tyTypeDesc})
+    let tmp = c.genx(n.sons[1])
+    c.gABC(n, opcNarrowU, tmp, TRegister(t.size*8))
+    # assign result to dest register
+    if dest < 0: dest = c.getTemp(n.typ)
+    c.gABC(n, opcAsgnInt, dest, tmp)
+    c.freeTemp(tmp)
+  of mToU8, mToU16, mToU32:
+    let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
+    var tmp = c.genx(n.sons[1])
+    if dest < 0: dest = c.getTemp(n.typ)
+    c.gABC(n, opcToNarrowInt, dest, tmp, TRegister(t.size*8))
+    c.freeTemp(tmp)
   of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr)
   of mLeStr: genBinaryABC(c, n, dest, opcLeStr)
   of mLtStr: genBinaryABC(c, n, dest, opcLtStr)
@@ -1071,7 +1086,16 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent)
   of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode)
   of mSameNodeType: genBinaryABC(c, n, dest, opcSameNodeType)
-  of mNLineInfo: genUnaryABC(c, n, dest, opcNLineInfo)
+  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 false
   of mNHint:
     unused(n, dest)
     genUnaryStmt(c, n, opcNHint)
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index 98fd912d8..773ab8ff5 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -55,7 +55,7 @@ type
     wFloatchecks, wNanChecks, wInfChecks,
     wAssertions, wPatterns, wWarnings,
     wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags,
-    wDeadCodeElim, wSafecode, wNoForward, wNoRewrite,
+    wDeadCodeElim, wSafecode, wNoForward, wReorder, wNoRewrite,
     wPragma,
     wCompileTime, wNoInit,
     wPassc, wPassl, wBorrow, wDiscardable,
@@ -143,7 +143,7 @@ const
 
     "assertions", "patterns", "warnings", "hints",
     "optimization", "raises", "writes", "reads", "size", "effects", "tags",
-    "deadcodeelim", "safecode", "noforward", "norewrite",
+    "deadcodeelim", "safecode", "noforward", "reorder", "norewrite",
     "pragma",
     "compiletime", "noinit",
     "passc", "passl", "borrow", "discardable", "fieldchecks",
diff --git a/config/nim.cfg b/config/nim.cfg
index 9374e2b88..7ac87cc33 100644
--- a/config/nim.cfg
+++ b/config/nim.cfg
@@ -90,6 +90,18 @@ path="$lib/pure"
   @end
 @end
 
+@if android:
+  cc = clang
+  @if termux:
+    gcc.options.linker = "-landroid-glob"
+    gcc.cpp.options.linker = "-landroid-glob"
+    clang.options.linker = "-landroid-glob"
+    clang.cpp.options.linker = "-landroid-glob"
+    tcc.options.linker = "-landroid-glob"
+    define:"useShPath:/system/bin/sh"
+  @end
+@end
+
 # Configuration for the Intel C/C++ compiler:
 @if windows:
   icl.options.speed = "/Ox /arch:SSE2"
@@ -109,6 +121,9 @@ path="$lib/pure"
   tlsEmulation:on
   gcc.options.always = "-w"
   gcc.cpp.options.always = "-w -fpermissive"
+@elif windows:
+  gcc.options.always = "-w -mno-ms-bitfields"
+  gcc.cpp.options.always = "-w -fpermissive -mno-ms-bitfields"
 @else:
   gcc.options.always = "-w"
   gcc.cpp.options.always = "-w -fpermissive"
@@ -180,27 +195,39 @@ clang.options.speed = "-O3"
 clang.options.size = "-Os"
 
 # Configuration for the Visual C/C++ compiler:
-vcc.exe = "vccexe.exe"
-vcc.linkerexe = "vccexe.exe"
+vcc.exe =     "vccexe.exe"
+vcc.cpp.exe = "vccexe.exe"
+vcc.linkerexe =     "vccexe.exe"
+vcc.cpp.linkerexe = "vccexe.exe"
 
 # set the options for specific platforms:
+vcc.options.always =      "/nologo"
+vcc.cpp.options.always %=  "${vcc.options.always} /EHsc"
+vcc.options.linker =      "/nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB
+vcc.cpp.options.linker %=  "${vcc.options.linker}"
 @if i386:
-vcc.options.always = "--platform:x86 /nologo"
-vcc.options.linker = "--platform:x86 /nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB
+vcc.options.always %=      "--platform:x86 ${vcc.options.always}"
+vcc.cpp.options.always %=  "--platform:x86 ${vcc.cpp.options.always}"
+vcc.options.linker %=      "--platform:x86 ${vcc.options.linker}"
+vcc.cpp.options.linker %=  "--platform:x86 ${vcc.cpp.options.linker}"
 @elif amd64:
-vcc.options.always = "--platform:amd64 /nologo"
-vcc.options.linker = "--platform:amd64 /nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB
+vcc.options.always %=      "--platform:amd64 ${vcc.options.always}"
+vcc.cpp.options.always %=  "--platform:amd64 ${vcc.cpp.options.always}"
+vcc.options.linker %=      "--platform:amd64 ${vcc.options.linker}"
+vcc.cpp.options.linker %=  "--platform:amd64 ${vcc.cpp.options.linker}"
 @elif arm:
-vcc.options.always = "--platform:arm /nologo"
-vcc.options.linker = "--platform:arm /nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB
-@else:
-vcc.options.always = "/nologo"
-vcc.options.linker = "/nologo /DEBUG /Zi /F33554432" # set the stack size to 32 MiB
+vcc.options.always %=      "--platform:arm ${vcc.options.always}"
+vcc.cpp.options.always %=  "--platform:arm ${vcc.cpp.options.always}"
+vcc.options.linker %=      "--platform:arm ${vcc.options.linker}"
+vcc.cpp.options.linker %=  "--platform:arm ${vcc.cpp.options.linker}"
 @end
 
-vcc.options.debug = "/Zi /FS /Od"
-vcc.options.speed = "/O2"
-vcc.options.size = "/O1"
+vcc.options.debug =     "/Zi /FS /Od"
+vcc.cpp.options.debug = "/Zi /FS /Od"
+vcc.options.speed =     "/O2"
+vcc.cpp.options.speed = "/O2"
+vcc.options.size =     "/O1"
+vcc.cpp.options.size = "/O1"
 
 # Configuration for the Tiny C Compiler:
 tcc.options.always = "-w"
diff --git a/doc/advopt.txt b/doc/advopt.txt
index b88e5f063..fb6fd719b 100644
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -65,7 +65,7 @@ Advanced options:
   --skipUserCfg             do not read the user's configuration file
   --skipParentCfg           do not read the parent dirs' configuration files
   --skipProjCfg             do not read the project's configuration file
-  --gc:refc|v2|markAndSweep|boehm|go|none
+  --gc:refc|v2|markAndSweep|boehm|go|none|regions
                             select the GC to use; default is 'refc'
   --index:on|off            turn index file generation on|off
   --putenv:key=value        set an environment variable
diff --git a/doc/lib.rst b/doc/lib.rst
index ea43c0db9..64c3c11eb 100644
--- a/doc/lib.rst
+++ b/doc/lib.rst
@@ -539,9 +539,6 @@ Network Programming and Internet Protocols
 * `joyent_http_parser <joyent_http_parser.html>`_
   Wrapper for the joyent's high-performance HTTP parser.
 
-* `libcurl <libcurl.html>`_
-  Wrapper for the libcurl library.
-
 * `openssl <openssl.html>`_
   Wrapper for OpenSSL.
 
diff --git a/doc/manual/ffi.txt b/doc/manual/ffi.txt
index e9b52eaca..06fed1430 100644
--- a/doc/manual/ffi.txt
+++ b/doc/manual/ffi.txt
@@ -132,7 +132,7 @@ translated into a C array of undetermined size:
 
 .. code-block:: nim
   type
-    ArrayPart{.unchecked.} = array[0..0, int]
+    ArrayPart{.unchecked.} = array[0, int]
     MySeq = object
       len, cap: int
       data: ArrayPart
@@ -146,10 +146,6 @@ Produces roughly this C code:
     NI data[];
   } MySeq;
 
-The bounds checking done at compile time is not disabled for now, so to access
-``s.data[C]`` (where ``C`` is a constant) the array's index needs to
-include ``C``.
-
 The base type of the unchecked array may not contain any GC'ed memory but this
 is currently not checked.
 
diff --git a/doc/manual/lexing.txt b/doc/manual/lexing.txt
index d4c11adf7..3147dd97c 100644
--- a/doc/manual/lexing.txt
+++ b/doc/manual/lexing.txt
@@ -130,7 +130,7 @@ Two identifiers are considered equal if the following algorithm returns true:
 .. code-block:: nim
   proc sameIdentifier(a, b: string): bool =
     a[0] == b[0] and
-      a.replace(re"_|–", "").toLower == b.replace(re"_|–", "").toLower
+      a.replace("_", "").toLower == b.replace("_", "").toLower
 
 That means only the first letters are compared in a case sensitive manner. Other
 letters are compared case insensitively and underscores are ignored.
diff --git a/doc/manual/procs.txt b/doc/manual/procs.txt
index 5f4c9f2fa..6d09ac3f5 100644
--- a/doc/manual/procs.txt
+++ b/doc/manual/procs.txt
@@ -248,12 +248,20 @@ calls can use the ``do`` keyword:
 .. code-block:: nim
   sort(cities) do (x,y: string) -> int:
     cmp(x.len, y.len)
+
   # Less parenthesis using the method plus command syntax:
   cities = cities.map do (x:string) -> string:
     "City of " & x
 
+  # In macros, the do notation is often used for quasi-quoting
+  macroResults.add quote do:
+    if not `ex`:
+      echo `info`, ": Check failed: ", `expString`
+
 ``do`` is written after the parentheses enclosing the regular proc params.
 The proc expression represented by the do block is appended to them.
+In calls using the command syntax, the do block will bind to the immediately
+preceeding expression, transforming it in a call.
 
 ``do`` with parentheses is an anonymous ``proc``; however a ``do`` without
 parentheses is just a block of code. The ``do`` notation can be used to
@@ -275,8 +283,8 @@ Nonoverloadable builtins
 The following builtin procs cannot be overloaded for reasons of implementation
 simplicity (they require specialized semantic checking)::
 
-  declared, defined, definedInScope, compiles, low, high, sizeOf,
-  is, of, shallowCopy, getAst, astToStr, spawn, procCall
+  declared, defined, definedInScope, compiles, sizeOf,
+  is, shallowCopy, getAst, astToStr, spawn, procCall
 
 Thus they act more like keywords than like ordinary identifiers; unlike a
 keyword however, a redefinition may `shadow`:idx: the definition in
diff --git a/doc/nimc.rst b/doc/nimc.rst
index 5d9ed03ab..e949df69c 100644
--- a/doc/nimc.rst
+++ b/doc/nimc.rst
@@ -189,12 +189,28 @@ resides in its own directory so that the generated ``nimcache`` directory
 is not shared between different projects.
 
 
+Compiler Selection
+==================
+
+To change the compiler from the default compiler (at the command line)::
+
+  nim c --cc:llvm_gcc --compile_only myfile.nim
+
+This uses the configuration defined in ``config\nim.cfg`` for ``lvm_gcc``.
+
+If nimcache already contains compiled code from a different compiler for the same project,
+add the ``-f`` flag to force all files to be recompiled.
+
+The default compiler is defined at the top of ``config\nim.cfg``.  Changing this setting
+affects the compiler used by ``koch`` to (re)build Nim.
+
+
 Cross compilation
 =================
 
 To cross compile, use for example::
 
-  nim c --cpu:i386 --os:linux --compile_only --gen_script myproject.nim
+  nim c --cpu:i386 --os:linux --compileOnly --genScript myproject.nim
 
 Then move the C code and the compile script ``compile_myproject.sh`` to your
 Linux i386 machine and run the script.
@@ -262,6 +278,15 @@ Define               Effect
                      what's in the Nim file with what's in the C header
                      (requires a C compiler with _Static_assert support, like
                      any C11 compiler)
+``tempDir``          This symbol takes a string as its value, like 
+                     ``--define:tempDir:/some/temp/path`` to override the
+                     temporary directory returned by ``os.getTempDir()``.
+                     The value **should** end with a directory separator 
+                     character. (Relevant for the Android platform)
+``useShPath``        This symbol takes a string as its value, like 
+                     ``--define:useShPath:/opt/sh/bin/sh`` to override the
+                     path for the ``sh`` binary, in cases where it is not
+                     located in the default location ``/bin/sh``
 ==================   =========================================================
 
 
diff --git a/doc/tut1.rst b/doc/tut1.rst
index fc8e411cb..89893a39a 100644
--- a/doc/tut1.rst
+++ b/doc/tut1.rst
@@ -138,7 +138,7 @@ comments can also be nested.
     ]#
   ]#
 
-You can also use the `discard statement`_ together with *long string
+You can also use the `discard statement <#procedures-discard-statement>`_ together with *long string
 literals* to create block comments:
 
 .. code-block:: nim
@@ -364,8 +364,7 @@ iterator:
     echo i
   # --> Outputs 1 2 3 4 5 6 7 8 9 10 on different lines
 
-The built-in `$ <system.html#$>`_ operator turns an integer (``int``) and many
-other types into a string. The variable ``i`` is implicitly declared by the
+The variable ``i`` is implicitly declared by the
 ``for`` loop and has the type ``int``, because that is what `countup
 <system.html#countup>`_ returns. ``i`` runs through the values 1, 2, .., 10.
 Each value is ``echo``-ed. This code does the same:
@@ -501,10 +500,6 @@ differences:
 The ``when`` statement is useful for writing platform specific code, similar to
 the ``#ifdef`` construct in the C programming language.
 
-**Note**: To comment out a large piece of code, it is often better to use a
-``when false:`` statement than to use real comments. This way nesting is
-possible.
-
 
 Statements and indentation
 ==========================
diff --git a/doc/tut2.rst b/doc/tut2.rst
index f145528a1..0bb4c94e1 100644
--- a/doc/tut2.rst
+++ b/doc/tut2.rst
@@ -218,7 +218,7 @@ So "pure object oriented" code is easy to write:
   import strutils, sequtils
 
   stdout.writeLine("Give a list of numbers (separated by spaces): ")
-  stdout.write(stdin.readLine.split.map(parseInt).max.`$`)
+  stdout.write(stdin.readLine.splitWhitespace.map(parseInt).max.`$`)
   stdout.writeLine(" is the maximum!")
 
 
@@ -233,15 +233,15 @@ is needed:
 
   type
     Socket* = ref object of RootObj
-      host: int # cannot be accessed from the outside of the module due to missing star
+      h: int # cannot be accessed from the outside of the module due to missing star
 
   proc `host=`*(s: var Socket, value: int) {.inline.} =
     ## setter of host address
-    s.host = value
+    s.h = value
 
   proc host*(s: Socket): int {.inline.} =
     ## getter of host address
-    s.host
+    s.h
 
   var s: Socket
   new s
@@ -723,7 +723,7 @@ regular expressions:
 
 .. code-block:: nim
 
-  macro case_token(n: typed): typed =
+  macro case_token(n: varargs[untyped]): typed =
     # creates a lexical analyzer from regular expressions
     # ... (implementation is an exercise for the reader :-)
     discard
diff --git a/examples/allany.nim b/examples/allany.nim
index 52a794204..6ff055aa6 100644
--- a/examples/allany.nim
+++ b/examples/allany.nim
@@ -1,6 +1,6 @@
 # All and any
 
-template all(container, cond: expr): expr {.immediate.} =
+template all(container, cond: untyped): bool =
   block:
     var result = true
     for it in items(container):
@@ -9,7 +9,7 @@ template all(container, cond: expr): expr {.immediate.} =
         break
     result
 
-template any(container, cond: expr): expr {.immediate.} =
+template any(container, cond: untyped): bool =
   block:
     var result = false
     for it in items(container):
diff --git a/koch.nim b/koch.nim
index aaa03d558..6ae45fcb7 100644
--- a/koch.nim
+++ b/koch.nim
@@ -227,14 +227,15 @@ proc bundleWinTools() =
   removeFile("tools/finish".exe)
   buildVccTool()
   nimexec("c -o:bin/nimgrab.exe -d:ssl tools/nimgrab.nim")
+  nimexec("c -o:bin/nimgrep.exe tools/nimgrep.nim")
   when false:
     # not yet a tool worth including
     nimexec(r"c --cc:vcc --app:gui -o:bin\downloader.exe -d:ssl --noNimblePath " &
             r"--path:..\ui tools\downloader.nim")
 
 proc zip(args: string) =
-  bundleNimbleSrc()
-  bundleNimsuggest(false)
+  bundleNimbleExe()
+  bundleNimsuggest(true)
   bundleWinTools()
   nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
        [VersionAsString, compileNimInst])
@@ -407,13 +408,15 @@ proc winReleaseArch(arch: string) =
   withMingw r"..\mingw" & arch & r"\bin":
     # Rebuilding koch is necessary because it uses its pointer size to
     # determine which mingw link to put in the NSIS installer.
-    nimexec "c --out:koch_temp --cpu:$# koch" % cpu
-    exec "koch_temp boot -d:release --cpu:$#" % cpu
-    exec "koch_temp zip -d:release"
+    nimexec "c --cpu:$# koch" % cpu
+    exec "koch boot -d:release --cpu:$#" % cpu
+    exec "koch zip -d:release"
     overwriteFile r"build\nim-$#.zip" % VersionAsString,
              r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch]
 
-proc winRelease() =
+proc winRelease*() =
+  # Now used from "tools/winrelease" and not directly supported by koch
+  # anymore!
   # Build -docs file:
   when true:
     web(gaCode)
@@ -530,39 +533,40 @@ proc showHelp() =
   quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
                    CompileDate, CompileTime], QuitSuccess)
 
-var op = initOptParser()
-op.next()
-case op.kind
-of cmdLongOption, cmdShortOption: showHelp()
-of cmdArgument:
-  case normalize(op.key)
-  of "boot": boot(op.cmdLineRest)
-  of "clean": clean(op.cmdLineRest)
-  of "web": web(op.cmdLineRest)
-  of "doc", "docs": web("--onlyDocs " & op.cmdLineRest)
-  of "json2": web("--json2 " & op.cmdLineRest)
-  of "website": website(op.cmdLineRest & gaCode)
-  of "web0":
-    # undocumented command for Araq-the-merciful:
-    web(op.cmdLineRest & gaCode)
-  of "pdf": pdf()
-  of "csource", "csources": csource(op.cmdLineRest)
-  of "zip": zip(op.cmdLineRest)
-  of "xz": xz(op.cmdLineRest)
-  of "nsis": nsis(op.cmdLineRest)
-  of "geninstall": geninstall(op.cmdLineRest)
-  of "distrohelper": geninstall()
-  of "install": install(op.cmdLineRest)
-  of "testinstall": testUnixInstall()
-  of "test", "tests": tests(op.cmdLineRest)
-  of "temp": temp(op.cmdLineRest)
-  of "xtemp": xtemp(op.cmdLineRest)
-  of "winrelease": winRelease()
-  of "wintools": bundleWinTools()
-  of "nimble": buildNimble(existsDir(".git"))
-  of "nimsuggest": bundleNimsuggest(buildExe=true)
-  of "tools": buildTools(existsDir(".git"))
-  of "pushcsource", "pushcsources": pushCsources()
-  of "valgrind": valgrind(op.cmdLineRest)
-  else: showHelp()
-of cmdEnd: showHelp()
+when isMainModule:
+  var op = initOptParser()
+  op.next()
+  case op.kind
+  of cmdLongOption, cmdShortOption: showHelp()
+  of cmdArgument:
+    case normalize(op.key)
+    of "boot": boot(op.cmdLineRest)
+    of "clean": clean(op.cmdLineRest)
+    of "web": web(op.cmdLineRest)
+    of "doc", "docs": web("--onlyDocs " & op.cmdLineRest)
+    of "json2": web("--json2 " & op.cmdLineRest)
+    of "website": website(op.cmdLineRest & gaCode)
+    of "web0":
+      # undocumented command for Araq-the-merciful:
+      web(op.cmdLineRest & gaCode)
+    of "pdf": pdf()
+    of "csource", "csources": csource(op.cmdLineRest)
+    of "zip": zip(op.cmdLineRest)
+    of "xz": xz(op.cmdLineRest)
+    of "nsis": nsis(op.cmdLineRest)
+    of "geninstall": geninstall(op.cmdLineRest)
+    of "distrohelper": geninstall()
+    of "install": install(op.cmdLineRest)
+    of "testinstall": testUnixInstall()
+    of "test", "tests": tests(op.cmdLineRest)
+    of "temp": temp(op.cmdLineRest)
+    of "xtemp": xtemp(op.cmdLineRest)
+    #of "winrelease": winRelease()
+    of "wintools": bundleWinTools()
+    of "nimble": buildNimble(existsDir(".git"))
+    of "nimsuggest": bundleNimsuggest(buildExe=true)
+    of "tools": buildTools(existsDir(".git"))
+    of "pushcsource", "pushcsources": pushCsources()
+    of "valgrind": valgrind(op.cmdLineRest)
+    else: showHelp()
+  of cmdEnd: showHelp()
diff --git a/lib/core/locks.nim b/lib/core/locks.nim
index fbe9c8acf..f9add0037 100644
--- a/lib/core/locks.nim
+++ b/lib/core/locks.nim
@@ -19,6 +19,8 @@ type
 
 {.deprecated: [TLock: Lock, TCond: Cond].}
 
+{.push stackTrace: off.}
+
 proc initLock*(lock: var Lock) {.inline.} =
   ## Initializes the given lock.
   initSysLock(lock)
@@ -59,9 +61,12 @@ proc signal*(cond: var Cond) {.inline.} =
 template withLock*(a: Lock, body: untyped) =
   ## Acquires the given lock, executes the statements in body and
   ## releases the lock after the statements finish executing.
+  mixin acquire, release
   a.acquire()
   {.locks: [a].}:
     try:
       body
     finally:
       a.release()
+
+{.pop.}
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index af1e9de28..3cfefb5c1 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -94,8 +94,9 @@ type
     ntyVarargs,
     ntyUnused,
     ntyError,
-    ntyBuiltinTypeClass, ntyConcept, ntyConceptInst, ntyComposite,
-    ntyAnd, ntyOr, ntyNot
+    ntyBuiltinTypeClass, ntyUserTypeClass, ntyUserTypeClassInst,
+    ntyCompositeTypeClass, ntyInferred, ntyAnd, ntyOr, ntyNot,
+    ntyAnything, ntyStatic, ntyFromExpr, ntyFieldAccessor, ntyVoid
 
   TNimTypeKinds* {.deprecated.} = set[NimTypeKind]
   NimSymKind* = enum
@@ -149,6 +150,9 @@ proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect.}
 proc `==`*(a, b: NimNode): bool {.magic: "EqNimrodNode", noSideEffect.}
   ## compares two Nim nodes
 
+proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect.}
+  ## compares two Nim symbols
+
 proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} =
   ## compares two Nim nodes' types. Return true if the types are the same,
   ## eg. true when comparing alias with original type.
@@ -317,10 +321,30 @@ proc toStrLit*(n: NimNode): NimNode {.compileTime.} =
   ## in a string literal node
   return newStrLitNode(repr(n))
 
-proc lineinfo*(n: NimNode): string {.magic: "NLineInfo", noSideEffect.}
+type
+  LineInfo* = object
+    filename*: string
+    line*,column*: int
+
+proc `$`*(arg: Lineinfo): string =
+  result = arg.filename & "(" & $arg.line & ", " & $arg.column & ")"
+
+#proc lineinfo*(n: NimNode): LineInfo {.magic: "NLineInfo", noSideEffect.}
   ## returns the position the node appears in the original source file
   ## in the form filename(line, col)
 
+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 lineInfoObj*(n: NimNode): LineInfo {.compileTime.} =
+  result.filename = n.getFile
+  result.line = n.getLine
+  result.column = n.getColumn
+
+proc lineInfo*(arg: NimNode): string {.compileTime.} =
+  $arg.lineInfoObj
+
 proc internalParseExpr(s: string): NimNode {.
   magic: "ParseExprToAst", noSideEffect.}
 
@@ -527,10 +551,17 @@ proc newLit*[N,T](arg: array[N,T]): NimNode {.compileTime.} =
     result.add newLit(x)
 
 proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} =
-  result = nnkBracket.newTree
+  var bracket = nnkBracket.newTree
   for x in arg:
-    result.add newLit(x)
-  result = nnkPrefix.newTree(bindSym"@", result)
+    bracket.add newLit(x)
+
+  result = nnkCall.newTree(
+    nnkBracketExpr.newTree(
+      nnkAccQuoted.newTree( bindSym"@" ),
+      getTypeInst( bindSym"T" )
+    ),
+    bracket
+  )
 
 proc newLit*(arg: tuple): NimNode {.compileTime.} =
   result = nnkPar.newTree
@@ -557,7 +588,8 @@ proc nestList*(theProc: NimIdent,
 proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
   ## Convert the AST `n` to a human-readable tree-like string.
   ##
-  ## See also `repr` and `lispRepr`.
+  ## See also `repr`, `lispRepr`, and `astGenRepr`.
+
   proc traverse(res: var string, level: int, n: NimNode) {.benign.} =
     for i in 0..level-1: res.add "  "
     res.add(($n.kind).substr(3))
@@ -582,7 +614,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
 proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
   ## Convert the AST `n` to a human-readable lisp-like string,
   ##
-  ## See also `repr` and `treeRepr`.
+  ## See also `repr`, `treeRepr`, and `astGenRepr`.
 
   result = ($n.kind).substr(3)
   add(result, "(")
@@ -605,9 +637,96 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} =
 
   add(result, ")")
 
+proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
+  ## Convert the AST `n` to the code required to generate that AST. So for example
+  ##
+  ## .. code-block:: nim
+  ##   astGenRepr:
+  ##     echo "Hello world"
+  ##
+  ## Would output:
+  ##
+  ## .. code-block:: nim
+  ##   nnkStmtList.newTree(
+  ##     nnkCommand.newTree(
+  ##       newIdentNode(!"echo"),
+  ##       newLit("Hello world")
+  ##     )
+  ##   )
+  ##
+  ## See also `repr`, `treeRepr`, and `lispRepr`.
+
+  const
+    NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone}
+    LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit}
+
+  proc escape(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect.} =
+    ## Functions copied from strutils
+    proc toHex(x: BiggestInt, len: Positive): string {.noSideEffect, rtl.} =
+      const
+        HexChars = "0123456789ABCDEF"
+      var
+        t = x
+      result = newString(len)
+      for j in countdown(len-1, 0):
+        result[j] = HexChars[t and 0xF]
+        t = t shr 4
+        # handle negative overflow
+        if t == 0 and x < 0: t = -1
+
+    result = newStringOfCap(s.len + s.len shr 2)
+    result.add(prefix)
+    for c in items(s):
+      case c
+      of '\0'..'\31', '\128'..'\255':
+        add(result, "\\x")
+        add(result, toHex(ord(c), 2))
+      of '\\': add(result, "\\\\")
+      of '\'': add(result, "\\'")
+      of '\"': add(result, "\\\"")
+      else: add(result, c)
+    add(result, suffix)
+
+  proc traverse(res: var string, level: int, n: NimNode) {.benign.} =
+    for i in 0..level-1: res.add "  "
+    if n.kind in NodeKinds:
+      res.add("new" & ($n.kind).substr(3) & "Node(")
+    elif n.kind in LitKinds:
+      res.add("newLit(")
+    else:
+      res.add($n.kind)
+
+    case n.kind
+    of nnkEmpty: discard
+    of nnkNilLit: res.add("nil")
+    of nnkCharLit: res.add("'" & $chr(n.intVal) & "'")
+    of nnkIntLit..nnkInt64Lit: res.add($n.intVal)
+    of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal)
+    of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape())
+    of nnkIdent: res.add("!" & ($n.ident).escape())
+    of nnkSym: res.add(($n.symbol).escape())
+    of nnkNone: assert false
+    else:
+      res.add(".newTree(")
+      for j in 0..<n.len:
+        res.add "\n"
+        traverse(res, level + 1, n[j])
+        if j != n.len-1:
+          res.add(",")
+
+      res.add("\n")
+      for i in 0..level-1: res.add "  "
+      res.add(")")
+
+    if n.kind in NodeKinds+LitKinds:
+      res.add(")")
+
+  result = ""
+  traverse(result, 0, n)
+
 macro dumpTree*(s: untyped): untyped = echo s.treeRepr
   ## Accepts a block of nim code and prints the parsed abstract syntax
-  ## tree using the `toTree` function. Printing is done *at compile time*.
+  ## tree using the `treeRepr` function. Printing is done *at compile time*.
   ##
   ## You can use this as a tool to explore the Nim's abstract syntax
   ## tree and to discover what kind of nodes must be created to represent
@@ -615,7 +734,16 @@ macro dumpTree*(s: untyped): untyped = echo s.treeRepr
 
 macro dumpLisp*(s: untyped): untyped = echo s.lispRepr
   ## Accepts a block of nim code and prints the parsed abstract syntax
-  ## tree using the `toLisp` function. Printing is done *at compile time*.
+  ## tree using the `lispRepr` function. Printing is done *at compile time*.
+  ##
+  ## See `dumpTree`.
+
+macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr
+  ## Accepts a block of nim code and prints the parsed abstract syntax
+  ## tree using the `astGenRepr` function. Printing is done *at compile time*.
+  ##
+  ## You can use this as a tool to write macros quicker by writing example
+  ## outputs and then copying the snippets into the macro for modification.
   ##
   ## See `dumpTree`.
 
@@ -766,6 +894,8 @@ template expectRoutine(node: NimNode) =
 proc name*(someProc: NimNode): NimNode {.compileTime.} =
   someProc.expectRoutine
   result = someProc[0]
+  if result.kind == nnkPostfix:
+    result = result[1]
 proc `name=`*(someProc: NimNode; val: NimNode) {.compileTime.} =
   someProc.expectRoutine
   someProc[0] = val
diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim
index c4b2ceca3..72b139202 100644
--- a/lib/core/typeinfo.nim
+++ b/lib/core/typeinfo.nim
@@ -97,7 +97,7 @@ proc newObj(typ: PNimType, size: int): pointer {.importCompilerProc.}
 proc newSeq(typ: PNimType, len: int): pointer {.importCompilerProc.}
 proc objectInit(dest: pointer, typ: PNimType) {.importCompilerProc.}
 
-template `+!!`(a, b: expr): expr = cast[pointer](cast[ByteAddress](a) + b)
+template `+!!`(a, b): untyped = cast[pointer](cast[ByteAddress](a) + b)
 
 proc getDiscriminant(aa: pointer, n: ptr TNimNode): int =
   assert(n.kind == nkCase)
diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim
index 415078126..153db9ed8 100644
--- a/lib/deprecated/pure/sockets.nim
+++ b/lib/deprecated/pure/sockets.nim
@@ -42,6 +42,8 @@ from times import epochTime
 
 when defined(ssl):
   import openssl
+else:
+  type SSLAcceptResult = int
 
 when defined(Windows):
   import winlean
@@ -206,16 +208,16 @@ proc htons*(x: int16): int16 =
   ## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
   result = sockets.ntohs(x)
 
-template ntohl(x: uint32): expr =
+template ntohl(x: uint32): uint32 =
   cast[uint32](sockets.ntohl(cast[int32](x)))
 
-template ntohs(x: uint16): expr =
+template ntohs(x: uint16): uint16 =
   cast[uint16](sockets.ntohs(cast[int16](x)))
 
-template htonl(x: uint32): expr =
+template htonl(x: uint32): uint32 =
   sockets.ntohl(x)
 
-template htons(x: uint16): expr =
+template htons(x: uint16): uint16 =
   sockets.ntohs(x)
 
 when defined(Posix):
@@ -442,14 +444,13 @@ proc parseIp4*(s: string): BiggestInt =
   if s[i] != '\0': invalidIp4(s)
   result = BiggestInt(a shl 24 or b shl 16 or c shl 8 or d)
 
-template gaiNim(a, p, h, list: expr): stmt =
-  block:
-    var gaiResult = getaddrinfo(a, $p, addr(h), list)
-    if gaiResult != 0'i32:
-      when defined(windows):
-        raiseOSError(osLastError())
-      else:
-        raiseOSError(osLastError(), $gai_strerror(gaiResult))
+template gaiNim(a, p, h, list: untyped): untyped =
+  var gaiResult = getaddrinfo(a, $p, addr(h), list)
+  if gaiResult != 0'i32:
+    when defined(windows):
+      raiseOSError(osLastError())
+    else:
+      raiseOSError(osLastError(), $gai_strerror(gaiResult))
 
 proc bindAddr*(socket: Socket, port = Port(0), address = "") {.
   tags: [ReadIOEffect].} =
@@ -493,8 +494,8 @@ proc getSockName*(socket: Socket): Port =
     raiseOSError(osLastError())
   result = Port(sockets.ntohs(name.sin_port))
 
-template acceptAddrPlain(noClientRet, successRet: expr,
-                         sslImplementation: stmt): stmt {.immediate.} =
+template acceptAddrPlain(noClientRet, successRet: SSLAcceptResult or int,
+                         sslImplementation: untyped): untyped =
   assert(client != nil)
   var sockAddress: Sockaddr_in
   var addrLen = sizeof(sockAddress).SockLen
@@ -550,7 +551,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string) {.
   ##
   ## **Warning:** When using SSL with non-blocking sockets, it is best to use
   ## the acceptAddrSSL procedure as this procedure will most likely block.
-  acceptAddrPlain(-1, -1):
+  acceptAddrPlain(SSLAcceptResult(-1), SSLAcceptResult(-1)):
     when defined(ssl):
       if server.isSSL:
         # We must wrap the client sock in a ssl context.
@@ -594,7 +595,7 @@ when defined(ssl):
     ##
     ## ``AcceptNoClient`` will be returned when no client is currently attempting
     ## to connect.
-    template doHandshake(): stmt =
+    template doHandshake(): untyped =
       when defined(ssl):
         if server.isSSL:
           client.setBlocking(false)
@@ -1278,7 +1279,7 @@ proc recvLine*(socket: Socket, line: var TaintedString, timeout = -1): bool {.
   ## **Deprecated since version 0.9.2**: This function has been deprecated in
   ## favour of readLine.
 
-  template addNLIfEmpty(): stmt =
+  template addNLIfEmpty(): untyped =
     if line.len == 0:
       line.add("\c\L")
 
@@ -1319,7 +1320,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1) {.
   ## A timeout can be specified in milliseconds, if data is not received within
   ## the specified time an ETimeout exception will be raised.
 
-  template addNLIfEmpty(): stmt =
+  template addNLIfEmpty(): untyped =
     if line.len == 0:
       line.add("\c\L")
 
diff --git a/lib/impure/nre/private/util.nim b/lib/impure/nre/private/util.nim
index 253bfada7..12d2506ea 100644
--- a/lib/impure/nre/private/util.nim
+++ b/lib/impure/nre/private/util.nim
@@ -16,7 +16,7 @@ proc checkNil(arg: string): string =
   else:
     return arg
 
-template formatStr*(howExpr, namegetter, idgetter: expr): expr =
+template formatStr*(howExpr, namegetter, idgetter): untyped =
   let how = howExpr
   var val = newStringOfCap(how.len)
   var i = 0
diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim
index 3b3f38e41..0d359d9e6 100644
--- a/lib/js/jsffi.nim
+++ b/lib/js/jsffi.nim
@@ -408,20 +408,19 @@ macro `{}`*(typ: typedesc, xs: varargs[untyped]): auto =
         kString = quote do:
           when compiles($`k`): $`k` else: "invalid"
         v = x[1]
-      body.add(quote do:
+      body.add quote do:
         when compiles(`a`.`k`):
           `a`.`k` = `v`
         elif compiles(`a`[`k`]):
           `a`[`k`] = `v`
         else:
           `a`[`kString`] = `v`
-      )
+
     else:
       error("Expression `" & $x.toStrLit & "` not allowed in `{}` macro")
 
-  body.add(quote do:
+  body.add quote do:
     return `a`
-  )
 
   result = quote do:
     proc inner(): `typ` {.gensym.} =
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index ce63d780c..13016bfc0 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -765,7 +765,7 @@ proc renderTocEntries*(d: var RstGenerator, j: var int, lvl: int,
     result.add(tmp)
 
 proc renderImage(d: PDoc, n: PRstNode, result: var string) =
-  template valid(s): expr =
+  template valid(s): bool =
     s.len > 0 and allCharsInSet(s, {'.','/',':','%','_','\\','\128'..'\xFF'} +
                                    Digits + Letters + WhiteSpace)
   let
@@ -1194,7 +1194,7 @@ proc defaultConfig*(): StringTableRef =
   ## ``rstToHtml`` to generate the bare minimum HTML.
   result = newStringTable(modeStyleInsensitive)
 
-  template setConfigVar(key, val: expr) =
+  template setConfigVar(key, val) =
     result[key] = val
 
   # If you need to modify these values, it might be worth updating the template
diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim
index 55d1dd2eb..b635c0b0b 100644
--- a/lib/posix/posix.nim
+++ b/lib/posix/posix.nim
@@ -92,7 +92,7 @@ else:
 # There used to be this name in posix.nim a long time ago, not sure why!
 {.deprecated: [cSIG_HOLD: SIG_HOLD].}
 
-when not defined(macosx):
+when not defined(macosx) and not defined(android):
   proc st_atime*(s: Stat): Time {.inline.} =
     ## Second-granularity time of last access
     result = s.st_atim.tv_sec
diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim
index b1cc244b7..69f6acfb8 100644
--- a/lib/posix/posix_other.nim
+++ b/lib/posix/posix_other.nim
@@ -15,7 +15,7 @@ const
   hasSpawnH = not defined(haiku) # should exist for every Posix system nowadays
   hasAioH = defined(linux)
 
-when defined(linux):
+when defined(linux) and not defined(android):
   # On Linux:
   # timer_{create,delete,settime,gettime},
   # clock_{getcpuclockid, getres, gettime, nanosleep, settime} lives in librt
@@ -46,9 +46,9 @@ type
     d_ino*: Ino  ## File serial number.
     when defined(dragonfly):
       # DragonflyBSD doesn't have `d_reclen` field.
-      d_type*: uint8 
+      d_type*: uint8
     elif defined(linux) or defined(macosx) or defined(freebsd) or
-         defined(netbsd) or defined(openbsd):
+         defined(netbsd) or defined(openbsd) or defined(genode):
       d_reclen*: cshort ## Length of this record. (not POSIX)
       d_type*: int8 ## Type of file; not supported by all filesystem types.
                     ## (not POSIX)
@@ -215,7 +215,7 @@ type
                            ## For a typed memory object, the length in bytes.
                            ## For other file types, the use of this field is
                            ## unspecified.
-    when defined(macosx):
+    when defined(macosx) or defined(android):
       st_atime*: Time      ## Time of last access.
       st_mtime*: Time      ## Time of last data modification.
       st_ctime*: Time      ## Time of last status change.
@@ -572,7 +572,8 @@ else:
     MAP_POPULATE*: cint = 0
 
 when defined(linux) or defined(nimdoc):
-  when defined(alpha) or defined(mips) or defined(parisc) or
+  when defined(alpha) or defined(mips) or defined(mipsel) or
+      defined(mips64) or defined(mips64el) or defined(parisc) or
       defined(sparc) or defined(nimdoc):
     const SO_REUSEPORT* = cint(0x0200)
       ## Multiple binding: load balancing on incoming TCP connections
diff --git a/lib/posix/termios.nim b/lib/posix/termios.nim
index 21b21aefb..f86e408fb 100644
--- a/lib/posix/termios.nim
+++ b/lib/posix/termios.nim
@@ -174,7 +174,7 @@ var
 # Compare a character C to a value VAL from the `cc' array in a
 #   `struct termios'.  If VAL is _POSIX_VDISABLE, no character can match it.
 
-template cceq*(val, c: expr): expr =
+template cceq*(val, c): untyped =
   c == val and val != POSIX_VDISABLE
 
 # Return the output baud rate stored in *TERMIOS_P.
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 8c1cf6b18..281d5b848 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -9,11 +9,12 @@
 
 include "system/inclrtl"
 
-import os, tables, strutils, times, heapqueue, options
-
+import os, tables, strutils, times, heapqueue, options, asyncstreams
+import asyncfutures except callSoon
 import nativesockets, net, deques
 
 export Port, SocketFlag
+export asyncfutures, asyncstreams
 
 #{.injectStmt: newGcInvariant().}
 
@@ -159,8 +160,6 @@ export Port, SocketFlag
 
 # TODO: Check if yielded future is nil and throw a more meaningful exception
 
-include includes/asyncfutures
-
 type
   PDispatcherBase = ref object of RootRef
     timers*: HeapQueue[tuple[finishAt: float, fut: Future[void]]]
@@ -190,6 +189,12 @@ proc adjustedTimeout(p: PDispatcherBase, timeout: int): int {.inline.} =
       result = int((timerTimeout - curTime) * 1000)
       if result < 0: result = 0
 
+proc callSoon(cbproc: proc ()) {.gcsafe.}
+
+proc initCallSoonProc =
+  if asyncfutures.getCallSoonProc().isNil:
+    asyncfutures.setCallSoonProc(callSoon)
+
 when defined(windows) or defined(nimdoc):
   import winlean, sets, hashes
   type
@@ -237,15 +242,17 @@ when defined(windows) or defined(nimdoc):
     result.callbacks = initDeque[proc ()](64)
 
   var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
-  proc getGlobalDispatcher*(): PDispatcher =
-    ## Retrieves the global thread-local dispatcher.
-    if gDisp.isNil: gDisp = newDispatcher()
-    result = gDisp
 
   proc setGlobalDispatcher*(disp: PDispatcher) =
     if not gDisp.isNil:
       assert gDisp.callbacks.len == 0
     gDisp = disp
+    initCallSoonProc()
+
+  proc getGlobalDispatcher*(): PDispatcher =
+    if gDisp.isNil:
+      setGlobalDispatcher(newDispatcher())
+    result = gDisp
 
   proc register*(fd: AsyncFD) =
     ## Registers ``fd`` with the dispatcher.
@@ -932,14 +939,17 @@ else:
     result.callbacks = initDeque[proc ()](64)
 
   var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
-  proc getGlobalDispatcher*(): PDispatcher =
-    if gDisp.isNil: gDisp = newDispatcher()
-    result = gDisp
 
   proc setGlobalDispatcher*(disp: PDispatcher) =
     if not gDisp.isNil:
       assert gDisp.callbacks.len == 0
     gDisp = disp
+    initCallSoonProc()
+
+  proc getGlobalDispatcher*(): PDispatcher =
+    if gDisp.isNil:
+      setGlobalDispatcher(newDispatcher())
+    result = gDisp
 
   proc update(fd: AsyncFD, events: set[Event]) =
     let p = getGlobalDispatcher()
@@ -1307,7 +1317,7 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async, deprecated.} =
   ##
   ## **Deprecated since version 0.15.0**: Use ``asyncnet.recvLine()`` instead.
 
-  template addNLIfEmpty(): stmt =
+  template addNLIfEmpty(): untyped =
     if result.len == 0:
       result.add("\c\L")
 
@@ -1327,7 +1337,7 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async, deprecated.} =
       return
     add(result, c)
 
-proc callSoon*(cbproc: proc ()) =
+proc callSoon(cbproc: proc ()) =
   ## Schedule `cbproc` to be called as soon as possible.
   ## The callback is called when control returns to the event loop.
   getGlobalDispatcher().callbacks.addLast(cbproc)
diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim
index 8fb30075c..9f4da16a3 100644
--- a/lib/pure/asyncfile.nim
+++ b/lib/pure/asyncfile.nim
@@ -81,27 +81,32 @@ proc getFileSize*(f: AsyncFile): int64 =
   else:
     result = lseek(f.fd.cint, 0, SEEK_END)
 
+proc newAsyncFile*(fd: AsyncFd): AsyncFile =
+  ## Creates `AsyncFile` with a previously opened file descriptor `fd`.
+  new result
+  result.fd = fd
+  register(result.fd)
+
 proc openAsync*(filename: string, mode = fmRead): AsyncFile =
   ## Opens a file specified by the path in ``filename`` using
   ## the specified ``mode`` asynchronously.
-  new result
   when defined(windows) or defined(nimdoc):
     let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL
     let desiredAccess = getDesiredAccess(mode)
     let creationDisposition = getCreationDisposition(mode, filename)
     when useWinUnicode:
-      result.fd = createFileW(newWideCString(filename), desiredAccess,
+      let fd = createFileW(newWideCString(filename), desiredAccess,
           FILE_SHARE_READ,
           nil, creationDisposition, flags, 0).AsyncFd
     else:
-      result.fd = createFileA(filename, desiredAccess,
+      let fd = createFileA(filename, desiredAccess,
           FILE_SHARE_READ,
           nil, creationDisposition, flags, 0).AsyncFd
 
-    if result.fd.Handle == INVALID_HANDLE_VALUE:
+    if fd.Handle == INVALID_HANDLE_VALUE:
       raiseOSError(osLastError())
 
-    register(result.fd)
+    result = newAsyncFile(fd)
 
     if mode == fmAppend:
       result.offset = getFileSize(result)
@@ -110,11 +115,11 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
     let flags = getPosixFlags(mode)
     # RW (Owner), RW (Group), R (Other)
     let perm = S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH
-    result.fd = open(filename, flags, perm).AsyncFD
-    if result.fd.cint == -1:
+    let fd = open(filename, flags, perm).AsyncFD
+    if fd.cint == -1:
       raiseOSError(osLastError())
 
-    register(result.fd)
+    result = newAsyncFile(fd)
 
 proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
   ## Read ``size`` bytes from the specified file asynchronously starting at
diff --git a/lib/pure/includes/asyncfutures.nim b/lib/pure/asyncfutures.nim
index 6af5bf3cf..bebd19611 100644
--- a/lib/pure/includes/asyncfutures.nim
+++ b/lib/pure/asyncfutures.nim
@@ -1,8 +1,16 @@
+import os, tables, strutils, times, heapqueue, options, deques
 
 # TODO: This shouldn't need to be included, but should ideally be exported.
 type
+  CallbackFunc = proc () {.closure, gcsafe.}
+
+  CallbackList = object
+    function: CallbackFunc
+    next: ref CallbackList
+
   FutureBase* = ref object of RootObj ## Untyped future.
-    cb: proc () {.closure,gcsafe.}
+    callbacks: CallbackList
+
     finished: bool
     error*: ref Exception ## Stored exception
     errorStackTrace*: string
@@ -16,12 +24,6 @@ type
 
   FutureVar*[T] = distinct Future[T]
 
-  FutureStream*[T] = ref object of FutureBase   ## Special future that acts as
-                                                ## a queue. Its API is still
-                                                ## experimental and so is
-                                                ## subject to change.
-    queue: Deque[T]
-
   FutureError* = object of Exception
     cause*: FutureBase
 
@@ -30,7 +32,27 @@ type
 when not defined(release):
   var currentID = 0
 
-proc callSoon*(cbproc: proc ()) {.gcsafe.}
+var callSoonProc {.threadvar.}: proc (cbproc: proc ()) {.gcsafe.}
+
+proc getCallSoonProc*(): (proc(cbproc: proc ()) {.gcsafe.}) =
+  ## Get current implementation of ``callSoon``.
+  return callSoonProc
+
+proc setCallSoonProc*(p: (proc(cbproc: proc ()) {.gcsafe.})) =
+  ## Change current implementation of ``callSoon``. This is normally called when dispatcher from ``asyncdispatcher`` is initialized.
+  callSoonProc = p
+
+proc callSoon*(cbproc: proc ()) =
+  ## Call ``cbproc`` "soon".
+  ##
+  ## If async dispatcher is running, ``cbproc`` will be executed during next dispatcher tick.
+  ##
+  ## If async dispatcher is not running, ``cbproc`` will be executed immediately.
+  if callSoonProc.isNil:
+    # Loop not initialized yet. Call the function directly to allow setup code to use futures.
+    cbproc()
+  else:
+    callSoonProc(cbproc)
 
 template setupFutureBase(fromProc: string) =
   new(result)
@@ -56,22 +78,6 @@ proc newFutureVar*[T](fromProc = "unspecified"): FutureVar[T] =
   ## that this future belongs to, is a good habit as it helps with debugging.
   result = FutureVar[T](newFuture[T](fromProc))
 
-proc newFutureStream*[T](fromProc = "unspecified"): FutureStream[T] =
-  ## Create a new ``FutureStream``. This future's callback is activated when
-  ## two events occur:
-  ##
-  ## * New data is written into the future stream.
-  ## * The future stream is completed (this means that no more data will be
-  ##   written).
-  ##
-  ## Specifying ``fromProc``, which is a string specifying the name of the proc
-  ## that this future belongs to, is a good habit as it helps with debugging.
-  ##
-  ## **Note:** The API of FutureStream is still new and so has a higher
-  ## likelihood of changing in the future.
-  setupFutureBase(fromProc)
-  result.queue = initDeque[T]()
-
 proc clean*[T](future: FutureVar[T]) =
   ## Resets the ``finished`` status of ``future``.
   Future[T](future).finished = false
@@ -98,6 +104,33 @@ proc checkFinished[T](future: Future[T]) =
       err.cause = future
       raise err
 
+proc call(callbacks: var CallbackList) =
+  var current = callbacks
+
+  while true:
+    if not current.function.isNil:
+      callSoon(current.function)
+
+    if current.next.isNil:
+      break
+    else:
+      current = current.next[]
+
+  # callback will be called only once, let GC collect them now
+  callbacks.next = nil
+  callbacks.function = nil
+
+proc add(callbacks: var CallbackList, function: CallbackFunc) =
+  if callbacks.function.isNil:
+    callbacks.function = function
+    assert callbacks.next == nil
+  else:
+    let newNext = new(ref CallbackList)
+    newNext.function = callbacks.function
+    newNext.next = callbacks.next
+    callbacks.next = newNext
+    callbacks.function = function
+
 proc complete*[T](future: Future[T], val: T) =
   ## Completes ``future`` with value ``val``.
   #assert(not future.finished, "Future already finished, cannot finish twice.")
@@ -105,8 +138,7 @@ proc complete*[T](future: Future[T], val: T) =
   assert(future.error == nil)
   future.value = val
   future.finished = true
-  if future.cb != nil:
-    future.cb()
+  future.callbacks.call()
 
 proc complete*(future: Future[void]) =
   ## Completes a void ``future``.
@@ -114,8 +146,7 @@ proc complete*(future: Future[void]) =
   checkFinished(future)
   assert(future.error == nil)
   future.finished = true
-  if future.cb != nil:
-    future.cb()
+  future.callbacks.call()
 
 proc complete*[T](future: FutureVar[T]) =
   ## Completes a ``FutureVar``.
@@ -123,8 +154,7 @@ proc complete*[T](future: FutureVar[T]) =
   checkFinished(fut)
   assert(fut.error == nil)
   fut.finished = true
-  if fut.cb != nil:
-    fut.cb()
+  fut.callbacks.call()
 
 proc complete*[T](future: FutureVar[T], val: T) =
   ## Completes a ``FutureVar`` with value ``val``.
@@ -135,14 +165,7 @@ proc complete*[T](future: FutureVar[T], val: T) =
   assert(fut.error.isNil())
   fut.finished = true
   fut.value = val
-  if not fut.cb.isNil():
-    fut.cb()
-
-proc complete*[T](future: FutureStream[T]) =
-  ## Completes a ``FutureStream`` signalling the end of data.
-  future.finished = true
-  if not future.cb.isNil():
-    future.cb()
+  fut.callbacks.call()
 
 proc fail*[T](future: Future[T], error: ref Exception) =
   ## Completes ``future`` with ``error``.
@@ -152,26 +175,40 @@ proc fail*[T](future: Future[T], error: ref Exception) =
   future.error = error
   future.errorStackTrace =
     if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error)
-  if future.cb != nil:
-    future.cb()
+  future.callbacks.call()
+
+proc clearCallbacks(future: FutureBase) =
+  future.callbacks.function = nil
+  future.callbacks.next = nil
+
+proc addCallback*(future: FutureBase, cb: proc() {.closure,gcsafe.}) =
+  ## Adds the callbacks proc to be called when the future completes.
+  ##
+  ## If future has already completed then ``cb`` will be called immediately.
+  assert cb != nil
+  if future.finished:
+    callSoon(cb)
   else:
-    # This is to prevent exceptions from being silently ignored when a future
-    # is discarded.
-    # TODO: This may turn out to be a bad idea.
-    # Turns out this is a bad idea.
-    #raise error
-    discard
+    future.callbacks.add cb
+
+proc addCallback*[T](future: Future[T],
+                     cb: proc (future: Future[T]) {.closure,gcsafe.}) =
+  ## Adds the callbacks proc to be called when the future completes.
+  ##
+  ## If future has already completed then ``cb`` will be called immediately.
+  future.addCallback(
+    proc() =
+      cb(future)
+  )
 
 proc `callback=`*(future: FutureBase, cb: proc () {.closure,gcsafe.}) =
-  ## Sets the callback proc to be called when the future completes.
+  ## Clears the list of callbacks and sets the callback proc to be called when the future completes.
   ##
   ## If future has already completed then ``cb`` will be called immediately.
   ##
-  ## **Note**: You most likely want the other ``callback`` setter which
-  ## passes ``future`` as a param to the callback.
-  future.cb = cb
-  if future.finished:
-    callSoon(future.cb)
+  ## It's recommended to use ``addCallback`` or ``then`` instead.
+  future.clearCallbacks
+  future.addCallback cb
 
 proc `callback=`*[T](future: Future[T],
     cb: proc (future: Future[T]) {.closure,gcsafe.}) =
@@ -180,20 +217,6 @@ proc `callback=`*[T](future: Future[T],
   ## If future has already completed then ``cb`` will be called immediately.
   future.callback = proc () = cb(future)
 
-proc `callback=`*[T](future: FutureStream[T],
-    cb: proc (future: FutureStream[T]) {.closure,gcsafe.}) =
-  ## Sets the callback proc to be called when data was placed inside the
-  ## future stream.
-  ##
-  ## The callback is also called when the future is completed. So you should
-  ## use ``finished`` to check whether data is available.
-  ##
-  ## If the future stream already has data or is finished then ``cb`` will be
-  ## called immediately.
-  future.cb = proc () = cb(future)
-  if future.queue.len > 0 or future.finished:
-    callSoon(future.cb)
-
 proc injectStacktrace[T](future: Future[T]) =
   # TODO: Come up with something better.
   when not defined(release):
@@ -240,18 +263,12 @@ proc mget*[T](future: FutureVar[T]): var T =
   ## Future has not been finished.
   result = Future[T](future).value
 
-proc finished*[T](future: Future[T] | FutureVar[T] | FutureStream[T]): bool =
+proc finished*[T](future: Future[T] | FutureVar[T]): bool =
   ## Determines whether ``future`` has completed.
   ##
   ## ``True`` may indicate an error or a value. Use ``failed`` to distinguish.
-  ##
-  ## For a ``FutureStream`` a ``true`` value means that no more data will be
-  ## placed inside the stream _and_ that there is no data waiting to be
-  ## retrieved.
   when future is FutureVar[T]:
     result = (Future[T](future)).finished
-  elif future is FutureStream[T]:
-    result = future.finished and future.queue.len == 0
   else:
     result = future.finished
 
@@ -259,57 +276,6 @@ proc failed*(future: FutureBase): bool =
   ## Determines whether ``future`` completed with an error.
   return future.error != nil
 
-proc write*[T](future: FutureStream[T], value: T): Future[void] =
-  ## Writes the specified value inside the specified future stream.
-  ##
-  ## This will raise ``ValueError`` if ``future`` is finished.
-  result = newFuture[void]("FutureStream.put")
-  if future.finished:
-    let msg = "FutureStream is finished and so no longer accepts new data."
-    result.fail(newException(ValueError, msg))
-    return
-  # TODO: Implement limiting of the streams storage to prevent it growing
-  # infinitely when no reads are occuring.
-  future.queue.addLast(value)
-  if not future.cb.isNil: future.cb()
-  result.complete()
-
-proc read*[T](future: FutureStream[T]): Future[(bool, T)] =
-  ## Returns a future that will complete when the ``FutureStream`` has data
-  ## placed into it. The future will be completed with the oldest
-  ## value stored inside the stream. The return value will also determine
-  ## whether data was retrieved, ``false`` means that the future stream was
-  ## completed and no data was retrieved.
-  ##
-  ## This function will remove the data that was returned from the underlying
-  ## ``FutureStream``.
-  var resFut = newFuture[(bool, T)]("FutureStream.take")
-  let savedCb = future.cb
-  future.callback =
-    proc (fs: FutureStream[T]) =
-      # We don't want this callback called again.
-      future.cb = nil
-
-      # The return value depends on whether the FutureStream has finished.
-      var res: (bool, T)
-      if finished(fs):
-        # Remember, this callback is called when the FutureStream is completed.
-        res[0] = false
-      else:
-        res[0] = true
-        res[1] = fs.queue.popFirst()
-
-      if not resFut.finished:
-        resFut.complete(res)
-
-      # If the saved callback isn't nil then let's call it.
-      if not savedCb.isNil: savedCb()
-  return resFut
-
-proc len*[T](future: FutureStream[T]): int =
-  ## Returns the amount of data pieces inside the stream.
-  future.queue.len
-
 proc asyncCheck*[T](future: Future[T]) =
   ## Sets a callback on ``future`` which raises an exception if the future
   ## finished with an error.
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index a374e80e8..b7b57a82f 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -127,7 +127,7 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
   i.inc protocol.parseInt(result.minor, i)
 
 proc sendStatus(client: AsyncSocket, status: string): Future[void] =
-  client.send("HTTP/1.1 " & status & "\c\L\c\L") 
+  client.send("HTTP/1.1 " & status & "\c\L\c\L")
 
 proc processClient(client: AsyncSocket, address: string,
                    callback: proc (request: Request):
@@ -233,7 +233,7 @@ proc processClient(client: AsyncSocket, address: string,
           await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
           continue
     elif request.reqMethod == HttpPost:
-      await request.respond(Http400, "Bad Request. No Content-Length.")
+      await request.respond(Http411, "Content-Length required.")
       continue
 
     # Call the user's callback.
diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim
index 89b216b25..6e7d7993f 100644
--- a/lib/pure/asyncmacro.nim
+++ b/lib/pure/asyncmacro.nim
@@ -28,7 +28,7 @@ template createCb(retFutureSym, iteratorNameSym,
                   name, futureVarCompletions: untyped) =
   var nameIterVar = iteratorNameSym
   #{.push stackTrace: off.}
-  proc cb {.closure,gcsafe.} =
+  proc cb0 {.closure.} =
     try:
       if not nameIterVar.finished:
         var next = nameIterVar()
@@ -38,7 +38,10 @@ template createCb(retFutureSym, iteratorNameSym,
                     "`nil` Future?"
             raise newException(AssertionError, msg % name)
         else:
-          next.callback = cb
+          {.gcsafe.}:
+            {.push hint[ConvFromXtoItselfNotNeeded]: off.}
+            next.callback = (proc() {.closure, gcsafe.})(cb0)
+            {.pop.}
     except:
       futureVarCompletions
 
@@ -49,7 +52,7 @@ template createCb(retFutureSym, iteratorNameSym,
       else:
         retFutureSym.fail(getCurrentException())
 
-  cb()
+  cb0()
   #{.pop.}
 proc generateExceptionCheck(futSym,
     tryStmt, rootReceiver, fromNode: NimNode): NimNode {.compileTime.} =
@@ -379,7 +382,10 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
                                   procBody, nnkIteratorDef)
     closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom=prc.body)
     closureIterator.addPragma(newIdentNode("closure"))
-    closureIterator.addPragma(newIdentNode("gcsafe"))
+
+    # If proc has an explicit gcsafe pragma, we add it to iterator as well.
+    if prc.pragma.findChild(it.kind in {nnkSym, nnkIdent} and $it == "gcsafe") != nil:
+      closureIterator.addPragma(newIdentNode("gcsafe"))
     outerProcBody.add(closureIterator)
 
     # -> createCb(retFuture)
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index 5de65efe0..5be457d2a 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -220,7 +220,7 @@ when defineSsl:
       raiseSSLError("Cannot appease SSL.")
 
   template sslLoop(socket: AsyncSocket, flags: set[SocketFlag],
-                   op: expr) =
+                   op: untyped) =
     var opResult {.inject.} = -1.cint
     while opResult < 0:
       # Call the desired operation.
@@ -490,7 +490,7 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
   # them when the result future is completed.
   # Can we replace the result future with the FutureVar?
 
-  template addNLIfEmpty(): stmt =
+  template addNLIfEmpty(): untyped =
     if resString.mget.len == 0:
       resString.mget.add("\c\L")
 
diff --git a/lib/pure/asyncstreams.nim b/lib/pure/asyncstreams.nim
new file mode 100644
index 000000000..d3ea143f3
--- /dev/null
+++ b/lib/pure/asyncstreams.nim
@@ -0,0 +1,105 @@
+import asyncfutures
+
+import deques
+
+type
+  FutureStream*[T] = ref object   ## Special future that acts as
+                                  ## a queue. Its API is still
+                                  ## experimental and so is
+                                  ## subject to change.
+    queue: Deque[T]
+    finished: bool
+    cb: proc () {.closure, gcsafe.}
+
+proc newFutureStream*[T](fromProc = "unspecified"): FutureStream[T] =
+  ## Create a new ``FutureStream``. This future's callback is activated when
+  ## two events occur:
+  ##
+  ## * New data is written into the future stream.
+  ## * The future stream is completed (this means that no more data will be
+  ##   written).
+  ##
+  ## Specifying ``fromProc``, which is a string specifying the name of the proc
+  ## that this future belongs to, is a good habit as it helps with debugging.
+  ##
+  ## **Note:** The API of FutureStream is still new and so has a higher
+  ## likelihood of changing in the future.
+  result = FutureStream[T](finished: false, cb: nil)
+  result.queue = initDeque[T]()
+
+proc complete*[T](future: FutureStream[T]) =
+  ## Completes a ``FutureStream`` signalling the end of data.
+  future.finished = true
+  if not future.cb.isNil:
+    future.cb()
+
+proc `callback=`*[T](future: FutureStream[T],
+    cb: proc (future: FutureStream[T]) {.closure,gcsafe.}) =
+  ## Sets the callback proc to be called when data was placed inside the
+  ## future stream.
+  ##
+  ## The callback is also called when the future is completed. So you should
+  ## use ``finished`` to check whether data is available.
+  ##
+  ## If the future stream already has data or is finished then ``cb`` will be
+  ## called immediately.
+  future.cb = proc () = cb(future)
+  if future.queue.len > 0 or future.finished:
+    callSoon(future.cb)
+
+proc finished*[T](future: FutureStream[T]): bool =
+  ## Check if a ``FutureStream`` is finished. ``true`` value means that
+  ## no more data will be placed inside the stream _and_ that there is
+  ## no data waiting to be retrieved.
+  result = future.finished and future.queue.len == 0
+
+proc write*[T](future: FutureStream[T], value: T): Future[void] =
+  ## Writes the specified value inside the specified future stream.
+  ##
+  ## This will raise ``ValueError`` if ``future`` is finished.
+  result = newFuture[void]("FutureStream.put")
+  if future.finished:
+    let msg = "FutureStream is finished and so no longer accepts new data."
+    result.fail(newException(ValueError, msg))
+    return
+  # TODO: Implement limiting of the streams storage to prevent it growing
+  # infinitely when no reads are occuring.
+  future.queue.addLast(value)
+  if not future.cb.isNil: future.cb()
+  result.complete()
+
+proc read*[T](future: FutureStream[T]): Future[(bool, T)] =
+  ## Returns a future that will complete when the ``FutureStream`` has data
+  ## placed into it. The future will be completed with the oldest
+  ## value stored inside the stream. The return value will also determine
+  ## whether data was retrieved, ``false`` means that the future stream was
+  ## completed and no data was retrieved.
+  ##
+  ## This function will remove the data that was returned from the underlying
+  ## ``FutureStream``.
+  var resFut = newFuture[(bool, T)]("FutureStream.take")
+  let savedCb = future.cb
+  future.callback =
+    proc (fs: FutureStream[T]) =
+      # We don't want this callback called again.
+      future.cb = nil
+
+      # The return value depends on whether the FutureStream has finished.
+      var res: (bool, T)
+      if finished(fs):
+        # Remember, this callback is called when the FutureStream is completed.
+        res[0] = false
+      else:
+        res[0] = true
+        res[1] = fs.queue.popFirst()
+
+      if not resFut.finished:
+        resFut.complete(res)
+
+      # If the saved callback isn't nil then let's call it.
+      if not savedCb.isNil: savedCb()
+  return resFut
+
+proc len*[T](future: FutureStream[T]): int =
+  ## Returns the amount of data pieces inside the stream.
+  future.queue.len
diff --git a/lib/pure/basic2d.nim b/lib/pure/basic2d.nim
index e4696c6a8..31b3814d6 100644
--- a/lib/pure/basic2d.nim
+++ b/lib/pure/basic2d.nim
@@ -116,13 +116,13 @@ proc safeArccos(v:float):float=
   return arccos(clamp(v,-1.0,1.0))
 
 
-template makeBinOpVector(s:expr)=
+template makeBinOpVector(s) =
   ## implements binary operators ``+``, ``-``, ``*`` and ``/`` for vectors
   proc s*(a,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y))
   proc s*(a:Vector2d,b:float):Vector2d {.inline,noInit.}  = vector2d(s(a.x,b),s(a.y,b))
   proc s*(a:float,b:Vector2d):Vector2d {.inline,noInit.}  = vector2d(s(a,b.x),s(a,b.y))
 
-template makeBinOpAssignVector(s:expr)=
+template makeBinOpAssignVector(s)=
   ## implements inplace binary operators ``+=``, ``-=``, ``/=`` and ``*=`` for vectors
   proc s*(a:var Vector2d,b:Vector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y)
   proc s*(a:var Vector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b)
@@ -853,5 +853,3 @@ proc degToRad*(deg:float):float {.inline.}=
 proc radToDeg*(rad:float):float {.inline.}=
   ## converts `rad` radians to degrees
   rad * RAD2DEGCONST
-
-
diff --git a/lib/pure/basic3d.nim b/lib/pure/basic3d.nim
index f7a9c237c..e2d2464c0 100644
--- a/lib/pure/basic3d.nim
+++ b/lib/pure/basic3d.nim
@@ -116,7 +116,7 @@ proc safeArccos(v:float):float=
   ## due to rounding issues
   return arccos(clamp(v,-1.0,1.0))
 
-template makeBinOpVector(s:expr)=
+template makeBinOpVector(s) =
   proc s*(a,b:Vector3d):Vector3d {.inline,noInit.} =
     vector3d(s(a.x,b.x),s(a.y,b.y),s(a.z,b.z))
   proc s*(a:Vector3d,b:float):Vector3d {.inline,noInit.}  =
@@ -124,7 +124,7 @@ template makeBinOpVector(s:expr)=
   proc s*(a:float,b:Vector3d):Vector3d {.inline,noInit.}  =
     vector3d(s(a,b.x),s(a,b.y),s(a,b.z))
 
-template makeBinOpAssignVector(s:expr)=
+template makeBinOpAssignVector(s) =
   proc s*(a:var Vector3d,b:Vector3d) {.inline.} =
     s(a.x,b.x); s(a.y,b.y); s(a.z,b.z)
   proc s*(a:var Vector3d,b:float) {.inline.} =
diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim
index 519c58653..5f84f3101 100644
--- a/lib/pure/collections/critbits.nim
+++ b/lib/pure/collections/critbits.nim
@@ -143,15 +143,16 @@ proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) =
   var n = rawInsert(c, key)
   n.val = val
 
-template get[T](c: CritBitTree[T], key: string): T {.immediate.} =
+template get[T](c: CritBitTree[T], key: string): T =
   let n = rawGet(c, key)
-  if n != nil: result = n.val
-  else:
+  if n == nil:
     when compiles($key):
       raise newException(KeyError, "key not found: " & $key)
     else:
       raise newException(KeyError, "key not found")
 
+  n.val
+
 proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline, deprecatedGet.} =
   ## retrieves the value at ``c[key]``. If `key` is not in `t`, the
   ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim
index 78953228b..1bbe9f1ad 100644
--- a/lib/pure/collections/deques.nim
+++ b/lib/pure/collections/deques.nim
@@ -33,7 +33,7 @@
 ##     assert deq.peekLast == a
 ##
 ##     while deq.len > 0:  # checking if the deque is empty
-##       echo deq.removeLast()
+##       echo deq.popLast()
 ##
 ## Note: For inter thread communication use
 ## a `Channel <channels.html>`_ instead.
diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim
index 4ecac11be..334e33f2e 100644
--- a/lib/pure/collections/intsets.nim
+++ b/lib/pure/collections/intsets.nim
@@ -31,16 +31,18 @@ const
 
 type
   PTrunk = ref Trunk
-  Trunk {.final.} = object
+  Trunk = object
     next: PTrunk             # all nodes are connected with this pointer
     key: int                 # start address at bit 0
     bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector
 
   TrunkSeq = seq[PTrunk]
   IntSet* = object ## an efficient set of 'int' implemented as a sparse bit set
+    elems: int # only valid for small numbers
     counter, max: int
     head: PTrunk
     data: TrunkSeq
+    a: array[0..33, int] # profiling shows that 34 elements are enough
 
 {.deprecated: [TIntSet: IntSet, TTrunk: Trunk, TTrunkSeq: TrunkSeq].}
 
@@ -95,101 +97,154 @@ proc intSetPut(t: var IntSet, key: int): PTrunk =
 
 proc contains*(s: IntSet, key: int): bool =
   ## returns true iff `key` is in `s`.
-  var t = intSetGet(s, `shr`(key, TrunkShift))
-  if t != nil:
-    var u = key and TrunkMask
-    result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0
+  if s.elems <= s.a.len:
+    for i in 0..<s.elems:
+      if s.a[i] == key: return true
   else:
-    result = false
-
-proc incl*(s: var IntSet, key: int) =
-  ## includes an element `key` in `s`.
+    var t = intSetGet(s, `shr`(key, TrunkShift))
+    if t != nil:
+      var u = key and TrunkMask
+      result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0
+    else:
+      result = false
+
+proc bitincl(s: var IntSet, key: int) {.inline.} =
   var t = intSetPut(s, `shr`(key, TrunkShift))
   var u = key and TrunkMask
   t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or
       `shl`(1, u and IntMask)
 
+proc incl*(s: var IntSet, key: int) =
+  ## includes an element `key` in `s`.
+  if s.elems <= s.a.len:
+    for i in 0..<s.elems:
+      if s.a[i] == key: return
+    if s.elems < s.a.len:
+      s.a[s.elems] = key
+      inc s.elems
+      return
+    newSeq(s.data, InitIntSetSize)
+    s.max = InitIntSetSize-1
+    for i in 0..<s.elems:
+      bitincl(s, s.a[i])
+    s.elems = s.a.len + 1
+    # fall through:
+  bitincl(s, key)
+
 proc excl*(s: var IntSet, key: int) =
   ## excludes `key` from the set `s`.
-  var t = intSetGet(s, `shr`(key, TrunkShift))
-  if t != nil:
-    var u = key and TrunkMask
-    t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] and
-        not `shl`(1, u and IntMask)
+  if s.elems <= s.a.len:
+    for i in 0..<s.elems:
+      if s.a[i] == key:
+        s.a[i] = s.a[s.elems-1]
+        dec s.elems
+        return
+  else:
+    var t = intSetGet(s, `shr`(key, TrunkShift))
+    if t != nil:
+      var u = key and TrunkMask
+      t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] and
+          not `shl`(1, u and IntMask)
 
 proc containsOrIncl*(s: var IntSet, key: int): bool =
   ## returns true if `s` contains `key`, otherwise `key` is included in `s`
   ## and false is returned.
-  var t = intSetGet(s, `shr`(key, TrunkShift))
-  if t != nil:
-    var u = key and TrunkMask
-    result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0
-    if not result:
-      t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or
-          `shl`(1, u and IntMask)
-  else:
+  if s.elems <= s.a.len:
+    for i in 0..<s.elems:
+      if s.a[i] == key:
+        return true
     incl(s, key)
     result = false
+  else:
+    var t = intSetGet(s, `shr`(key, TrunkShift))
+    if t != nil:
+      var u = key and TrunkMask
+      result = (t.bits[`shr`(u, IntShift)] and `shl`(1, u and IntMask)) != 0
+      if not result:
+        t.bits[`shr`(u, IntShift)] = t.bits[`shr`(u, IntShift)] or
+            `shl`(1, u and IntMask)
+    else:
+      incl(s, key)
+      result = false
 
 proc initIntSet*: IntSet =
   ## creates a new int set that is empty.
-  newSeq(result.data, InitIntSetSize)
-  result.max = InitIntSetSize-1
+
+  #newSeq(result.data, InitIntSetSize)
+  #result.max = InitIntSetSize-1
+  result.data = nil
+  result.max = 0
   result.counter = 0
   result.head = nil
+  result.elems = 0
 
 proc clear*(result: var IntSet) =
-  setLen(result.data, InitIntSetSize)
-  for i in 0..InitIntSetSize-1: result.data[i] = nil
-  result.max = InitIntSetSize-1
+  #setLen(result.data, InitIntSetSize)
+  #for i in 0..InitIntSetSize-1: result.data[i] = nil
+  #result.max = InitIntSetSize-1
+  result.data = nil
+  result.max = 0
   result.counter = 0
   result.head = nil
+  result.elems = 0
 
-proc isNil*(x: IntSet): bool {.inline.} = x.head.isNil
+proc isNil*(x: IntSet): bool {.inline.} = x.head.isNil and x.elems == 0
 
 proc assign*(dest: var IntSet, src: IntSet) =
   ## copies `src` to `dest`. `dest` does not need to be initialized by
   ## `initIntSet`.
-  dest.counter = src.counter
-  dest.max = src.max
-  newSeq(dest.data, src.data.len)
+  if src.elems <= src.a.len:
+    dest.data = nil
+    dest.max = 0
+    dest.counter = src.counter
+    dest.head = nil
+    dest.elems = src.elems
+    dest.a = src.a
+  else:
+    dest.counter = src.counter
+    dest.max = src.max
+    newSeq(dest.data, src.data.len)
 
-  var it = src.head
-  while it != nil:
+    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 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
-    n.key = it.key
-    n.bits = it.bits
-    dest.head = n
-    dest.data[h] = n
+      var n: PTrunk
+      new(n)
+      n.next = dest.head
+      n.key = it.key
+      n.bits = it.bits
+      dest.head = n
+      dest.data[h] = n
 
-    it = it.next
+      it = it.next
 
 iterator items*(s: IntSet): int {.inline.} =
   ## iterates over any included element of `s`.
-  var r = s.head
-  while r != nil:
-    var i = 0
-    while i <= high(r.bits):
-      var w = r.bits[i]
-      # taking a copy of r.bits[i] here is correct, because
-      # modifying operations are not allowed during traversation
-      var j = 0
-      while w != 0:         # test all remaining bits for zero
-        if (w and 1) != 0:  # the bit is set!
-          yield (r.key shl TrunkShift) or (i shl IntShift +% j)
-        inc(j)
-        w = w shr 1
-      inc(i)
-    r = r.next
-
-template dollarImpl(): stmt =
+  if s.elems <= s.a.len:
+    for i in 0..<s.elems:
+      yield s.a[i]
+  else:
+    var r = s.head
+    while r != nil:
+      var i = 0
+      while i <= high(r.bits):
+        var w = r.bits[i]
+        # taking a copy of r.bits[i] here is correct, because
+        # modifying operations are not allowed during traversation
+        var j = 0
+        while w != 0:         # test all remaining bits for zero
+          if (w and 1) != 0:  # the bit is set!
+            yield (r.key shl TrunkShift) or (i shl IntShift +% j)
+          inc(j)
+          w = w shr 1
+        inc(i)
+      r = r.next
+
+template dollarImpl(): untyped =
   result = "{"
   for key in items(s):
     if result.len > 1: result.add(", ")
@@ -225,3 +280,9 @@ when isMainModule:
   ys.sort(cmp[int])
   assert ys == @[1, 2, 7, 1056]
 
+  var z: IntSet
+  for i in 0..1000:
+    incl z, i
+  for i in 0..1000:
+    assert i in z
+
diff --git a/lib/pure/collections/rtarrays.nim b/lib/pure/collections/rtarrays.nim
index 89a02553a..3849117a0 100644
--- a/lib/pure/collections/rtarrays.nim
+++ b/lib/pure/collections/rtarrays.nim
@@ -19,7 +19,7 @@ type
     L: Natural
     spart: seq[T]
     apart: array[ArrayPartSize, T]
-  UncheckedArray* {.unchecked.}[T] = array[0..100_000_000, T]
+  UncheckedArray* {.unchecked.}[T] = array[0, T]
 
 template usesSeqPart(x): untyped = x.L > ArrayPartSize
 
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index 19512d5f4..e8e725aa3 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -455,7 +455,7 @@ template anyIt*(seq1, pred: untyped): bool =
       break
   result
 
-template toSeq*(iter: untyped): untyped {.oldimmediate.} =
+template toSeq*(iter: untyped): untyped =
   ## Transforms any iterator into a sequence.
   ##
   ## Example:
diff --git a/lib/pure/collections/sharedstrings.nim b/lib/pure/collections/sharedstrings.nim
index 10ab30767..a9e194fb4 100644
--- a/lib/pure/collections/sharedstrings.nim
+++ b/lib/pure/collections/sharedstrings.nim
@@ -9,10 +9,8 @@
 
 ## Shared string support for Nim.
 
-const ArrayDummySize = when defined(cpu16): 10_000 else: 100_000_000
-
 type
-  UncheckedCharArray {.unchecked.} = array[0..ArrayDummySize, char]
+  UncheckedCharArray = UncheckedArray[char]
 
 type
   Buffer = ptr object
diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim
index de573bcb2..fc50ea41c 100644
--- a/lib/pure/collections/sharedtables.nim
+++ b/lib/pure/collections/sharedtables.nim
@@ -25,7 +25,7 @@ type
     counter, dataLen: int
     lock: Lock
 
-template maxHash(t): expr = t.dataLen-1
+template maxHash(t): untyped = t.dataLen-1
 
 include tableimpl
 
diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim
index c0d45c392..eec98fcaf 100644
--- a/lib/pure/collections/tableimpl.nim
+++ b/lib/pure/collections/tableimpl.nim
@@ -85,7 +85,7 @@ template addImpl(enlarge) {.dirty.} =
   rawInsert(t, t.data, key, val, hc, j)
   inc(t.counter)
 
-template maybeRehashPutImpl(enlarge) {.oldimmediate, dirty.} =
+template maybeRehashPutImpl(enlarge) {.dirty.} =
   if mustRehash(t.dataLen, t.counter):
     enlarge(t)
     index = rawGetKnownHC(t, key, hc)
@@ -93,7 +93,7 @@ template maybeRehashPutImpl(enlarge) {.oldimmediate, dirty.} =
   rawInsert(t, t.data, key, val, hc, index)
   inc(t.counter)
 
-template putImpl(enlarge) {.oldimmediate, dirty.} =
+template putImpl(enlarge) {.dirty.} =
   var hc: Hash
   var index = rawGet(t, key, hc)
   if index >= 0: t.data[index].val = val
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index 5b6701a12..01a42efab 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -648,7 +648,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
     var nxtt = t.data[ht].next
     var nxts = s.data[hs].next
     if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode):
-      if (s.data[hs].key != t.data[ht].key) and (s.data[hs].val != t.data[ht].val):
+      if (s.data[hs].key != t.data[ht].key) or (s.data[hs].val != t.data[ht].val):
         return false
     ht = nxtt
     hs = nxts
@@ -939,7 +939,7 @@ proc enlarge[A](t: var CountTable[A]) =
 
 proc `[]=`*[A](t: var CountTable[A], key: A, val: int) =
   ## puts a (key, value)-pair into `t`.
-  assert val > 0
+  assert val >= 0
   var h = rawGet(t, key)
   if h >= 0:
     t.data[h].val = val
@@ -1311,3 +1311,17 @@ when isMainModule:
     assert a == c
 
 
+  block: #6250
+    let
+      a = {3: 1}.toOrderedTable
+      b = {3: 2}.toOrderedTable
+    assert((a == b) == false)
+    assert((b == a) == false)
+
+  block: #6250
+    let
+      a = {3: 2}.toOrderedTable
+      b = {3: 2}.toOrderedTable
+    assert((a == b) == true)
+    assert((b == a) == true)
+
diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim
index f4c027576..4ec76dee0 100644
--- a/lib/pure/colors.nim
+++ b/lib/pure/colors.nim
@@ -19,18 +19,18 @@ type
 proc `==` *(a, b: Color): bool {.borrow.}
   ## compares two colors.
 
-template extract(a: Color, r, g, b: expr) {.immediate.}=
+template extract(a: Color, r, g, b: untyped) =
   var r = a.int shr 16 and 0xff
   var g = a.int shr 8 and 0xff
   var b = a.int and 0xff
 
-template rawRGB(r, g, b: int): expr =
+template rawRGB(r, g, b: int): Color =
   Color(r shl 16 or g shl 8 or b)
 
-template colorOp(op: expr) {.immediate.} =
+template colorOp(op): Color =
   extract(a, ar, ag, ab)
   extract(b, br, bg, bb)
-  result = rawRGB(op(ar, br), op(ag, bg), op(ab, bb))
+  rawRGB(op(ar, br), op(ag, bg), op(ab, bb))
 
 proc satPlus(a, b: int): int {.inline.} =
   result = a +% b
@@ -67,12 +67,12 @@ proc intensity*(a: Color, f: float): Color =
   if b >% 255: b = 255
   result = rawRGB(r, g, b)
 
-template mix*(a, b: Color, fn: expr): expr =
+template mix*(a, b: Color, fn: untyped): untyped =
   ## uses `fn` to mix the colors `a` and `b`. `fn` is invoked for each component
   ## R, G, and B. This is a template because `fn` should be inlined and the
   ## compiler cannot inline proc pointers yet. If `fn`'s result is not in the
   ## range[0..255], it will be saturated to be so.
-  template `><` (x: expr): expr =
+  template `><` (x: untyped): untyped =
     # keep it in the range 0..255
     block:
       var y = x # eval only once
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index f438a85e7..2a0dbd2ca 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -409,20 +409,20 @@ proc preferSpawn*(): bool =
   ## it is not necessary to call this directly; use 'spawnX' instead.
   result = gSomeReady.counter > 0
 
-proc spawn*(call: expr): expr {.magic: "Spawn".}
+proc spawn*(call: typed): void {.magic: "Spawn".}
   ## always spawns a new task, so that the 'call' is never executed on
   ## the calling thread. 'call' has to be proc call 'p(...)' where 'p'
   ## is gcsafe and has a return type that is either 'void' or compatible
   ## with ``FlowVar[T]``.
 
-proc pinnedSpawn*(id: ThreadId; call: expr): expr {.magic: "Spawn".}
+proc pinnedSpawn*(id: ThreadId; call: typed): void {.magic: "Spawn".}
   ## always spawns a new task on the worker thread with ``id``, so that
   ## the 'call' is **always** executed on
   ## the thread. 'call' has to be proc call 'p(...)' where 'p'
   ## is gcsafe and has a return type that is either 'void' or compatible
   ## with ``FlowVar[T]``.
 
-template spawnX*(call: expr): expr =
+template spawnX*(call): void =
   ## spawns a new task if a CPU core is ready, otherwise executes the
   ## call in the calling thread. Usually it is advised to
   ## use 'spawn' in order to not block the producer for an unknown
@@ -431,7 +431,7 @@ template spawnX*(call: expr): expr =
   ## with ``FlowVar[T]``.
   (if preferSpawn(): spawn call else: call)
 
-proc parallel*(body: stmt) {.magic: "Parallel".}
+proc parallel*(body: untyped) {.magic: "Parallel".}
   ## a parallel section can be used to execute a block in parallel. ``body``
   ## has to be in a DSL that is a particular subset of the language. Please
   ## refer to the manual for further information.
@@ -530,6 +530,7 @@ proc nimSpawn4(fn: WorkerProc; data: pointer; id: ThreadId) {.compilerProc.} =
 proc sync*() =
   ## a simple barrier to wait for all spawn'ed tasks. If you need more elaborate
   ## waiting, you have to use an explicit barrier.
+  var toRelease = 0
   while true:
     var allReady = true
     for i in 0 .. <currentPoolSize:
@@ -537,5 +538,9 @@ proc sync*() =
       allReady = allReady and workersData[i].ready
     if allReady: break
     await(gSomeReady)
+    inc toRelease
+
+  for i in 0 ..< toRelease:
+    signal(gSomeReady)
 
 setup()
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 909a2613f..cb4f4f664 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -1245,6 +1245,8 @@ proc downloadFile*(client: HttpClient | AsyncHttpClient,
                    url: string, filename: string): Future[void] {.multisync.} =
   ## Downloads ``url`` and saves it to ``filename``.
   client.getBody = false
+  defer:
+    client.getBody = true
   let resp = await client.get(url)
 
   when client is HttpClient:
diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim
new file mode 100644
index 000000000..8d2fc235a
--- /dev/null
+++ b/lib/pure/includes/osenv.nim
@@ -0,0 +1,159 @@
+## Include file that implements 'getEnv' and friends. Do not import it!
+
+when not declared(ospaths):
+  {.error: "This is an include file for ospaths.nim!".}
+
+proc c_getenv(env: cstring): cstring {.
+  importc: "getenv", header: "<stdlib.h>".}
+proc c_putenv(env: cstring): cint {.
+  importc: "putenv", header: "<stdlib.h>".}
+
+# Environment handling cannot be put into RTL, because the ``envPairs``
+# iterator depends on ``environment``.
+
+var
+  envComputed {.threadvar.}: bool
+  environment {.threadvar.}: seq[string]
+
+when defined(windows) and not defined(nimscript):
+  # because we support Windows GUI applications, things get really
+  # messy here...
+  when useWinUnicode:
+    when defined(cpp):
+      proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.
+        importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", header: "<string.h>".}
+    else:
+      proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.
+        importc: "wcschr", header: "<string.h>".}
+  else:
+    proc strEnd(cstr: cstring, c = 0'i32): cstring {.
+      importc: "strchr", header: "<string.h>".}
+
+  proc getEnvVarsC() =
+    if not envComputed:
+      environment = @[]
+      when useWinUnicode:
+        var
+          env = getEnvironmentStringsW()
+          e = env
+        if e == nil: return # an error occurred
+        while true:
+          var eend = strEnd(e)
+          add(environment, $e)
+          e = cast[WideCString](cast[ByteAddress](eend)+2)
+          if eend[1].int == 0: break
+        discard freeEnvironmentStringsW(env)
+      else:
+        var
+          env = getEnvironmentStringsA()
+          e = env
+        if e == nil: return # an error occurred
+        while true:
+          var eend = strEnd(e)
+          add(environment, $e)
+          e = cast[cstring](cast[ByteAddress](eend)+1)
+          if eend[1] == '\0': break
+        discard freeEnvironmentStringsA(env)
+      envComputed = true
+
+else:
+  const
+    useNSGetEnviron = (defined(macosx) and not defined(ios)) or defined(nimscript)
+
+  when useNSGetEnviron:
+    # From the manual:
+    # Shared libraries and bundles don't have direct access to environ,
+    # which is only available to the loader ld(1) when a complete program
+    # is being linked.
+    # The environment routines can still be used, but if direct access to
+    # environ is needed, the _NSGetEnviron() routine, defined in
+    # <crt_externs.h>, can be used to retrieve the address of environ
+    # at runtime.
+    proc NSGetEnviron(): ptr cstringArray {.
+      importc: "_NSGetEnviron", header: "<crt_externs.h>".}
+  else:
+    var gEnv {.importc: "environ".}: cstringArray
+
+  proc getEnvVarsC() =
+    # retrieves the variables of char** env of C's main proc
+    if not envComputed:
+      environment = @[]
+      when useNSGetEnviron:
+        var gEnv = NSGetEnviron()[]
+      var i = 0
+      while true:
+        if gEnv[i] == nil: break
+        add environment, $gEnv[i]
+        inc(i)
+      envComputed = true
+
+proc findEnvVar(key: string): int =
+  getEnvVarsC()
+  var temp = key & '='
+  for i in 0..high(environment):
+    if startsWith(environment[i], temp): return i
+  return -1
+
+proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} =
+  ## Returns the value of the `environment variable`:idx: named `key`.
+  ##
+  ## If the variable does not exist, "" is returned. To distinguish
+  ## whether a variable exists or it's value is just "", call
+  ## `existsEnv(key)`.
+  when nimvm:
+    discard "built into the compiler"
+  else:
+    var i = findEnvVar(key)
+    if i >= 0:
+      return TaintedString(substr(environment[i], find(environment[i], '=')+1))
+    else:
+      var env = c_getenv(key)
+      if env == nil: return TaintedString("")
+      result = TaintedString($env)
+
+proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
+  ## Checks whether the environment variable named `key` exists.
+  ## Returns true if it exists, false otherwise.
+  when nimvm:
+    discard "built into the compiler"
+  else:
+    if c_getenv(key) != nil: return true
+    else: return findEnvVar(key) >= 0
+
+proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
+  ## Sets the value of the `environment variable`:idx: named `key` to `val`.
+  ## If an error occurs, `EInvalidEnvVar` is raised.
+
+  # Note: by storing the string in the environment sequence,
+  # we guarantee that we don't free the memory before the program
+  # ends (this is needed for POSIX compliance). It is also needed so that
+  # the process itself may access its modified environment variables!
+  when nimvm:
+    discard "built into the compiler"
+  else:
+    var indx = findEnvVar(key)
+    if indx >= 0:
+      environment[indx] = key & '=' & val
+    else:
+      add environment, (key & '=' & val)
+      indx = high(environment)
+    when defined(windows) and not defined(nimscript):
+      when useWinUnicode:
+        var k = newWideCString(key)
+        var v = newWideCString(val)
+        if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError())
+      else:
+        if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError())
+    else:
+      if c_putenv(environment[indx]) != 0'i32:
+        raiseOSError(osLastError())
+
+iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
+  ## Iterate over all `environments variables`:idx:. In the first component
+  ## of the tuple is the name of the current variable stored, in the second
+  ## its value.
+  getEnvVarsC()
+  for i in 0..high(environment):
+    var p = find(environment[i], '=')
+    yield (TaintedString(substr(environment[i], 0, p-1)),
+           TaintedString(substr(environment[i], p+1)))
diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim
new file mode 100644
index 000000000..dbb709f1b
--- /dev/null
+++ b/lib/pure/includes/oserr.nim
@@ -0,0 +1,135 @@
+## Include file that implements 'osErrorMsg' and friends. Do not import it!
+
+when not declared(ospaths):
+  {.error: "This is an include file for ospaths.nim!".}
+
+when not defined(nimscript):
+  var errno {.importc, header: "<errno.h>".}: cint
+
+  proc c_strerror(errnum: cint): cstring {.
+    importc: "strerror", header: "<string.h>".}
+
+  when defined(windows):
+    import winlean
+
+proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
+  ## Retrieves the operating system's error flag, ``errno``.
+  ## On Windows ``GetLastError`` is checked before ``errno``.
+  ## Returns "" if no error occurred.
+  ##
+  ## **Deprecated since version 0.9.4**: use the other ``osErrorMsg`` proc.
+
+  result = ""
+  when defined(Windows) and not defined(nimscript):
+    var err = getLastError()
+    if err != 0'i32:
+      when useWinUnicode:
+        var msgbuf: WideCString
+        if formatMessageW(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
+                          nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
+          result = $msgbuf
+          if msgbuf != nil: localFree(cast[pointer](msgbuf))
+      else:
+        var msgbuf: cstring
+        if formatMessageA(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
+                          nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
+          result = $msgbuf
+          if msgbuf != nil: localFree(msgbuf)
+  when not defined(nimscript):
+    if errno != 0'i32:
+      result = $c_strerror(errno)
+
+{.push warning[deprecated]: off.}
+proc raiseOSError*(msg: string = "") {.noinline, rtl, extern: "nos$1",
+                                       deprecated.} =
+  ## raises an OSError exception with the given message ``msg``.
+  ## If ``msg == ""``, the operating system's error flag
+  ## (``errno``) is converted to a readable error message. On Windows
+  ## ``GetLastError`` is checked before ``errno``.
+  ## If no error flag is set, the message ``unknown OS error`` is used.
+  ##
+  ## **Deprecated since version 0.9.4**: use the other ``raiseOSError`` proc.
+  if len(msg) == 0:
+    var m = osErrorMsg()
+    raise newException(OSError, if m.len > 0: m else: "unknown OS error")
+  else:
+    raise newException(OSError, msg)
+{.pop.}
+
+when not defined(nimfix):
+  {.deprecated: [osError: raiseOSError].}
+
+proc `==`*(err1, err2: OSErrorCode): bool {.borrow.}
+proc `$`*(err: OSErrorCode): string {.borrow.}
+
+proc osErrorMsg*(errorCode: OSErrorCode): string =
+  ## Converts an OS error code into a human readable string.
+  ##
+  ## The error code can be retrieved using the ``osLastError`` proc.
+  ##
+  ## If conversion fails, or ``errorCode`` is ``0`` then ``""`` will be
+  ## returned.
+  ##
+  ## On Windows, the ``-d:useWinAnsi`` compilation flag can be used to
+  ## make this procedure use the non-unicode Win API calls to retrieve the
+  ## message.
+  result = ""
+  when defined(nimscript):
+    discard
+  elif defined(Windows):
+    if errorCode != OSErrorCode(0'i32):
+      when useWinUnicode:
+        var msgbuf: WideCString
+        if formatMessageW(0x00000100 or 0x00001000 or 0x00000200,
+                        nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
+          result = $msgbuf
+          if msgbuf != nil: localFree(cast[pointer](msgbuf))
+      else:
+        var msgbuf: cstring
+        if formatMessageA(0x00000100 or 0x00001000 or 0x00000200,
+                        nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
+          result = $msgbuf
+          if msgbuf != nil: localFree(msgbuf)
+  else:
+    if errorCode != OSErrorCode(0'i32):
+      result = $c_strerror(errorCode.int32)
+
+proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
+  ## Raises an ``OSError`` exception. The ``errorCode`` will determine the
+  ## message, ``osErrorMsg`` will be used to get this message.
+  ##
+  ## The error code can be retrieved using the ``osLastError`` proc.
+  ##
+  ## If the error code is ``0`` or an error message could not be retrieved,
+  ## the message ``unknown OS error`` will be used.
+  var e: ref OSError; new(e)
+  e.errorCode = errorCode.int32
+  if additionalInfo.len == 0:
+    e.msg = osErrorMsg(errorCode)
+  else:
+    e.msg = osErrorMsg(errorCode) & "\nAdditional info: " & additionalInfo
+  if e.msg == "":
+    e.msg = "unknown OS error"
+  raise e
+
+{.push stackTrace:off.}
+proc osLastError*(): OSErrorCode =
+  ## Retrieves the last operating system error code.
+  ##
+  ## This procedure is useful in the event when an OS call fails. In that case
+  ## this procedure will return the error code describing the reason why the
+  ## OS call failed. The ``OSErrorMsg`` procedure can then be used to convert
+  ## this code into a string.
+  ##
+  ## **Warning**:
+  ## The behaviour of this procedure varies between Windows and POSIX systems.
+  ## On Windows some OS calls can reset the error code to ``0`` causing this
+  ## procedure to return ``0``. It is therefore advised to call this procedure
+  ## immediately after an OS call fails. On POSIX systems this is not a problem.
+  when defined(nimscript):
+    discard
+  elif defined(windows):
+    result = OSErrorCode(getLastError())
+  else:
+    result = OSErrorCode(errno)
+{.pop.}
diff --git a/lib/pure/ioselectors.nim b/lib/pure/ioselectors.nim
index cbef5ce0d..ef8072221 100644
--- a/lib/pure/ioselectors.nim
+++ b/lib/pure/ioselectors.nim
@@ -208,7 +208,7 @@ else:
     import locks
 
     type
-      SharedArray {.unchecked.}[T] = array[0..100, T]
+      SharedArray[T] = UncheckedArray[T]
 
     proc allocSharedArray[T](nsize: int): ptr SharedArray[T] =
       result = cast[ptr SharedArray[T]](allocShared0(sizeof(T) * nsize))
diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim
index b6154d8de..d1cf5d9bc 100644
--- a/lib/pure/memfiles.nim
+++ b/lib/pure/memfiles.nim
@@ -123,7 +123,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
     result.size = 0
 
   when defined(windows):
-    template fail(errCode: OSErrorCode, msg: expr) =
+    template fail(errCode: OSErrorCode, msg: untyped) =
       rollback()
       if result.fHandle != 0: discard closeHandle(result.fHandle)
       if result.mapHandle != 0: discard closeHandle(result.mapHandle)
@@ -131,7 +131,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
       # return false
       #raise newException(EIO, msg)
 
-    template callCreateFile(winApiProc, filename: expr): expr =
+    template callCreateFile(winApiProc, filename): untyped =
       winApiProc(
         filename,
         # GENERIC_ALL != (GENERIC_READ or GENERIC_WRITE)
@@ -198,7 +198,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
         result.fHandle = INVALID_HANDLE_VALUE
 
   else:
-    template fail(errCode: OSErrorCode, msg: expr) =
+    template fail(errCode: OSErrorCode, msg: string) =
       rollback()
       if result.handle != -1: discard close(result.handle)
       raiseOSError(errCode)
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index 7568408a6..1a62c0bf6 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -233,7 +233,7 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
   # OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED.
   # FreeBSD doesn'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):
+  when not defined(freebsd) and not defined(openbsd) and not defined(netbsd) and not defined(android):
     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 629e916fa..215a301b6 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -797,7 +797,7 @@ when false: #defineSsl:
     ##
     ## ``AcceptNoClient`` will be returned when no client is currently attempting
     ## to connect.
-    template doHandshake(): stmt =
+    template doHandshake(): untyped =
       when defineSsl:
         if server.isSSL:
           client.setBlocking(false)
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index f7bcfb60e..b85181edf 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -29,150 +29,17 @@ else:
 import ospaths
 export ospaths
 
-when defined(posix):
-  when NoFakeVars:
-    const pathMax = 5000 # doesn't matter really. The concept of PATH_MAX
-                         # doesn't work anymore on modern OSes.
-  else:
-    var
-      pathMax {.importc: "PATH_MAX", header: "<stdlib.h>".}: cint
-
 proc c_remove(filename: cstring): cint {.
   importc: "remove", header: "<stdio.h>".}
 proc c_rename(oldname, newname: cstring): cint {.
   importc: "rename", header: "<stdio.h>".}
 proc c_system(cmd: cstring): cint {.
   importc: "system", header: "<stdlib.h>".}
-proc c_strerror(errnum: cint): cstring {.
-  importc: "strerror", header: "<string.h>".}
 proc c_strlen(a: cstring): cint {.
   importc: "strlen", header: "<string.h>", noSideEffect.}
-proc c_getenv(env: cstring): cstring {.
-  importc: "getenv", header: "<stdlib.h>".}
-proc c_putenv(env: cstring): cint {.
-  importc: "putenv", header: "<stdlib.h>".}
 proc c_free(p: pointer) {.
   importc: "free", header: "<stdlib.h>".}
 
-var errno {.importc, header: "<errno.h>".}: cint
-
-proc osErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
-  ## Retrieves the operating system's error flag, ``errno``.
-  ## On Windows ``GetLastError`` is checked before ``errno``.
-  ## Returns "" if no error occurred.
-  ##
-  ## **Deprecated since version 0.9.4**: use the other ``osErrorMsg`` proc.
-
-  result = ""
-  when defined(Windows):
-    var err = getLastError()
-    if err != 0'i32:
-      when useWinUnicode:
-        var msgbuf: WideCString
-        if formatMessageW(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
-                          nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
-          result = $msgbuf
-          if msgbuf != nil: localFree(cast[pointer](msgbuf))
-      else:
-        var msgbuf: cstring
-        if formatMessageA(0x00000100 or 0x00001000 or 0x00000200 or 0x000000FF,
-                          nil, err, 0, addr(msgbuf), 0, nil) != 0'i32:
-          result = $msgbuf
-          if msgbuf != nil: localFree(msgbuf)
-  if errno != 0'i32:
-    result = $os.c_strerror(errno)
-
-{.push warning[deprecated]: off.}
-proc raiseOSError*(msg: string = "") {.noinline, rtl, extern: "nos$1",
-                                       deprecated.} =
-  ## raises an OSError exception with the given message ``msg``.
-  ## If ``msg == ""``, the operating system's error flag
-  ## (``errno``) is converted to a readable error message. On Windows
-  ## ``GetLastError`` is checked before ``errno``.
-  ## If no error flag is set, the message ``unknown OS error`` is used.
-  ##
-  ## **Deprecated since version 0.9.4**: use the other ``raiseOSError`` proc.
-  if len(msg) == 0:
-    var m = osErrorMsg()
-    raise newException(OSError, if m.len > 0: m else: "unknown OS error")
-  else:
-    raise newException(OSError, msg)
-{.pop.}
-
-when not defined(nimfix):
-  {.deprecated: [osError: raiseOSError].}
-
-proc `==`*(err1, err2: OSErrorCode): bool {.borrow.}
-proc `$`*(err: OSErrorCode): string {.borrow.}
-
-proc osErrorMsg*(errorCode: OSErrorCode): string =
-  ## Converts an OS error code into a human readable string.
-  ##
-  ## The error code can be retrieved using the ``osLastError`` proc.
-  ##
-  ## If conversion fails, or ``errorCode`` is ``0`` then ``""`` will be
-  ## returned.
-  ##
-  ## On Windows, the ``-d:useWinAnsi`` compilation flag can be used to
-  ## make this procedure use the non-unicode Win API calls to retrieve the
-  ## message.
-  result = ""
-  when defined(Windows):
-    if errorCode != OSErrorCode(0'i32):
-      when useWinUnicode:
-        var msgbuf: WideCString
-        if formatMessageW(0x00000100 or 0x00001000 or 0x00000200,
-                        nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
-          result = $msgbuf
-          if msgbuf != nil: localFree(cast[pointer](msgbuf))
-      else:
-        var msgbuf: cstring
-        if formatMessageA(0x00000100 or 0x00001000 or 0x00000200,
-                        nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
-          result = $msgbuf
-          if msgbuf != nil: localFree(msgbuf)
-  else:
-    if errorCode != OSErrorCode(0'i32):
-      result = $os.c_strerror(errorCode.int32)
-
-proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
-  ## Raises an ``OSError`` exception. The ``errorCode`` will determine the
-  ## message, ``osErrorMsg`` will be used to get this message.
-  ##
-  ## The error code can be retrieved using the ``osLastError`` proc.
-  ##
-  ## If the error code is ``0`` or an error message could not be retrieved,
-  ## the message ``unknown OS error`` will be used.
-  var e: ref OSError; new(e)
-  e.errorCode = errorCode.int32
-  if additionalInfo.len == 0:
-    e.msg = osErrorMsg(errorCode)
-  else:
-    e.msg = osErrorMsg(errorCode) & "\nAdditional info: " & additionalInfo
-  if e.msg == "":
-    e.msg = "unknown OS error"
-  raise e
-
-{.push stackTrace:off.}
-proc osLastError*(): OSErrorCode =
-  ## Retrieves the last operating system error code.
-  ##
-  ## This procedure is useful in the event when an OS call fails. In that case
-  ## this procedure will return the error code describing the reason why the
-  ## OS call failed. The ``OSErrorMsg`` procedure can then be used to convert
-  ## this code into a string.
-  ##
-  ## **Warning**:
-  ## The behaviour of this procedure varies between Windows and POSIX systems.
-  ## On Windows some OS calls can reset the error code to ``0`` causing this
-  ## procedure to return ``0``. It is therefore advised to call this procedure
-  ## immediately after an OS call fails. On POSIX systems this is not a problem.
-
-  when defined(windows):
-    result = OSErrorCode(getLastError())
-  else:
-    result = OSErrorCode(errno)
-{.pop.}
 
 when defined(windows):
   when useWinUnicode:
@@ -252,6 +119,60 @@ proc dirExists*(dir: string): bool {.inline.} =
   ## Synonym for existsDir
   existsDir(dir)
 
+when not defined(windows):
+  proc checkSymlink(path: string): bool =
+    var rawInfo: Stat
+    if lstat(path, rawInfo) < 0'i32: result = false
+    else: result = S_ISLNK(rawInfo.st_mode)
+
+const
+  ExeExts* = when defined(windows): ["exe", "cmd", "bat"] else: [""] ## \
+    ## platform specific file extension for executables. On Windows
+    ## ``["exe", "cmd", "bat"]``, on Posix ``[""]``.
+
+proc findExe*(exe: string, followSymlinks: bool = true;
+              extensions: openarray[string]=ExeExts): string {.
+  tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect].} =
+  ## Searches for `exe` in the current working directory and then
+  ## in directories listed in the ``PATH`` environment variable.
+  ## Returns "" if the `exe` cannot be found. `exe`
+  ## is added the `ExeExts <#ExeExts>`_ file extensions if it has none.
+  ## If the system supports symlinks it also resolves them until it
+  ## meets the actual file. This behavior can be disabled if desired.
+  for ext in extensions:
+    result = addFileExt(exe, ext)
+    if existsFile(result): return
+  var path = string(getEnv("PATH"))
+  for candidate in split(path, PathSep):
+    when defined(windows):
+      var x = (if candidate[0] == '"' and candidate[^1] == '"':
+                substr(candidate, 1, candidate.len-2) else: candidate) /
+              exe
+    else:
+      var x = expandTilde(candidate) / exe
+    for ext in extensions:
+      var x = addFileExt(x, ext)
+      if existsFile(x):
+        when not defined(windows):
+          while followSymlinks: # doubles as if here
+            if x.checkSymlink:
+              var r = newString(256)
+              var len = readlink(x, r, 256)
+              if len < 0:
+                raiseOSError(osLastError())
+              if len > 256:
+                r = newString(len+1)
+                len = readlink(x, r, len)
+              setLen(r, len)
+              if isAbsolute(r):
+                x = r
+              else:
+                x = parentDir(x) / r
+            else:
+              break
+        return x
+  result = ""
+
 proc getLastModificationTime*(file: string): Time {.rtl, extern: "nos$1".} =
   ## Returns the `file`'s last modification time.
   when defined(posix):
@@ -714,147 +635,6 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
   else:
     result = c_system(command)
 
-# Environment handling cannot be put into RTL, because the ``envPairs``
-# iterator depends on ``environment``.
-
-var
-  envComputed {.threadvar.}: bool
-  environment {.threadvar.}: seq[string]
-
-when defined(windows):
-  # because we support Windows GUI applications, things get really
-  # messy here...
-  when useWinUnicode:
-    when defined(cpp):
-      proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.
-        importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", header: "<string.h>".}
-    else:
-      proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.
-        importc: "wcschr", header: "<string.h>".}
-  else:
-    proc strEnd(cstr: cstring, c = 0'i32): cstring {.
-      importc: "strchr", header: "<string.h>".}
-
-  proc getEnvVarsC() =
-    if not envComputed:
-      environment = @[]
-      when useWinUnicode:
-        var
-          env = getEnvironmentStringsW()
-          e = env
-        if e == nil: return # an error occurred
-        while true:
-          var eend = strEnd(e)
-          add(environment, $e)
-          e = cast[WideCString](cast[ByteAddress](eend)+2)
-          if eend[1].int == 0: break
-        discard freeEnvironmentStringsW(env)
-      else:
-        var
-          env = getEnvironmentStringsA()
-          e = env
-        if e == nil: return # an error occurred
-        while true:
-          var eend = strEnd(e)
-          add(environment, $e)
-          e = cast[cstring](cast[ByteAddress](eend)+1)
-          if eend[1] == '\0': break
-        discard freeEnvironmentStringsA(env)
-      envComputed = true
-
-else:
-  const
-    useNSGetEnviron = defined(macosx) and not defined(ios)
-
-  when useNSGetEnviron:
-    # From the manual:
-    # Shared libraries and bundles don't have direct access to environ,
-    # which is only available to the loader ld(1) when a complete program
-    # is being linked.
-    # The environment routines can still be used, but if direct access to
-    # environ is needed, the _NSGetEnviron() routine, defined in
-    # <crt_externs.h>, can be used to retrieve the address of environ
-    # at runtime.
-    proc NSGetEnviron(): ptr cstringArray {.
-      importc: "_NSGetEnviron", header: "<crt_externs.h>".}
-  else:
-    var gEnv {.importc: "environ".}: cstringArray
-
-  proc getEnvVarsC() =
-    # retrieves the variables of char** env of C's main proc
-    if not envComputed:
-      environment = @[]
-      when useNSGetEnviron:
-        var gEnv = NSGetEnviron()[]
-      var i = 0
-      while true:
-        if gEnv[i] == nil: break
-        add environment, $gEnv[i]
-        inc(i)
-      envComputed = true
-
-proc findEnvVar(key: string): int =
-  getEnvVarsC()
-  var temp = key & '='
-  for i in 0..high(environment):
-    if startsWith(environment[i], temp): return i
-  return -1
-
-proc getEnv*(key: string): TaintedString {.tags: [ReadEnvEffect].} =
-  ## Returns the value of the `environment variable`:idx: named `key`.
-  ##
-  ## If the variable does not exist, "" is returned. To distinguish
-  ## whether a variable exists or it's value is just "", call
-  ## `existsEnv(key)`.
-  var i = findEnvVar(key)
-  if i >= 0:
-    return TaintedString(substr(environment[i], find(environment[i], '=')+1))
-  else:
-    var env = c_getenv(key)
-    if env == nil: return TaintedString("")
-    result = TaintedString($env)
-
-proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
-  ## Checks whether the environment variable named `key` exists.
-  ## Returns true if it exists, false otherwise.
-  if c_getenv(key) != nil: return true
-  else: return findEnvVar(key) >= 0
-
-proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
-  ## Sets the value of the `environment variable`:idx: named `key` to `val`.
-  ## If an error occurs, `EInvalidEnvVar` is raised.
-
-  # Note: by storing the string in the environment sequence,
-  # we guarantee that we don't free the memory before the program
-  # ends (this is needed for POSIX compliance). It is also needed so that
-  # the process itself may access its modified environment variables!
-  var indx = findEnvVar(key)
-  if indx >= 0:
-    environment[indx] = key & '=' & val
-  else:
-    add environment, (key & '=' & val)
-    indx = high(environment)
-  when defined(windows):
-    when useWinUnicode:
-      var k = newWideCString(key)
-      var v = newWideCString(val)
-      if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError())
-    else:
-      if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError())
-  else:
-    if c_putenv(environment[indx]) != 0'i32:
-      raiseOSError(osLastError())
-
-iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
-  ## Iterate over all `environments variables`:idx:. In the first component
-  ## of the tuple is the name of the current variable stored, in the second
-  ## its value.
-  getEnvVarsC()
-  for i in 0..high(environment):
-    var p = find(environment[i], '=')
-    yield (TaintedString(substr(environment[i], 0, p-1)),
-           TaintedString(substr(environment[i], p+1)))
-
 # Templates for filtering directories and files
 when defined(windows):
   template isDir(f: WIN32_FIND_DATA): bool =
@@ -1017,7 +797,7 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
               y = dir / y
             var k = pcFile
 
-            when defined(linux) or defined(macosx) or defined(bsd):
+            when defined(linux) or defined(macosx) or defined(bsd) or defined(genode):
               if x.d_type != DT_UNKNOWN:
                 if x.d_type == DT_DIR: k = pcDir
                 if x.d_type == DT_LNK:
@@ -1184,7 +964,9 @@ proc createSymlink*(src, dest: string) =
   ## Some OS's (such as Microsoft Windows) restrict the creation
   ## of symlinks to root users (administrators).
   when defined(Windows):
-    let flag = dirExists(src).int32
+    # 2 is the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE. This allows
+    # anyone with developer mode on to create a link
+    let flag = dirExists(src).int32 or 2
     when useWinUnicode:
       var wSrc = newWideCString(src)
       var wDst = newWideCString(dest)
@@ -1386,7 +1168,7 @@ proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect].} =
       copyDir(source, dest)
       removeDir(source)
 
-include ospaths
+#include ospaths
 
 proc expandSymlink*(symlinkPath: string): string =
   ## Returns a string representing the path to which the symbolic link points.
diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim
index fa5342fcf..dcb785c83 100644
--- a/lib/pure/ospaths.nim
+++ b/lib/pure/ospaths.nim
@@ -7,618 +7,554 @@
 #    distribution, for details about the copyright.
 #
 
-# Included by the ``os`` module but a module in its own right for NimScript
+# Forwarded by the ``os`` module but a module in its own right for NimScript
 # support.
 
-when not declared(os):
-  {.pragma: rtl.}
-  import strutils
-
-when defined(nimscript) or (defined(nimdoc) and not declared(os)):
-  {.pragma: rtl.}
-  {.push hint[ConvFromXtoItselfNotNeeded]:off.}
-
-when not declared(getEnv) or defined(nimscript):
-  type
-    ReadEnvEffect* = object of ReadIOEffect   ## effect that denotes a read
-                                              ## from an environment variable
-    WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
-                                              ## to an environment variable
-
-    ReadDirEffect* = object of ReadIOEffect   ## effect that denotes a read
-                                              ## operation from the directory
-                                              ## structure
-    WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write
-                                              ## operation to
-                                              ## the directory structure
-
-    OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
-
-  {.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect,
-      FReadDir: ReadDirEffect,
-      FWriteDir: WriteDirEffect,
-      TOSErrorCode: OSErrorCode
-  ].}
-  const
-    doslikeFileSystem* = defined(windows) or defined(OS2) or defined(DOS)
-
-  when defined(Nimdoc): # only for proper documentation:
-    const
-      CurDir* = '.'
-        ## The constant string used by the operating system to refer to the
-        ## current directory.
-        ##
-        ## For example: '.' for POSIX or ':' for the classic Macintosh.
-
-      ParDir* = ".."
-        ## The constant string used by the operating system to refer to the
-        ## parent directory.
-        ##
-        ## For example: ".." for POSIX or "::" for the classic Macintosh.
-
-      DirSep* = '/'
-        ## The character used by the operating system to separate pathname
-        ## components, for example, '/' for POSIX or ':' for the classic
-        ## Macintosh.
-
-      AltSep* = '/'
-        ## An alternative character used by the operating system to separate
-        ## pathname components, or the same as `DirSep` if only one separator
-        ## character exists. This is set to '/' on Windows systems
-        ## where `DirSep` is a backslash.
-
-      PathSep* = ':'
-        ## The character conventionally used by the operating system to separate
-        ## search patch components (as in PATH), such as ':' for POSIX
-        ## or ';' for Windows.
-
-      FileSystemCaseSensitive* = true
-        ## true if the file system is case sensitive, false otherwise. Used by
-        ## `cmpPaths` to compare filenames properly.
-
-      ExeExt* = ""
-        ## The file extension of native executables. For example:
-        ## "" for POSIX, "exe" on Windows.
-
-      ScriptExt* = ""
-        ## The file extension of a script file. For example: "" for POSIX,
-        ## "bat" on Windows.
-
-      DynlibFormat* = "lib$1.so"
-        ## The format string to turn a filename into a `DLL`:idx: file (also
-        ## called `shared object`:idx: on some operating systems).
+include "system/inclrtl"
 
-  elif defined(macos):
-    const
-      CurDir* = ':'
-      ParDir* = "::"
-      DirSep* = ':'
-      AltSep* = Dirsep
-      PathSep* = ','
-      FileSystemCaseSensitive* = false
-      ExeExt* = ""
-      ScriptExt* = ""
-      DynlibFormat* = "$1.dylib"
-
-    #  MacOS paths
-    #  ===========
-    #  MacOS directory separator is a colon ":" which is the only character not
-    #  allowed in filenames.
-    #
-    #  A path containing no colon or which begins with a colon is a partial
-    #  path.
-    #  E.g. ":kalle:petter" ":kalle" "kalle"
-    #
-    #  All other paths are full (absolute) paths. E.g. "HD:kalle:" "HD:"
-    #  When generating paths, one is safe if one ensures that all partial paths
-    #  begin with a colon, and all full paths end with a colon.
-    #  In full paths the first name (e g HD above) is the name of a mounted
-    #  volume.
-    #  These names are not unique, because, for instance, two diskettes with the
-    #  same names could be inserted. This means that paths on MacOS are not
-    #  waterproof. In case of equal names the first volume found will do.
-    #  Two colons "::" are the relative path to the parent. Three is to the
-    #  grandparent etc.
-  elif doslikeFileSystem:
-    const
-      CurDir* = '.'
-      ParDir* = ".."
-      DirSep* = '\\' # seperator within paths
-      AltSep* = '/'
-      PathSep* = ';' # seperator between paths
-      FileSystemCaseSensitive* = false
-      ExeExt* = "exe"
-      ScriptExt* = "bat"
-      DynlibFormat* = "$1.dll"
-  elif defined(PalmOS) or defined(MorphOS):
-    const
-      DirSep* = '/'
-      AltSep* = Dirsep
-      PathSep* = ';'
-      ParDir* = ".."
-      FileSystemCaseSensitive* = false
-      ExeExt* = ""
-      ScriptExt* = ""
-      DynlibFormat* = "$1.prc"
-  elif defined(RISCOS):
-    const
-      DirSep* = '.'
-      AltSep* = '.'
-      ParDir* = ".." # is this correct?
-      PathSep* = ','
-      FileSystemCaseSensitive* = true
-      ExeExt* = ""
-      ScriptExt* = ""
-      DynlibFormat* = "lib$1.so"
-  else: # UNIX-like operating system
-    const
-      CurDir* = '.'
-      ParDir* = ".."
-      DirSep* = '/'
-      AltSep* = DirSep
-      PathSep* = ':'
-      FileSystemCaseSensitive* = true
-      ExeExt* = ""
-      ScriptExt* = ""
-      DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so"
+import strutils
+
+type
+  ReadEnvEffect* = object of ReadIOEffect   ## effect that denotes a read
+                                            ## from an environment variable
+  WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write
+                                            ## to an environment variable
+
+  ReadDirEffect* = object of ReadIOEffect   ## effect that denotes a read
+                                            ## operation from the directory
+                                            ## structure
+  WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write
+                                            ## operation to
+                                            ## the directory structure
+
+  OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
 
+{.deprecated: [FReadEnv: ReadEnvEffect, FWriteEnv: WriteEnvEffect,
+    FReadDir: ReadDirEffect,
+    FWriteDir: WriteDirEffect,
+    TOSErrorCode: OSErrorCode
+].}
+const
+  doslikeFileSystem* = defined(windows) or defined(OS2) or defined(DOS)
+
+when defined(Nimdoc): # only for proper documentation:
   const
-    ExtSep* = '.'
-      ## The character which separates the base filename from the extension;
-      ## for example, the '.' in ``os.nim``.
-
-
-  proc joinPath*(head, tail: string): string {.
-    noSideEffect, rtl, extern: "nos$1".} =
-    ## Joins two directory names to one.
-    ##
-    ## For example on Unix:
-    ##
-    ## .. code-block:: nim
-    ##   joinPath("usr", "lib")
-    ##
-    ## results in:
-    ##
-    ## .. code-block:: nim
-    ##   "usr/lib"
-    ##
-    ## If head is the empty string, tail is returned. If tail is the empty
-    ## string, head is returned with a trailing path separator. If tail starts
-    ## with a path separator it will be removed when concatenated to head. Other
-    ## path separators not located on boundaries won't be modified. More
-    ## examples on Unix:
-    ##
-    ## .. code-block:: nim
-    ##   assert joinPath("usr", "") == "usr/"
-    ##   assert joinPath("", "lib") == "lib"
-    ##   assert joinPath("", "/lib") == "/lib"
-    ##   assert joinPath("usr/", "/lib") == "usr/lib"
-    if len(head) == 0:
-      result = tail
-    elif head[len(head)-1] in {DirSep, AltSep}:
-      if tail[0] in {DirSep, AltSep}:
-        result = head & substr(tail, 1)
-      else:
-        result = head & tail
-    else:
-      if tail[0] in {DirSep, AltSep}:
-        result = head & tail
-      else:
-        result = head & DirSep & tail
-
-  proc joinPath*(parts: varargs[string]): string {.noSideEffect,
-    rtl, extern: "nos$1OpenArray".} =
-    ## The same as `joinPath(head, tail)`, but works with any number of
-    ## directory parts. You need to pass at least one element or the proc
-    ## will assert in debug builds and crash on release builds.
-    result = parts[0]
-    for i in 1..high(parts):
-      result = joinPath(result, parts[i])
-
-  proc `/` * (head, tail: string): string {.noSideEffect.} =
-    ## The same as ``joinPath(head, tail)``
-    ##
-    ## Here are some examples for Unix:
-    ##
-    ## .. code-block:: nim
-    ##   assert "usr" / "" == "usr/"
-    ##   assert "" / "lib" == "lib"
-    ##   assert "" / "/lib" == "/lib"
-    ##   assert "usr/" / "/lib" == "usr/lib"
-    return joinPath(head, tail)
-
-  proc splitPath*(path: string): tuple[head, tail: string] {.
-    noSideEffect, rtl, extern: "nos$1".} =
-    ## Splits a directory into (head, tail), so that
-    ## ``head / tail == path`` (except for edge cases like "/usr").
-    ##
-    ## Examples:
-    ##
-    ## .. code-block:: nim
-    ##   splitPath("usr/local/bin") -> ("usr/local", "bin")
-    ##   splitPath("usr/local/bin/") -> ("usr/local/bin", "")
-    ##   splitPath("bin") -> ("", "bin")
-    ##   splitPath("/bin") -> ("", "bin")
-    ##   splitPath("") -> ("", "")
-    var sepPos = -1
-    for i in countdown(len(path)-1, 0):
-      if path[i] in {DirSep, AltSep}:
-        sepPos = i
-        break
-    if sepPos >= 0:
-      result.head = substr(path, 0, sepPos-1)
-      result.tail = substr(path, sepPos+1)
+    CurDir* = '.'
+      ## The constant string used by the operating system to refer to the
+      ## current directory.
+      ##
+      ## For example: '.' for POSIX or ':' for the classic Macintosh.
+
+    ParDir* = ".."
+      ## The constant string used by the operating system to refer to the
+      ## parent directory.
+      ##
+      ## For example: ".." for POSIX or "::" for the classic Macintosh.
+
+    DirSep* = '/'
+      ## The character used by the operating system to separate pathname
+      ## components, for example, '/' for POSIX or ':' for the classic
+      ## Macintosh.
+
+    AltSep* = '/'
+      ## An alternative character used by the operating system to separate
+      ## pathname components, or the same as `DirSep` if only one separator
+      ## character exists. This is set to '/' on Windows systems
+      ## where `DirSep` is a backslash.
+
+    PathSep* = ':'
+      ## The character conventionally used by the operating system to separate
+      ## search patch components (as in PATH), such as ':' for POSIX
+      ## or ';' for Windows.
+
+    FileSystemCaseSensitive* = true
+      ## true if the file system is case sensitive, false otherwise. Used by
+      ## `cmpPaths` to compare filenames properly.
+
+    ExeExt* = ""
+      ## The file extension of native executables. For example:
+      ## "" for POSIX, "exe" on Windows.
+
+    ScriptExt* = ""
+      ## The file extension of a script file. For example: "" for POSIX,
+      ## "bat" on Windows.
+
+    DynlibFormat* = "lib$1.so"
+      ## The format string to turn a filename into a `DLL`:idx: file (also
+      ## called `shared object`:idx: on some operating systems).
+
+elif defined(macos):
+  const
+    CurDir* = ':'
+    ParDir* = "::"
+    DirSep* = ':'
+    AltSep* = Dirsep
+    PathSep* = ','
+    FileSystemCaseSensitive* = false
+    ExeExt* = ""
+    ScriptExt* = ""
+    DynlibFormat* = "$1.dylib"
+
+  #  MacOS paths
+  #  ===========
+  #  MacOS directory separator is a colon ":" which is the only character not
+  #  allowed in filenames.
+  #
+  #  A path containing no colon or which begins with a colon is a partial
+  #  path.
+  #  E.g. ":kalle:petter" ":kalle" "kalle"
+  #
+  #  All other paths are full (absolute) paths. E.g. "HD:kalle:" "HD:"
+  #  When generating paths, one is safe if one ensures that all partial paths
+  #  begin with a colon, and all full paths end with a colon.
+  #  In full paths the first name (e g HD above) is the name of a mounted
+  #  volume.
+  #  These names are not unique, because, for instance, two diskettes with the
+  #  same names could be inserted. This means that paths on MacOS are not
+  #  waterproof. In case of equal names the first volume found will do.
+  #  Two colons "::" are the relative path to the parent. Three is to the
+  #  grandparent etc.
+elif doslikeFileSystem:
+  const
+    CurDir* = '.'
+    ParDir* = ".."
+    DirSep* = '\\' # seperator within paths
+    AltSep* = '/'
+    PathSep* = ';' # seperator between paths
+    FileSystemCaseSensitive* = false
+    ExeExt* = "exe"
+    ScriptExt* = "bat"
+    DynlibFormat* = "$1.dll"
+elif defined(PalmOS) or defined(MorphOS):
+  const
+    DirSep* = '/'
+    AltSep* = Dirsep
+    PathSep* = ';'
+    ParDir* = ".."
+    FileSystemCaseSensitive* = false
+    ExeExt* = ""
+    ScriptExt* = ""
+    DynlibFormat* = "$1.prc"
+elif defined(RISCOS):
+  const
+    DirSep* = '.'
+    AltSep* = '.'
+    ParDir* = ".." # is this correct?
+    PathSep* = ','
+    FileSystemCaseSensitive* = true
+    ExeExt* = ""
+    ScriptExt* = ""
+    DynlibFormat* = "lib$1.so"
+else: # UNIX-like operating system
+  const
+    CurDir* = '.'
+    ParDir* = ".."
+    DirSep* = '/'
+    AltSep* = DirSep
+    PathSep* = ':'
+    FileSystemCaseSensitive* = true
+    ExeExt* = ""
+    ScriptExt* = ""
+    DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so"
+
+const
+  ExtSep* = '.'
+    ## The character which separates the base filename from the extension;
+    ## for example, the '.' in ``os.nim``.
+
+
+proc joinPath*(head, tail: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Joins two directory names to one.
+  ##
+  ## For example on Unix:
+  ##
+  ## .. code-block:: nim
+  ##   joinPath("usr", "lib")
+  ##
+  ## results in:
+  ##
+  ## .. code-block:: nim
+  ##   "usr/lib"
+  ##
+  ## If head is the empty string, tail is returned. If tail is the empty
+  ## string, head is returned with a trailing path separator. If tail starts
+  ## with a path separator it will be removed when concatenated to head. Other
+  ## path separators not located on boundaries won't be modified. More
+  ## examples on Unix:
+  ##
+  ## .. code-block:: nim
+  ##   assert joinPath("usr", "") == "usr/"
+  ##   assert joinPath("", "lib") == "lib"
+  ##   assert joinPath("", "/lib") == "/lib"
+  ##   assert joinPath("usr/", "/lib") == "usr/lib"
+  if len(head) == 0:
+    result = tail
+  elif head[len(head)-1] in {DirSep, AltSep}:
+    if tail[0] in {DirSep, AltSep}:
+      result = head & substr(tail, 1)
     else:
-      result.head = ""
-      result.tail = path
-
-  proc parentDirPos(path: string): int =
-    var q = 1
-    if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
-    for i in countdown(len(path)-q, 0):
-      if path[i] in {DirSep, AltSep}: return i
-    result = -1
-
-  proc parentDir*(path: string): string {.
-    noSideEffect, rtl, extern: "nos$1".} =
-    ## Returns the parent directory of `path`.
-    ##
-    ## This is often the same as the ``head`` result of ``splitPath``.
-    ## If there is no parent, "" is returned.
-    ## | Example: ``parentDir("/usr/local/bin") == "/usr/local"``.
-    ## | Example: ``parentDir("/usr/local/bin/") == "/usr/local"``.
-    let sepPos = parentDirPos(path)
-    if sepPos >= 0:
-      result = substr(path, 0, sepPos-1)
+      result = head & tail
+  else:
+    if tail[0] in {DirSep, AltSep}:
+      result = head & tail
     else:
-      result = ""
-
-  proc tailDir*(path: string): string {.
-    noSideEffect, rtl, extern: "nos$1".} =
-    ## Returns the tail part of `path`..
-    ##
-    ## | Example: ``tailDir("/usr/local/bin") == "local/bin"``.
-    ## | Example: ``tailDir("usr/local/bin/") == "local/bin"``.
-    ## | Example: ``tailDir("bin") == ""``.
-    var q = 1
-    if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
-    for i in 0..len(path)-q:
-      if path[i] in {DirSep, AltSep}:
-        return substr(path, i+1)
+      result = head & DirSep & tail
+
+proc joinPath*(parts: varargs[string]): string {.noSideEffect,
+  rtl, extern: "nos$1OpenArray".} =
+  ## The same as `joinPath(head, tail)`, but works with any number of
+  ## directory parts. You need to pass at least one element or the proc
+  ## will assert in debug builds and crash on release builds.
+  result = parts[0]
+  for i in 1..high(parts):
+    result = joinPath(result, parts[i])
+
+proc `/` * (head, tail: string): string {.noSideEffect.} =
+  ## The same as ``joinPath(head, tail)``
+  ##
+  ## Here are some examples for Unix:
+  ##
+  ## .. code-block:: nim
+  ##   assert "usr" / "" == "usr/"
+  ##   assert "" / "lib" == "lib"
+  ##   assert "" / "/lib" == "/lib"
+  ##   assert "usr/" / "/lib" == "usr/lib"
+  return joinPath(head, tail)
+
+proc splitPath*(path: string): tuple[head, tail: string] {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Splits a directory into (head, tail), so that
+  ## ``head / tail == path`` (except for edge cases like "/usr").
+  ##
+  ## Examples:
+  ##
+  ## .. code-block:: nim
+  ##   splitPath("usr/local/bin") -> ("usr/local", "bin")
+  ##   splitPath("usr/local/bin/") -> ("usr/local/bin", "")
+  ##   splitPath("bin") -> ("", "bin")
+  ##   splitPath("/bin") -> ("", "bin")
+  ##   splitPath("") -> ("", "")
+  var sepPos = -1
+  for i in countdown(len(path)-1, 0):
+    if path[i] in {DirSep, AltSep}:
+      sepPos = i
+      break
+  if sepPos >= 0:
+    result.head = substr(path, 0, sepPos-1)
+    result.tail = substr(path, sepPos+1)
+  else:
+    result.head = ""
+    result.tail = path
+
+proc parentDirPos(path: string): int =
+  var q = 1
+  if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
+  for i in countdown(len(path)-q, 0):
+    if path[i] in {DirSep, AltSep}: return i
+  result = -1
+
+proc parentDir*(path: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Returns the parent directory of `path`.
+  ##
+  ## This is often the same as the ``head`` result of ``splitPath``.
+  ## If there is no parent, "" is returned.
+  ## | Example: ``parentDir("/usr/local/bin") == "/usr/local"``.
+  ## | Example: ``parentDir("/usr/local/bin/") == "/usr/local"``.
+  let sepPos = parentDirPos(path)
+  if sepPos >= 0:
+    result = substr(path, 0, sepPos-1)
+  else:
     result = ""
 
-  proc isRootDir*(path: string): bool {.
-    noSideEffect, rtl, extern: "nos$1".} =
-    ## Checks whether a given `path` is a root directory
-    result = parentDirPos(path) < 0
-
-  iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string =
-    ## Walks over all parent directories of a given `path`
-    ##
-    ## If `fromRoot` is set, the traversal will start from the file system root
-    ## diretory. If `inclusive` is set, the original argument will be included
-    ## in the traversal.
-    ##
-    ## Relative paths won't be expanded by this proc. Instead, it will traverse
-    ## only the directories appearing in the relative path.
-    if not fromRoot:
-      var current = path
-      if inclusive: yield path
-      while true:
-        if current.isRootDir: break
-        current = current.parentDir
-        yield current
-    else:
-      for i in countup(0, path.len - 2): # ignore the last /
-        # deal with non-normalized paths such as /foo//bar//baz
-        if path[i] in {DirSep, AltSep} and
-            (i == 0 or path[i-1] notin {DirSep, AltSep}):
-          yield path.substr(0, i)
-
-      if inclusive: yield path
-
-  proc `/../` * (head, tail: string): string {.noSideEffect.} =
-    ## The same as ``parentDir(head) / tail`` unless there is no parent
-    ## directory. Then ``head / tail`` is performed instead.
-    let sepPos = parentDirPos(head)
-    if sepPos >= 0:
-      result = substr(head, 0, sepPos-1) / tail
-    else:
-      result = head / tail
-
-  proc normExt(ext: string): string =
-    if ext == "" or ext[0] == ExtSep: result = ext # no copy needed here
-    else: result = ExtSep & ext
-
-  proc searchExtPos*(path: string): int =
-    ## Returns index of the '.' char in `path` if it signifies the beginning
-    ## of extension. Returns -1 otherwise.
-    # BUGFIX: do not search until 0! .DS_Store is no file extension!
-    result = -1
-    for i in countdown(len(path)-1, 1):
+proc tailDir*(path: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Returns the tail part of `path`..
+  ##
+  ## | Example: ``tailDir("/usr/local/bin") == "local/bin"``.
+  ## | Example: ``tailDir("usr/local/bin/") == "local/bin"``.
+  ## | Example: ``tailDir("bin") == ""``.
+  var q = 1
+  if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
+  for i in 0..len(path)-q:
+    if path[i] in {DirSep, AltSep}:
+      return substr(path, i+1)
+  result = ""
+
+proc isRootDir*(path: string): bool {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Checks whether a given `path` is a root directory
+  result = parentDirPos(path) < 0
+
+iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string =
+  ## Walks over all parent directories of a given `path`
+  ##
+  ## If `fromRoot` is set, the traversal will start from the file system root
+  ## diretory. If `inclusive` is set, the original argument will be included
+  ## in the traversal.
+  ##
+  ## Relative paths won't be expanded by this proc. Instead, it will traverse
+  ## only the directories appearing in the relative path.
+  if not fromRoot:
+    var current = path
+    if inclusive: yield path
+    while true:
+      if current.isRootDir: break
+      current = current.parentDir
+      yield current
+  else:
+    for i in countup(0, path.len - 2): # ignore the last /
+      # deal with non-normalized paths such as /foo//bar//baz
+      if path[i] in {DirSep, AltSep} and
+          (i == 0 or path[i-1] notin {DirSep, AltSep}):
+        yield path.substr(0, i)
+
+    if inclusive: yield path
+
+proc `/../`*(head, tail: string): string {.noSideEffect.} =
+  ## The same as ``parentDir(head) / tail`` unless there is no parent
+  ## directory. Then ``head / tail`` is performed instead.
+  let sepPos = parentDirPos(head)
+  if sepPos >= 0:
+    result = substr(head, 0, sepPos-1) / tail
+  else:
+    result = head / tail
+
+proc normExt(ext: string): string =
+  if ext == "" or ext[0] == ExtSep: result = ext # no copy needed here
+  else: result = ExtSep & ext
+
+proc searchExtPos*(path: string): int =
+  ## Returns index of the '.' char in `path` if it signifies the beginning
+  ## of extension. Returns -1 otherwise.
+  # BUGFIX: do not search until 0! .DS_Store is no file extension!
+  result = -1
+  for i in countdown(len(path)-1, 1):
+    if path[i] == ExtSep:
+      result = i
+      break
+    elif path[i] in {DirSep, AltSep}:
+      break # do not skip over path
+
+proc splitFile*(path: string): tuple[dir, name, ext: string] {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Splits a filename into (dir, filename, extension).
+  ## `dir` does not end in `DirSep`.
+  ## `extension` includes the leading dot.
+  ##
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##   var (dir, name, ext) = splitFile("usr/local/nimc.html")
+  ##   assert dir == "usr/local"
+  ##   assert name == "nimc"
+  ##   assert ext == ".html"
+  ##
+  ## If `path` has no extension, `ext` is the empty string.
+  ## If `path` has no directory component, `dir` is the empty string.
+  ## If `path` has no filename component, `name` and `ext` are empty strings.
+  if path.len == 0 or path[path.len-1] in {DirSep, AltSep}:
+    result = (path, "", "")
+  else:
+    var sepPos = -1
+    var dotPos = path.len
+    for i in countdown(len(path)-1, 0):
       if path[i] == ExtSep:
-        result = i
-        break
+        if dotPos == path.len and i > 0 and
+            path[i-1] notin {DirSep, AltSep}: dotPos = i
       elif path[i] in {DirSep, AltSep}:
-        break # do not skip over path
-
-  proc splitFile*(path: string): tuple[dir, name, ext: string] {.
-    noSideEffect, rtl, extern: "nos$1".} =
-    ## Splits a filename into (dir, filename, extension).
-    ## `dir` does not end in `DirSep`.
-    ## `extension` includes the leading dot.
-    ##
-    ## Example:
-    ##
-    ## .. code-block:: nim
-    ##   var (dir, name, ext) = splitFile("usr/local/nimc.html")
-    ##   assert dir == "usr/local"
-    ##   assert name == "nimc"
-    ##   assert ext == ".html"
-    ##
-    ## If `path` has no extension, `ext` is the empty string.
-    ## If `path` has no directory component, `dir` is the empty string.
-    ## If `path` has no filename component, `name` and `ext` are empty strings.
-    if path.len == 0 or path[path.len-1] in {DirSep, AltSep}:
-      result = (path, "", "")
-    else:
-      var sepPos = -1
-      var dotPos = path.len
-      for i in countdown(len(path)-1, 0):
-        if path[i] == ExtSep:
-          if dotPos == path.len and i > 0 and
-              path[i-1] notin {DirSep, AltSep}: dotPos = i
-        elif path[i] in {DirSep, AltSep}:
-          sepPos = i
-          break
-      result.dir = substr(path, 0, sepPos-1)
-      result.name = substr(path, sepPos+1, dotPos-1)
-      result.ext = substr(path, dotPos)
-
-  proc extractFilename*(path: string): string {.
-    noSideEffect, rtl, extern: "nos$1".} =
-    ## Extracts the filename of a given `path`. This is the same as
-    ## ``name & ext`` from ``splitFile(path)``.
-    if path.len == 0 or path[path.len-1] in {DirSep, AltSep}:
-      result = ""
-    else:
-      result = splitPath(path).tail
-
-
-  proc changeFileExt*(filename, ext: string): string {.
-    noSideEffect, rtl, extern: "nos$1".} =
-    ## Changes the file extension to `ext`.
-    ##
-    ## If the `filename` has no extension, `ext` will be added.
-    ## If `ext` == "" then any extension is removed.
-    ## `Ext` should be given without the leading '.', because some
-    ## filesystems may use a different character. (Although I know
-    ## of none such beast.)
-    var extPos = searchExtPos(filename)
-    if extPos < 0: result = filename & normExt(ext)
-    else: result = substr(filename, 0, extPos-1) & normExt(ext)
-
-  proc addFileExt*(filename, ext: string): string {.
-    noSideEffect, rtl, extern: "nos$1".} =
-    ## Adds the file extension `ext` to `filename`, unless
-    ## `filename` already has an extension.
-    ##
-    ## `Ext` should be given without the leading '.', because some
-    ## filesystems may use a different character.
-    ## (Although I know of none such beast.)
-    var extPos = searchExtPos(filename)
-    if extPos < 0: result = filename & normExt(ext)
-    else: result = filename
-
-  proc cmpPaths*(pathA, pathB: string): int {.
-    noSideEffect, rtl, extern: "nos$1".} =
-    ## Compares two paths.
-    ##
-    ## On a case-sensitive filesystem this is done
-    ## case-sensitively otherwise case-insensitively. Returns:
-    ##
-    ## | 0 iff pathA == pathB
-    ## | < 0 iff pathA < pathB
-    ## | > 0 iff pathA > pathB
-    if FileSystemCaseSensitive:
-      result = cmp(pathA, pathB)
-    else:
-      when defined(nimscript):
-        result = cmpic(pathA, pathB)
-      elif defined(nimdoc): discard
-      else:
-        result = cmpIgnoreCase(pathA, pathB)
-
-  proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
-    ## Checks whether a given `path` is absolute.
-    ##
-    ## On Windows, network paths are considered absolute too.
-    when doslikeFileSystem:
-      var len = len(path)
-      result = (len > 0 and path[0] in {'/', '\\'}) or
-               (len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
-    elif defined(macos):
-      result = path.len > 0 and path[0] != ':'
-    elif defined(RISCOS):
-      result = path[0] == '$'
-    elif defined(posix):
-      result = path[0] == '/'
-
-  proc unixToNativePath*(path: string, drive=""): string {.
-    noSideEffect, rtl, extern: "nos$1".} =
-    ## Converts an UNIX-like path to a native one.
-    ##
-    ## On an UNIX system this does nothing. Else it converts
-    ## '/', '.', '..' to the appropriate things.
-    ##
-    ## On systems with a concept of "drives", `drive` is used to determine
-    ## which drive label to use during absolute path conversion.
-    ## `drive` defaults to the drive of the current working directory, and is
-    ## ignored on systems that do not have a concept of "drives".
-
-    when defined(unix):
-      result = path
+        sepPos = i
+        break
+    result.dir = substr(path, 0, sepPos-1)
+    result.name = substr(path, sepPos+1, dotPos-1)
+    result.ext = substr(path, dotPos)
+
+proc extractFilename*(path: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Extracts the filename of a given `path`. This is the same as
+  ## ``name & ext`` from ``splitFile(path)``.
+  if path.len == 0 or path[path.len-1] in {DirSep, AltSep}:
+    result = ""
+  else:
+    result = splitPath(path).tail
+
+
+proc changeFileExt*(filename, ext: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Changes the file extension to `ext`.
+  ##
+  ## If the `filename` has no extension, `ext` will be added.
+  ## If `ext` == "" then any extension is removed.
+  ## `Ext` should be given without the leading '.', because some
+  ## filesystems may use a different character. (Although I know
+  ## of none such beast.)
+  var extPos = searchExtPos(filename)
+  if extPos < 0: result = filename & normExt(ext)
+  else: result = substr(filename, 0, extPos-1) & normExt(ext)
+
+proc addFileExt*(filename, ext: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Adds the file extension `ext` to `filename`, unless
+  ## `filename` already has an extension.
+  ##
+  ## `Ext` should be given without the leading '.', because some
+  ## filesystems may use a different character.
+  ## (Although I know of none such beast.)
+  var extPos = searchExtPos(filename)
+  if extPos < 0: result = filename & normExt(ext)
+  else: result = filename
+
+proc cmpPaths*(pathA, pathB: string): int {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Compares two paths.
+  ##
+  ## On a case-sensitive filesystem this is done
+  ## case-sensitively otherwise case-insensitively. Returns:
+  ##
+  ## | 0 iff pathA == pathB
+  ## | < 0 iff pathA < pathB
+  ## | > 0 iff pathA > pathB
+  if FileSystemCaseSensitive:
+    result = cmp(pathA, pathB)
+  else:
+    when defined(nimscript):
+      result = cmpic(pathA, pathB)
+    elif defined(nimdoc): discard
     else:
-      var start: int
-      if path[0] == '/':
-        # an absolute path
-        when doslikeFileSystem:
-          if drive != "":
-            result = drive & ":" & DirSep
-          else:
-            result = $DirSep
-        elif defined(macos):
-          result = "" # must not start with ':'
+      result = cmpIgnoreCase(pathA, pathB)
+
+proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
+  ## Checks whether a given `path` is absolute.
+  ##
+  ## On Windows, network paths are considered absolute too.
+  when doslikeFileSystem:
+    var len = len(path)
+    result = (len > 0 and path[0] in {'/', '\\'}) or
+              (len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
+  elif defined(macos):
+    result = path.len > 0 and path[0] != ':'
+  elif defined(RISCOS):
+    result = path[0] == '$'
+  elif defined(posix):
+    result = path[0] == '/'
+
+proc unixToNativePath*(path: string, drive=""): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Converts an UNIX-like path to a native one.
+  ##
+  ## On an UNIX system this does nothing. Else it converts
+  ## '/', '.', '..' to the appropriate things.
+  ##
+  ## On systems with a concept of "drives", `drive` is used to determine
+  ## which drive label to use during absolute path conversion.
+  ## `drive` defaults to the drive of the current working directory, and is
+  ## ignored on systems that do not have a concept of "drives".
+
+  when defined(unix):
+    result = path
+  else:
+    var start: int
+    if path[0] == '/':
+      # an absolute path
+      when doslikeFileSystem:
+        if drive != "":
+          result = drive & ":" & DirSep
         else:
           result = $DirSep
-        start = 1
-      elif path[0] == '.' and path[1] == '/':
-        # current directory
-        result = $CurDir
-        start = 2
+      elif defined(macos):
+        result = "" # must not start with ':'
       else:
-        result = ""
-        start = 0
-
-      var i = start
-      while i < len(path): # ../../../ --> ::::
-        if path[i] == '.' and path[i+1] == '.' and path[i+2] == '/':
-          # parent directory
-          when defined(macos):
-            if result[high(result)] == ':':
-              add result, ':'
-            else:
-              add result, ParDir
+        result = $DirSep
+      start = 1
+    elif path[0] == '.' and path[1] == '/':
+      # current directory
+      result = $CurDir
+      start = 2
+    else:
+      result = ""
+      start = 0
+
+    var i = start
+    while i < len(path): # ../../../ --> ::::
+      if path[i] == '.' and path[i+1] == '.' and path[i+2] == '/':
+        # parent directory
+        when defined(macos):
+          if result[high(result)] == ':':
+            add result, ':'
           else:
-            add result, ParDir & DirSep
-          inc(i, 3)
-        elif path[i] == '/':
-          add result, DirSep
-          inc(i)
+            add result, ParDir
         else:
-          add result, path[i]
-          inc(i)
-
-when defined(nimdoc) and not declared(os):
-  proc getEnv(x: string): string = discard
-  proc existsFile(x: string): bool = discard
-
-when declared(getEnv) or defined(nimscript):
-  proc getHomeDir*(): string {.rtl, extern: "nos$1",
-    tags: [ReadEnvEffect, ReadIOEffect].} =
-    ## Returns the home directory of the current user.
-    ##
-    ## This proc is wrapped by the expandTilde proc for the convenience of
-    ## processing paths coming from user configuration files.
-    when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
-    else: return string(getEnv("HOME")) & "/"
-
-  proc getConfigDir*(): string {.rtl, extern: "nos$1",
-    tags: [ReadEnvEffect, ReadIOEffect].} =
-    ## 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
-    ## 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")) & "/"
-    else: return string(getEnv("HOME")) & "/.config/"
-
-  proc getTempDir*(): string {.rtl, extern: "nos$1",
-    tags: [ReadEnvEffect, ReadIOEffect].} =
-    ## Returns the temporary directory of the current user for applications to
-    ## save temporary files in.
-    when defined(windows): return string(getEnv("TEMP")) & "\\"
-    else: return "/tmp/"
-
-  proc expandTilde*(path: string): string {.
-    tags: [ReadEnvEffect, ReadIOEffect].} =
-    ## Expands a path starting with ``~/`` to a full path.
-    ##
-    ## 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] == '\\'):
-      result = getHomeDir() / path.substr(2)
-    else:
-      result = path
-
-  when not declared(split):
-    iterator split(s: string, sep: char): string =
-      var last = 0
-      if len(s) > 0:
-        while last <= len(s):
-          var first = last
-          while last < len(s) and s[last] != sep: inc(last)
-          yield substr(s, first, last-1)
-          inc(last)
-
-  when not defined(windows) and declared(os):
-    proc checkSymlink(path: string): bool =
-      var rawInfo: Stat
-      if lstat(path, rawInfo) < 0'i32: result = false
-      else: result = S_ISLNK(rawInfo.st_mode)
-
-  const
-    ExeExts* = when defined(windows): ["exe", "cmd", "bat"] else: [""] ## \
-      ## platform specific file extension for executables. On Windows
-      ## ``["exe", "cmd", "bat"]``, on Posix ``[""]``.
-
-  proc findExe*(exe: string, followSymlinks: bool = true;
-                extensions: openarray[string]=ExeExts): string {.
-    tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect].} =
-    ## Searches for `exe` in the current working directory and then
-    ## in directories listed in the ``PATH`` environment variable.
-    ## Returns "" if the `exe` cannot be found. `exe`
-    ## is added the `ExeExts <#ExeExts>`_ file extensions if it has none.
-    ## If the system supports symlinks it also resolves them until it
-    ## meets the actual file. This behavior can be disabled if desired.
-    for ext in extensions:
-      result = addFileExt(exe, ext)
-      if existsFile(result): return
-    var path = string(getEnv("PATH"))
-    for candidate in split(path, PathSep):
-      when defined(windows):
-        var x = (if candidate[0] == '"' and candidate[^1] == '"':
-                  substr(candidate, 1, candidate.len-2) else: candidate) /
-               exe
+          add result, ParDir & DirSep
+        inc(i, 3)
+      elif path[i] == '/':
+        add result, DirSep
+        inc(i)
       else:
-        var x = expandTilde(candidate) / exe
-      for ext in extensions:
-        var x = addFileExt(x, ext)
-        if existsFile(x):
-          when not defined(windows) and declared(os):
-            while followSymlinks: # doubles as if here
-              if x.checkSymlink:
-                var r = newString(256)
-                var len = readlink(x, r, 256)
-                if len < 0:
-                  raiseOSError(osLastError())
-                if len > 256:
-                  r = newString(len+1)
-                  len = readlink(x, r, len)
-                setLen(r, len)
-                if isAbsolute(r):
-                  x = r
-                else:
-                  x = parentDir(x) / r
-              else:
-                break
-          return x
-    result = ""
-
-when defined(nimscript) or (defined(nimdoc) and not declared(os)):
-  {.pop.} # hint[ConvFromXtoItselfNotNeeded]:off
+        add result, path[i]
+        inc(i)
+
+include "includes/oserr"
+when not defined(nimscript):
+  include "includes/osenv"
+
+proc getHomeDir*(): string {.rtl, extern: "nos$1",
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the home directory of the current user.
+  ##
+  ## This proc is wrapped by the expandTilde proc for the convenience of
+  ## processing paths coming from user configuration files.
+  when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
+  else: return string(getEnv("HOME")) & "/"
+
+proc getConfigDir*(): string {.rtl, extern: "nos$1",
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## 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
+  ## 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")) & "/"
+  else: return string(getEnv("HOME")) & "/.config/"
+
+proc getTempDir*(): string {.rtl, extern: "nos$1",
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the temporary directory of the current user for applications to
+  ## save temporary files in.
+  ##
+  ## **Please do not use this**: On Android, it currently
+  ## returns ``getHomeDir()``, and on other Unix based systems it can cause
+  ## security problems too. That said, you can override this implementation
+  ## by adding ``-d:tempDir=mytempname`` to your compiler invokation.
+  when defined(tempDir):
+    const tempDir {.strdefine.}: string = nil
+    return tempDir
+  elif defined(windows): return string(getEnv("TEMP")) & "\\"
+  elif defined(android): return getHomeDir()
+  else: return "/tmp/"
+
+proc expandTilde*(path: string): string {.
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Expands a path starting with ``~/`` to a full path.
+  ##
+  ## 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] == '\\'):
+    result = getHomeDir() / path.substr(2)
+  else:
+    result = path
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 23c8546c4..fa723f593 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -119,7 +119,8 @@ proc execProcess*(command: string,
                                                   poUsePath,
                                                   poEvalCommand}): TaintedString {.
                                                   rtl, extern: "nosp$1",
-                                                  tags: [ExecIOEffect, ReadIOEffect].}
+                                                  tags: [ExecIOEffect, ReadIOEffect,
+                                                  RootEffect].}
   ## A convenience procedure that executes ``command`` with ``startProcess``
   ## and returns its output as a string.
   ## WARNING: this function uses poEvalCommand by default for backward compatibility.
@@ -131,7 +132,8 @@ proc execProcess*(command: string,
   ##  # Note: outp may have an interleave of text from the nim compile
   ##  # and any output from mytestfile when it runs
 
-proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [ExecIOEffect].}
+proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [ExecIOEffect,
+  ReadIOEffect, RootEffect].}
   ## Executes ``command`` and returns its error code. Standard input, output,
   ## error streams are inherited from the calling process. This operation
   ## is also often called `system`:idx:.
@@ -145,7 +147,8 @@ proc startProcess*(command: string,
                    args: openArray[string] = [],
                    env: StringTableRef = nil,
                    options: set[ProcessOption] = {poStdErrToStdOut}):
-              Process {.rtl, extern: "nosp$1", tags: [ExecIOEffect, ReadEnvEffect].}
+              Process {.rtl, extern: "nosp$1", tags: [ExecIOEffect, ReadEnvEffect,
+              RootEffect].}
   ## Starts a process. `Command` is the executable file, `workingDir` is the
   ## process's working directory. If ``workingDir == ""`` the current directory
   ## is used. `args` are the command line arguments that are passed to the
@@ -170,7 +173,7 @@ proc startProcess*(command: string,
 
 proc startCmd*(command: string, options: set[ProcessOption] = {
                poStdErrToStdOut, poUsePath}): Process {.
-               tags: [ExecIOEffect, ReadEnvEffect], deprecated.} =
+               tags: [ExecIOEffect, ReadEnvEffect, RootEffect], deprecated.} =
   ## Deprecated - use `startProcess` directly.
   result = startProcess(command=command, options=options + {poEvalCommand})
 
@@ -721,7 +724,7 @@ elif not defined(useNimRtl):
       inc(i)
 
   type StartProcessData = object
-    sysCommand: cstring
+    sysCommand: string
     sysArgs: cstringArray
     sysEnv: cstringArray
     workingDir: cstring
@@ -735,13 +738,13 @@ elif not defined(useNimRtl):
                              not defined(useClone) and not defined(linux)
   when useProcessAuxSpawn:
     proc startProcessAuxSpawn(data: StartProcessData): Pid {.
-      tags: [ExecIOEffect, ReadEnvEffect], gcsafe.}
+      tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.}
   else:
     proc startProcessAuxFork(data: StartProcessData): Pid {.
-      tags: [ExecIOEffect, ReadEnvEffect], gcsafe.}
+      tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.}
   {.push stacktrace: off, profiler: off.}
   proc startProcessAfterFork(data: ptr StartProcessData) {.
-    tags: [ExecIOEffect, ReadEnvEffect], cdecl, gcsafe.}
+    tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], cdecl, gcsafe.}
   {.pop.}
 
   proc startProcess(command: string,
@@ -762,7 +765,8 @@ elif not defined(useNimRtl):
     var sysCommand: string
     var sysArgsRaw: seq[string]
     if poEvalCommand in options:
-      sysCommand = "/bin/sh"
+      const useShPath {.strdefine.} = "/bin/sh"
+      sysCommand = useShPath
       sysArgsRaw = @[sysCommand, "-c", command]
       assert args.len == 0, "`args` has to be empty when using poEvalCommand."
     else:
@@ -784,7 +788,7 @@ elif not defined(useNimRtl):
     defer: deallocCStringArray(sysEnv)
 
     var data: StartProcessData
-    data.sysCommand = sysCommand
+    shallowCopy(data.sysCommand, sysCommand)
     data.sysArgs = sysArgs
     data.sysEnv = sysEnv
     data.pStdin = pStdin
@@ -949,11 +953,10 @@ elif not defined(useNimRtl):
     discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
 
     if data.optionPoUsePath:
-      when defined(uClibc):
+      when defined(uClibc) or defined(linux):
         # uClibc environment (OpenWrt included) doesn't have the full execvpe
-        discard execve(data.sysCommand, data.sysArgs, data.sysEnv)
-      elif defined(linux) and not defined(android):
-        discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv)
+        let exe = findExe(data.sysCommand)
+        discard execve(exe, data.sysArgs, data.sysEnv)
       else:
         # MacOSX doesn't have execvpe, so we need workaround.
         # On MacOSX we can arrive here only from fork, so this is safe:
@@ -1264,7 +1267,8 @@ elif not defined(useNimRtl):
 proc execCmdEx*(command: string, options: set[ProcessOption] = {
                 poStdErrToStdOut, poUsePath}): tuple[
                 output: TaintedString,
-                exitCode: int] {.tags: [ExecIOEffect, ReadIOEffect], gcsafe.} =
+                exitCode: int] {.tags:
+                [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} =
   ## a convenience proc that runs the `command`, grabs all its output and
   ## exit code and returns both.
   ##
diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim
index 5bdd3bc40..2a5dbc8f8 100644
--- a/lib/pure/parsecfg.nim
+++ b/lib/pure/parsecfg.nim
@@ -320,9 +320,13 @@ proc rawGetTok(c: var CfgParser, tok: var Token) =
     tok.literal = "="
   of '-':
     inc(c.bufpos)
-    if c.buf[c.bufpos] == '-': inc(c.bufpos)
-    tok.kind = tkDashDash
-    tok.literal = "--"
+    if c.buf[c.bufpos] == '-':
+      inc(c.bufpos)
+      tok.kind = tkDashDash
+      tok.literal = "--"
+    else:
+      dec(c.bufpos)
+      getSymbol(c, tok)
   of ':':
     tok.kind = tkColon
     inc(c.bufpos)
diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim
index d6fafec08..00d007d01 100644
--- a/lib/pure/parsesql.nim
+++ b/lib/pure/parsesql.nim
@@ -496,6 +496,7 @@ type
     nkPrimaryKey,
     nkForeignKey,
     nkNotNull,
+    nkNull,
 
     nkStmtList,
     nkDot,
@@ -565,8 +566,13 @@ proc newNode(k: SqlNodeKind, s: string): SqlNode =
   result.strVal = s
 
 proc len*(n: SqlNode): int =
-  if isNil(n.sons): result = 0
-  else: result = n.sons.len
+  if n.kind in {nkIdent, nkStringLit, nkBitStringLit, nkHexStringLit,
+                nkIntegerLit, nkNumericLit}:
+    result = 0
+  else:
+    result = n.sons.len
+
+proc `[]`*(n: SqlNode; i: int): SqlNode = n.sons[i]
 
 proc add*(father, n: SqlNode) =
   if isNil(father.sons): father.sons = @[]
@@ -613,6 +619,9 @@ proc eat(p: var SqlParser, keyw: string) =
   else:
     sqlError(p, keyw.toUpper() & " expected")
 
+proc opt(p: var SqlParser, kind: TokKind) =
+  if p.tok.kind == kind: getTok(p)
+
 proc parseDataType(p: var SqlParser): SqlNode =
   if isKeyw(p, "enum"):
     result = newNode(nkEnumDef)
@@ -705,7 +714,7 @@ proc primary(p: var SqlParser): SqlNode =
       result = newNode(nkCall)
       result.add(a)
       getTok(p)
-      while true:
+      while p.tok.kind != tkParRi:
         result.add(parseExpr(p))
         if p.tok.kind == tkComma: getTok(p)
         else: break
@@ -776,9 +785,19 @@ proc parseConstraint(p: var SqlParser): SqlNode =
   expectIdent(p)
   result.add(newNode(nkIdent, p.tok.literal))
   getTok(p)
-  eat(p, "check")
+  optKeyw(p, "check")
   result.add(parseExpr(p))
 
+proc parseParIdentList(p: var SqlParser, father: SqlNode) =
+  eat(p, tkParLe)
+  while true:
+    expectIdent(p)
+    father.add(newNode(nkIdent, p.tok.literal))
+    getTok(p)
+    if p.tok.kind != tkComma: break
+    getTok(p)
+  eat(p, tkParRi)
+
 proc parseColumnConstraints(p: var SqlParser, result: SqlNode) =
   while true:
     if isKeyw(p, "default"):
@@ -795,6 +814,9 @@ proc parseColumnConstraints(p: var SqlParser, result: SqlNode) =
       getTok(p)
       eat(p, "null")
       result.add(newNode(nkNotNull))
+    elif isKeyw(p, "null"):
+      getTok(p)
+      result.add(newNode(nkNull))
     elif isKeyw(p, "identity"):
       getTok(p)
       result.add(newNode(nkIdentity))
@@ -807,6 +829,7 @@ proc parseColumnConstraints(p: var SqlParser, result: SqlNode) =
     elif isKeyw(p, "constraint"):
       result.add(parseConstraint(p))
     elif isKeyw(p, "unique"):
+      getTok(p)
       result.add(newNode(nkUnique))
     else:
       break
@@ -829,16 +852,6 @@ proc parseIfNotExists(p: var SqlParser, k: SqlNodeKind): SqlNode =
   else:
     result = newNode(k)
 
-proc parseParIdentList(p: var SqlParser, father: SqlNode) =
-  eat(p, tkParLe)
-  while true:
-    expectIdent(p)
-    father.add(newNode(nkIdent, p.tok.literal))
-    getTok(p)
-    if p.tok.kind != tkComma: break
-    getTok(p)
-  eat(p, tkParRi)
-
 proc parseTableConstraint(p: var SqlParser): SqlNode =
   if isKeyw(p, "primary"):
     getTok(p)
@@ -866,20 +879,34 @@ proc parseTableConstraint(p: var SqlParser): SqlNode =
   else:
     sqlError(p, "column definition expected")
 
+proc parseUnique(p: var SqlParser): SqlNode =
+  result = parseExpr(p)
+  if result.kind == nkCall: result.kind = nkUnique
+
 proc parseTableDef(p: var SqlParser): SqlNode =
   result = parseIfNotExists(p, nkCreateTable)
   expectIdent(p)
   result.add(newNode(nkIdent, p.tok.literal))
   getTok(p)
   if p.tok.kind == tkParLe:
-    while true:
-      getTok(p)
-      if p.tok.kind == tkIdentifier or p.tok.kind == tkQuotedIdentifier:
+    getTok(p)
+    while p.tok.kind != tkParRi:
+      if isKeyw(p, "constraint"):
+        result.add parseConstraint(p)
+      elif isKeyw(p, "primary") or isKeyw(p, "foreign"):
+        result.add parseTableConstraint(p)
+      elif isKeyw(p, "unique"):
+        result.add parseUnique(p)
+      elif p.tok.kind == tkIdentifier or p.tok.kind == tkQuotedIdentifier:
         result.add(parseColumnDef(p))
       else:
         result.add(parseTableConstraint(p))
       if p.tok.kind != tkComma: break
+      getTok(p)
     eat(p, tkParRi)
+    # skip additional crap after 'create table (...) crap;'
+    while p.tok.kind notin {tkSemicolon, tkEof}:
+      getTok(p)
 
 proc parseTypeDef(p: var SqlParser): SqlNode =
   result = parseIfNotExists(p, nkCreateType)
@@ -1046,7 +1073,7 @@ proc parseSelect(p: var SqlParser): SqlNode =
       getTok(p)
     result.add(n)
 
-proc parseStmt(p: var SqlParser): SqlNode =
+proc parseStmt(p: var SqlParser; parent: SqlNode) =
   if isKeyw(p, "create"):
     getTok(p)
     optKeyw(p, "cached")
@@ -1058,21 +1085,23 @@ proc parseStmt(p: var SqlParser): SqlNode =
     optKeyw(p, "unique")
     optKeyw(p, "hash")
     if isKeyw(p, "table"):
-      result = parseTableDef(p)
+      parent.add parseTableDef(p)
     elif isKeyw(p, "type"):
-      result = parseTypeDef(p)
+      parent.add parseTypeDef(p)
     elif isKeyw(p, "index"):
-      result = parseIndexDef(p)
+      parent.add parseIndexDef(p)
     else:
       sqlError(p, "TABLE expected")
   elif isKeyw(p, "insert"):
-    result = parseInsert(p)
+    parent.add parseInsert(p)
   elif isKeyw(p, "update"):
-    result = parseUpdate(p)
+    parent.add parseUpdate(p)
   elif isKeyw(p, "delete"):
-    result = parseDelete(p)
+    parent.add parseDelete(p)
   elif isKeyw(p, "select"):
-    result = parseSelect(p)
+    parent.add parseSelect(p)
+  elif isKeyw(p, "begin"):
+    getTok(p)
   else:
     sqlError(p, "CREATE expected")
 
@@ -1089,9 +1118,8 @@ proc parse(p: var SqlParser): SqlNode =
   ## Syntax errors raise an `EInvalidSql` exception.
   result = newNode(nkStmtList)
   while p.tok.kind != tkEof:
-    var s = parseStmt(p)
+    parseStmt(p, result)
     eat(p, tkSemicolon)
-    result.add(s)
   if result.len == 1:
     result = result.sons[0]
 
@@ -1147,6 +1175,8 @@ proc ra(n: SqlNode, s: var string, indent: int) =
     rs(n, s, indent)
   of nkNotNull:
     s.add(" not null")
+  of nkNull:
+    s.add(" null")
   of nkDot:
     ra(n.sons[0], s, indent)
     s.add(".")
@@ -1330,6 +1360,10 @@ proc renderSQL*(n: SqlNode): string =
   result = ""
   ra(n, result, 0)
 
+proc `$`*(n: SqlNode): string =
+  ## an alias for `renderSQL`.
+  renderSQL(n)
+
 when not defined(testing) and isMainModule:
   echo(renderSQL(parseSQL(newStringStream("""
       CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic');
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index 6a52e2cd5..5ae2d9182 100644
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -139,7 +139,7 @@ proc addChoice(dest: var Peg, elem: Peg) =
     else: add(dest, elem)
   else: add(dest, elem)
 
-template multipleOp(k: PegKind, localOpt: expr) =
+template multipleOp(k: PegKind, localOpt: untyped) =
   result.kind = k
   result.sons = @[]
   for x in items(a):
@@ -328,32 +328,32 @@ proc newNonTerminal*(name: string, line, column: int): NonTerminal {.
   result.line = line
   result.col = column
 
-template letters*: expr =
+template letters*: Peg =
   ## expands to ``charset({'A'..'Z', 'a'..'z'})``
   charSet({'A'..'Z', 'a'..'z'})
 
-template digits*: expr =
+template digits*: Peg =
   ## expands to ``charset({'0'..'9'})``
   charSet({'0'..'9'})
 
-template whitespace*: expr =
+template whitespace*: Peg =
   ## expands to ``charset({' ', '\9'..'\13'})``
   charSet({' ', '\9'..'\13'})
 
-template identChars*: expr =
+template identChars*: Peg =
   ## expands to ``charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})``
   charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'})
 
-template identStartChars*: expr =
+template identStartChars*: Peg =
   ## expands to ``charset({'A'..'Z', 'a'..'z', '_'})``
   charSet({'a'..'z', 'A'..'Z', '_'})
 
-template ident*: expr =
+template ident*: Peg =
   ## same as ``[a-zA-Z_][a-zA-z_0-9]*``; standard identifier
   sequence(charSet({'a'..'z', 'A'..'Z', '_'}),
            *charSet({'a'..'z', 'A'..'Z', '0'..'9', '_'}))
 
-template natural*: expr =
+template natural*: Peg =
   ## same as ``\d+``
   +digits
 
@@ -514,10 +514,10 @@ proc bounds*(c: Captures,
 when not useUnicode:
   type
     Rune = char
-  template fastRuneAt(s, i, ch: expr) =
+  template fastRuneAt(s, i, ch) =
     ch = s[i]
     inc(i)
-  template runeLenAt(s, i: expr): expr = 1
+  template runeLenAt(s, i): untyped = 1
 
   proc isAlpha(a: char): bool {.inline.} = return a in {'a'..'z','A'..'Z'}
   proc isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'}
@@ -735,7 +735,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
     else: result = -1
   of pkRule, pkList: assert false
 
-template fillMatches(s, caps, c: expr) =
+template fillMatches(s, caps, c) =
   for k in 0..c.ml-1:
     let startIdx = c.matches[k][0]
     let endIdx = c.matches[k][1]
diff --git a/lib/pure/random.nim b/lib/pure/random.nim
index 8a32f7d9a..27fbfad45 100644
--- a/lib/pure/random.nim
+++ b/lib/pure/random.nim
@@ -101,7 +101,7 @@ proc random*[T](a: openArray[T]): T =
   ## returns a random element from the openarray `a`.
   result = a[random(a.low..a.len)]
 
-proc randomize*(seed: int) {.benign.} =
+proc randomize*(seed: int64) {.benign.} =
   ## Initializes the random number generator with a specific seed.
   state.a0 = ui(seed shr 16)
   state.a1 = ui(seed and 0xffff)
@@ -123,7 +123,7 @@ when not defined(nimscript):
       proc getMil(t: Time): int {.importcpp: "getTime", nodecl.}
       randomize(getMil times.getTime())
     else:
-      let time = int(times.epochTime() * 1_000_000_000)
+      let time = int64(times.epochTime() * 1_000_000_000)
       randomize(time)
 
 {.pop.}
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index 438b48beb..69f673990 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -411,12 +411,12 @@ when not defined(js):
     result.writeDataImpl = fsWriteData
     result.flushImpl = fsFlush
 
-  proc newFileStream*(filename: string, mode: FileMode = fmRead): FileStream =
+  proc newFileStream*(filename: string, mode: FileMode = fmRead, bufSize: int = -1): FileStream =
     ## creates a new stream from the file named `filename` with the mode `mode`.
     ## If the file cannot be opened, nil is returned. See the `system
     ## <system.html>`_ module for a list of available FileMode enums.
     var f: File
-    if open(f, filename, mode): result = newFileStream(f)
+    if open(f, filename, mode, bufSize): result = newFileStream(f)
 
 
 when true:
diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim
index 83b86fd54..bf26d2e59 100644
--- a/lib/pure/strscans.nim
+++ b/lib/pure/strscans.nim
@@ -187,7 +187,6 @@ overloaded to handle both single characters and sets of character.
   if scanp(content, idx, +( ~{'\L', '\0'} -> entry.add(peekChar($input))), '\L'):
     result.add entry
 
-
 Calling ordinary Nim procs inside the macro is possible:
 
 .. code-block:: nim
@@ -253,6 +252,30 @@ is performed.
 
   for r in collectLinks(body):
     echo r
+
+In this example both macros are combined seamlessly in order to maximise 
+efficiency and perform different checks.
+
+.. code-block:: nim
+
+  iterator parseIps*(soup: string): string =
+    ## ipv4 only!
+    const digits = {'0'..'9'}
+    var a, b, c, d: int
+    var buf = ""
+    var idx = 0
+    while idx < soup.len:
+      if scanp(soup, idx, (`digits`{1,3}, '.', `digits`{1,3}, '.',
+               `digits`{1,3}, '.', `digits`{1,3}) -> buf.add($_)):
+        discard buf.scanf("$i.$i.$i.$i", a, b, c, d)
+        if (a >= 0 and a <= 254) and
+           (b >= 0 and b <= 254) and
+           (c >= 0 and c <= 254) and
+           (d >= 0 and d <= 254):
+          yield buf
+      buf.setLen(0) # need to clear `buf` each time, cause it might contain garbage
+      idx.inc
+
 ]##
 
 
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 346248a25..2f2b89955 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -1551,6 +1551,37 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
   # copy the rest:
   add result, substr(s, i)
 
+proc multiReplace*(s: string, replacements: varargs[(string, string)]): string {.noSideEffect.} =
+  ## Same as replace, but specialized for doing multiple replacements in a single
+  ## pass through the input string.
+  ##
+  ## Calling replace multiple times after each other is inefficient and result in too many allocations
+  ## follwed by immediate deallocations as portions of the string gets replaced.
+  ## multiReplace performs all replacements in a single pass.
+  ##
+  ## If the resulting string is not longer than the original input string, only a single
+  ## memory allocation is required.
+  ##
+  ## The order of the replacements does matter. Earlier replacements are preferred over later
+  ## replacements in the argument list.
+  result = newStringOfCap(s.len)
+  var i = 0
+  var fastChk: set[char] = {}
+  for tup in replacements: fastChk.incl(tup[0][0]) # Include first character of all replacements
+  while i < s.len:
+    block sIteration:
+      # Assume most chars in s are not candidates for any replacement operation
+      if s[i] in fastChk:
+        for tup in replacements:
+          if s.continuesWith(tup[0], i):
+            add result, tup[1]
+            inc(i, tup[0].len)
+            break sIteration
+      # No matching replacement found
+      # copy current character from s
+      add result, s[i]
+      inc(i)
+
 proc delete*(s: var string, first, last: int) {.noSideEffect,
   rtl, extern: "nsuDelete".} =
   ## Deletes in `s` the characters at position `first` .. `last`.
@@ -2324,6 +2355,10 @@ when isMainModule:
 
   doAssert "  foo\n  bar".indent(4, "Q") == "QQQQ  foo\nQQQQ  bar"
 
+  doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab"
+  doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", "PEOPLE!")) == "HELLO PEOPLE!"
+  doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa"
+
   doAssert isAlphaAscii('r')
   doAssert isAlphaAscii('A')
   doAssert(not isAlphaAscii('$'))
diff --git a/lib/pure/subexes.nim b/lib/pure/subexes.nim
index 351b3c086..9d807abd4 100644
--- a/lib/pure/subexes.nim
+++ b/lib/pure/subexes.nim
@@ -46,12 +46,12 @@ type
     num, i, lineLen: int
 {.deprecated: [TFormatParser: FormatParser].}
 
-template call(x: stmt) {.immediate.} =
+template call(x: untyped): untyped =
   p.i = i
   x
   i = p.i
 
-template callNoLineLenTracking(x: stmt) {.immediate.} =
+template callNoLineLenTracking(x: untyped): untyped =
   let oldLineLen = p.lineLen
   p.i = i
   x
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index 87c663c3d..871ac5d39 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -578,7 +578,7 @@ template styledEchoProcessArg(f: File, cmd: TerminalCmd) =
   when cmd == resetStyle:
     resetAttributes(f)
 
-macro styledWriteLine*(f: File, m: varargs[expr]): stmt =
+macro styledWriteLine*(f: File, m: varargs[typed]): untyped =
   ## Similar to ``writeLine``, but treating terminal style arguments specially.
   ## When some argument is ``Style``, ``set[Style]``, ``ForegroundColor``,
   ## ``BackgroundColor`` or ``TerminalCmd`` then it is not sent directly to
@@ -614,16 +614,13 @@ macro styledWriteLine*(f: File, m: varargs[expr]): stmt =
   result.add(newCall(bindSym"write", f, newStrLitNode("\n")))
   if reset: result.add(newCall(bindSym"resetAttributes", f))
 
-macro callStyledEcho(args: varargs[expr]): stmt =
+macro styledEcho*(args: varargs[untyped]): untyped =
+  ## Echoes styles arguments to stdout using ``styledWriteLine``.
   result = newCall(bindSym"styledWriteLine")
   result.add(bindSym"stdout")
-  for arg in children(args[0][1]):
+  for arg in children(args):
     result.add(arg)
 
-template styledEcho*(args: varargs[expr]): expr =
-  ## Echoes styles arguments to stdout using ``styledWriteLine``.
-  callStyledEcho(args)
-
 proc getch*(): char =
   ## Read a single character from the terminal, blocking until it is entered.
   ## The character is not printed to the terminal.
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 1bda94d14..03054e2d9 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -222,6 +222,12 @@ proc toSeconds*(time: Time): float {.tags: [], raises: [], benign.}
 proc `-`*(a, b: Time): int64 {.
   rtl, extern: "ntDiffTime", tags: [], raises: [], noSideEffect, benign.}
   ## computes the difference of two calendar times. Result is in seconds.
+  ## .. code-block:: nim 
+  ## 
+  ##     let a = fromSeconds(1_000_000_000)
+  ##     let b = fromSeconds(1_500_000_000)
+  ##     echo initInterval(seconds=int(b - a)) 
+  ##     # (milliseconds: 0, seconds: 20, minutes: 53, hours: 0, days: 5787, months: 0, years: 0) 
 
 proc `<`*(a, b: Time): bool {.
   rtl, extern: "ntLtTime", tags: [], raises: [], noSideEffect.} =
@@ -301,6 +307,11 @@ proc `+`*(ti1, ti2: TimeInterval): TimeInterval =
   result.years = carryO + ti1.years + ti2.years
 
 proc `-`*(ti: TimeInterval): TimeInterval =
+  ## Reverses a time interval
+  ## .. code-block:: nim
+  ##
+  ##     let day = -initInterval(hours=24)
+  ##     echo day  # -> (milliseconds: 0, seconds: 0, minutes: 0, hours: 0, days: -1, months: 0, years: 0)
   result = TimeInterval(
     milliseconds: -ti.milliseconds,
     seconds: -ti.seconds,
@@ -313,6 +324,12 @@ proc `-`*(ti: TimeInterval): TimeInterval =
 
 proc `-`*(ti1, ti2: TimeInterval): TimeInterval =
   ## Subtracts TimeInterval ``ti1`` from ``ti2``.
+  ## .. code-block:: nim 
+  ## 
+  ##     let a = fromSeconds(1_000_000_000) 
+  ##     let b = fromSeconds(1_500_000_000) 
+  ##     echo b.toTimeInterval - a.toTimeInterval 
+  ##     # (milliseconds: 0, seconds: -40, minutes: -6, hours: 1, days: -2, months: -2, years: 16)
   result = ti1 + (-ti2)
 
 proc isLeapYear*(year: int): bool =
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index 1ea7b8545..28691fcb4 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -13,7 +13,24 @@
 ##
 ## The test status and name is printed after any output or traceback.
 ##
-## Example:
+## Tests can be nested, however failure of a nested test will not mark the
+## parent test as failed. Setup and teardown are inherited. Setup can be
+## overridden locally.
+##
+## Compiled test files return the number of failed test as exit code, while
+## ``nim c -r <testfile.nim>`` exits with 0 or 1
+##
+## Running a single test
+## ---------------------
+##
+## Simply specify the test name as a command line argument.
+##
+## .. code::
+##
+##   nim c -r test "my super awesome test name"
+##
+## Example
+## -------
 ##
 ## .. code:: nim
 ##
@@ -42,16 +59,9 @@
 ##         discard v[4]
 ##
 ##     echo "suite teardown: run once after the tests"
-##
-##
-## Tests can be nested, however failure of a nested test will not mark the
-## parent test as failed. Setup and teardown are inherited. Setup can be
-## overridden locally.
-## Compiled test files return the number of failed test as exit code, while
-## nim c -r <testfile.nim> exits with 0 or 1
 
 import
-  macros, strutils, streams, times
+  macros, strutils, streams, times, sets
 
 when declared(stdout):
   import os
@@ -111,6 +121,7 @@ var
 
   checkpoints {.threadvar.}: seq[string]
   formatters {.threadvar.}: seq[OutputFormatter]
+  testsToRun {.threadvar.}: HashSet[string]
 
 when declared(stdout):
   abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
@@ -290,12 +301,22 @@ method suiteEnded*(formatter: JUnitOutputFormatter) =
   formatter.stream.writeLine("\t</testsuite>")
 
 proc shouldRun(testName: string): bool =
-  result = true
+  if testsToRun.len == 0:
+    return true
 
-proc ensureFormattersInitialized() =
+  result = testName in testsToRun
+
+proc ensureInitialized() =
   if formatters == nil:
     formatters = @[OutputFormatter(defaultConsoleFormatter())]
 
+  if not testsToRun.isValid:
+    testsToRun.init()
+    when declared(os):
+      # Read tests to run from the command line.
+      for i in 1 .. paramCount():
+        testsToRun.incl(paramStr(i))
+
 # These two procs are added as workarounds for
 # https://github.com/nim-lang/Nim/issues/5549
 proc suiteEnded() =
@@ -335,7 +356,7 @@ template suite*(name, body) {.dirty.} =
   ##  [Suite] test suite for addition
   ##    [OK] 2 + 2 = 4
   ##    [OK] (2 + -2) != 4
-  bind formatters, ensureFormattersInitialized, suiteEnded
+  bind formatters, ensureInitialized, suiteEnded
 
   block:
     template setup(setupBody: untyped) {.dirty, used.} =
@@ -348,7 +369,7 @@ template suite*(name, body) {.dirty.} =
 
     let testSuiteName {.used.} = name
 
-    ensureFormattersInitialized()
+    ensureInitialized()
     try:
       for formatter in formatters:
         formatter.suiteStarted(name)
@@ -370,9 +391,9 @@ template test*(name, body) {.dirty.} =
   ## .. code-block::
   ##
   ##  [OK] roses are red
-  bind shouldRun, checkpoints, formatters, ensureFormattersInitialized, testEnded
+  bind shouldRun, checkpoints, formatters, ensureInitialized, testEnded
 
-  ensureFormattersInitialized()
+  ensureInitialized()
 
   if shouldRun(name):
     checkpoints = @[]
@@ -433,14 +454,14 @@ template fail* =
   ##  fail()
   ##
   ## outputs "Checkpoint A" before quitting.
-  bind ensureFormattersInitialized
+  bind ensureInitialized
 
   when declared(testStatusIMPL):
     testStatusIMPL = FAILED
   else:
     programResult += 1
 
-  ensureFormattersInitialized()
+  ensureInitialized()
 
     # var stackTrace: string = nil
   for formatter in formatters:
diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim
index c7e0ed1da..d8e4ed52f 100644
--- a/lib/pure/uri.nim
+++ b/lib/pure/uri.nim
@@ -81,7 +81,7 @@ proc parsePath(uri: string, i: var int, result: var Uri) =
   i.inc parseUntil(uri, result.path, {'?', '#'}, i)
 
   # The 'mailto' scheme's PATH actually contains the hostname/username
-  if result.scheme.toLower == "mailto":
+  if cmpIgnoreCase(result.scheme, "mailto") == 0:
     parseAuthority(result.path, result)
     result.path.setLen(0)
 
@@ -215,7 +215,7 @@ proc combine*(base: Uri, reference: Uri): Uri =
   ##   let bar = combine(parseUri("http://example.com/foo/bar/"), parseUri("baz"))
   ##   assert bar.path == "/foo/bar/baz"
 
-  template setAuthority(dest, src: expr): stmt =
+  template setAuthority(dest, src): untyped =
     dest.hostname = src.hostname
     dest.username = src.username
     dest.port = src.port
diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim
index 7cfb62157..45696c80c 100644
--- a/lib/pure/xmltree.nim
+++ b/lib/pure/xmltree.nim
@@ -338,7 +338,7 @@ proc xmlConstructor(e: NimNode): NimNode {.compileTime.} =
   else:
     result = newCall("newXmlTree", toStrLit(a))
 
-macro `<>`*(x: expr): expr {.immediate.} =
+macro `<>`*(x: untyped): untyped =
   ## Constructor macro for XML. Example usage:
   ##
   ## .. code-block:: nim
diff --git a/lib/system.nim b/lib/system.nim
index 8f653c1e0..9efa850ed 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -80,9 +80,10 @@ type
   `nil` {.magic: "Nil".}
 
   expr* {.magic: Expr, deprecated.} ## meta type to denote an expression (for templates)
-    ## **Deprecated** since version 0.15. Use ``untyped`` instead.
+                                    ## **Deprecated** since version 0.15. Use ``untyped`` instead.
   stmt* {.magic: Stmt, deprecated.} ## meta type to denote a statement (for templates)
-    ## **Deprecated** since version 0.15. Use ``typed`` instead.
+                                    ## **Deprecated** since version 0.15. Use ``typed`` instead.
+
   void* {.magic: "VoidType".}   ## meta type to denote the absence of any type
   auto* {.magic: Expr.} ## meta type for automatic type determination
   any* = distinct auto ## meta type for any supported type
@@ -230,9 +231,22 @@ proc reset*[T](obj: var T) {.magic: "Reset", noSideEffect.}
   ## resets an object `obj` to its initial (binary zero) value. This needs to
   ## be called before any possible `object branch transition`:idx:.
 
-# for low and high the return type T may not be correct, but
-# we handle that with compiler magic in semLowHigh()
-proc high*[T](x: T): T {.magic: "High", noSideEffect.}
+type
+  range*{.magic: "Range".}[T] ## Generic type to construct range types.
+  array*{.magic: "Array".}[I, T]  ## Generic type to construct
+                                  ## fixed-length arrays.
+  openArray*{.magic: "OpenArray".}[T]  ## Generic type to construct open arrays.
+                                       ## Open arrays are implemented as a
+                                       ## pointer to the array data and a
+                                       ## length field.
+  varargs*{.magic: "Varargs".}[T] ## Generic type to construct a varargs type.
+  seq*{.magic: "Seq".}[T]  ## Generic type to construct sequences.
+  set*{.magic: "Set".}[T]  ## Generic type to construct bit sets.
+
+  UncheckedArray* {.unchecked.}[T] = array[0, T]
+    ## Array with no bounds checking
+
+proc high*[T: Ordinal](x: T): T {.magic: "High", noSideEffect.}
   ## returns the highest possible index of an array, a sequence, a string or
   ## the highest possible value of an ordinal value `x`. As a special
   ## semantic rule, `x` may also be a type identifier.
@@ -244,8 +258,21 @@ proc high*[T](x: T): T {.magic: "High", noSideEffect.}
   ##  high(2) #=> 9223372036854775807
   ##  high(int) #=> 9223372036854775807
 
+proc high*[T: Ordinal](x: typeDesc[T]): T {.magic: "High", noSideEffect.}
+proc high*[T](x: openArray[T]): int {.magic: "High", noSideEffect.}
+proc high*[I, T](x: array[I, T]): I {.magic: "High", noSideEffect.}
+proc high*[I, T](x: typeDesc[array[I, T]]): I {.magic: "High", noSideEffect.}
+proc high*(x: cstring): int {.magic: "High", noSideEffect.}
+proc high*(x: string): int {.magic: "High", noSideEffect.}
+
+proc low*[T: Ordinal](x: typeDesc[T]): T {.magic: "Low", noSideEffect.}
+proc low*[T](x: openArray[T]): int {.magic: "Low", noSideEffect.}
+proc low*[I, T](x: array[I, T]): I {.magic: "Low", noSideEffect.}
 proc low*[T](x: T): T {.magic: "Low", noSideEffect.}
-  ## returns the lowest possible index of an array, a sequence, a string or
+proc low*[I, T](x: typeDesc[array[I, T]]): I {.magic: "Low", noSideEffect.}
+proc low*(x: cstring): int {.magic: "Low", noSideEffect.}
+proc low*(x: string): int {.magic: "Low", noSideEffect.}
+## returns the lowest possible index of an array, a sequence, a string or
   ## the lowest possible value of an ordinal value `x`. As a special
   ## semantic rule, `x` may also be a type identifier.
   ##
@@ -255,18 +282,6 @@ proc low*[T](x: T): T {.magic: "Low", noSideEffect.}
   ##  low(2) #=> -9223372036854775808
   ##  low(int) #=> -9223372036854775808
 
-type
-  range*{.magic: "Range".}[T] ## Generic type to construct range types.
-  array*{.magic: "Array".}[I, T]  ## Generic type to construct
-                                  ## fixed-length arrays.
-  openArray*{.magic: "OpenArray".}[T]  ## Generic type to construct open arrays.
-                                       ## Open arrays are implemented as a
-                                       ## pointer to the array data and a
-                                       ## length field.
-  varargs*{.magic: "Varargs".}[T] ## Generic type to construct a varargs type.
-  seq*{.magic: "Seq".}[T]  ## Generic type to construct sequences.
-  set*{.magic: "Set".}[T]  ## Generic type to construct bit sets.
-
 when defined(nimArrIdx):
   # :array|openarray|string|seq|cstring|tuple
   proc `[]`*[I: Ordinal;T](a: T; i: I): T {.
@@ -380,8 +395,6 @@ include "system/inclrtl"
 const NoFakeVars* = defined(nimscript) ## true if the backend doesn't support \
   ## "fake variables" like 'var EBADF {.importc.}: cint'.
 
-const ArrayDummySize = when defined(cpu16): 10_000 else: 100_000_000
-
 when not defined(JS):
   type
     TGenericSeq {.compilerproc, pure, inheritable.} = object
@@ -389,10 +402,9 @@ when not defined(JS):
       when defined(gogc):
         elemSize: int
     PGenericSeq {.exportc.} = ptr TGenericSeq
-    UncheckedCharArray {.unchecked.} = array[0..ArrayDummySize, char]
     # len and space without counting the terminating zero:
     NimStringDesc {.compilerproc, final.} = object of TGenericSeq
-      data: UncheckedCharArray
+      data: UncheckedArray[char]
     NimString = ptr NimStringDesc
 
 when not defined(JS) and not defined(nimscript):
@@ -413,7 +425,7 @@ type
     ## is an int type ranging from one to the maximum value
     ## of an int. This type is often useful for documentation and debugging.
 
-  RootObj* {.exportc: "TNimObject", inheritable.} =
+  RootObj* {.compilerProc, inheritable.} =
     object ## the root of Nim's object hierarchy. Objects should
            ## inherit from RootObj or one of its descendants. However,
            ## objects that have no ancestor are allowed.
@@ -421,7 +433,7 @@ type
 
   RootEffect* {.compilerproc.} = object of RootObj ## \
     ## base effect class; each effect should
-    ## inherit from `TEffect` unless you know what
+    ## inherit from `RootEffect` unless you know what
     ## you doing.
   TimeEffect* = object of RootEffect   ## Time effect.
   IOEffect* = object of RootEffect     ## IO effect.
@@ -1174,6 +1186,8 @@ proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.}
 template `isnot` *(x, y: untyped): untyped = not (x is y)
   ## Negated version of `is`. Equivalent to ``not(x is y)``.
 
+proc `of` *[T, S](x: typeDesc[T], y: typeDesc[S]): bool {.magic: "Of", noSideEffect.}
+proc `of` *[T, S](x: T, y: typeDesc[S]): bool {.magic: "Of", noSideEffect.}
 proc `of` *[T, S](x: T, y: S): bool {.magic: "Of", noSideEffect.}
   ## Checks if `x` has a type of `y`
   ##
@@ -1312,7 +1326,7 @@ const
   hostCPU* {.magic: "HostCPU".}: string = ""
     ## a string that describes the host CPU. Possible values:
     ## "i386", "alpha", "powerpc", "powerpc64", "powerpc64el", "sparc",
-    ## "amd64", "mips", "mipsel", "arm", "arm64".
+    ## "amd64", "mips", "mipsel", "arm", "arm64", "mips64", "mips64el".
 
   seqShallowFlag = low(int)
 
@@ -1599,8 +1613,7 @@ type # these work for most platforms:
   culonglong* {.importc: "unsigned long long", nodecl.} = uint64
     ## This is the same as the type ``unsigned long long`` in *C*.
 
-  cstringArray* {.importc: "char**", nodecl.} = ptr
-    array[0..ArrayDummySize, cstring]
+  cstringArray* {.importc: "char**", nodecl.} = ptr UncheckedArray[cstring]
     ## This is binary compatible to the type ``char**`` in *C*. The array's
     ## high value is large enough to disable bounds checking in practice.
     ## Use `cstringArrayToSeq` to convert it into a ``seq[string]``.
@@ -1951,30 +1964,34 @@ iterator countdown*[T](a, b: T, step = 1): T {.inline.} =
       yield res
       dec(res, step)
 
-template countupImpl(incr: untyped) {.oldimmediate, dirty.} =
+iterator countup*[S, T](a: S, b: T, step = 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
+  ## efficiency reasons.
   when T is IntLikeForCount:
     var res = int(a)
     while res <= int(b):
       yield T(res)
-      incr
+      inc(res, step)
   else:
     var res: T = T(a)
     while res <= b:
       yield res
-      incr
-
-iterator countup*[S, T](a: S, b: T, step = 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
-  ## efficiency reasons.
-  countupImpl:
-    inc(res, step)
+      inc(res, step)
 
 iterator `..`*[S, T](a: S, b: T): T {.inline.} =
   ## An alias for `countup`.
-  countupImpl:
-    inc(res)
+  when T is IntLikeForCount:
+    var res = int(a)
+    while res <= int(b):
+      yield T(res)
+      inc(res)
+  else:
+    var res: T = T(a)
+    while res <= b:
+      yield res
+      inc(res)
 
 iterator `||`*[S, T](a: S, b: T, annotation=""): T {.
   inline, magic: "OmpParFor", sideEffect.} =
@@ -2551,7 +2568,7 @@ const NimStackTrace = compileOption("stacktrace")
 
 template coroutinesSupportedPlatform(): bool =
   when defined(sparc) or defined(ELATE) or compileOption("gc", "v2") or
-    defined(boehmgc) or defined(gogc) or defined(nogc) or defined(gcStack) or
+    defined(boehmgc) or defined(gogc) or defined(nogc) or defined(gcRegions) or
     defined(gcMarkAndSweep):
     false
   else:
@@ -2752,10 +2769,10 @@ when not defined(JS): #and not defined(nimscript):
   {.push stack_trace: off, profiler:off.}
 
   when hasAlloc:
-    when not defined(gcStack):
+    when not defined(gcRegions):
       proc initGC() {.gcsafe.}
     when not defined(boehmgc) and not defined(useMalloc) and
-        not defined(gogc) and not defined(gcStack):
+        not defined(gogc) and not defined(gcRegions):
       proc initAllocator() {.inline.}
 
     proc initStackBottom() {.inline, compilerproc.} =
@@ -3040,7 +3057,8 @@ when not defined(JS): #and not defined(nimscript):
       ## creates a NULL terminated cstringArray from `a`. The result has to
       ## be freed with `deallocCStringArray` after it's not needed anymore.
       result = cast[cstringArray](alloc0((a.len+1) * sizeof(cstring)))
-      let x = cast[ptr array[0..ArrayDummySize, string]](a)
+
+      let x = cast[ptr UncheckedArray[string]](a)
       for i in 0 .. a.high:
         result[i] = cast[cstring](alloc0(x[i].len+1))
         copyMem(result[i], addr(x[i][0]), x[i].len)
@@ -3269,7 +3287,7 @@ when not defined(JS): #and not defined(nimscript):
     proc finished*[T: proc](x: T): bool {.noSideEffect, inline.} =
       ## can be used to determine if a first class iterator has finished.
       {.emit: """
-      `result` = *((NI*) `x`.ClE_0) < 0;
+      `result` = ((NI*) `x`.ClE_0)[1] < 0;
       """.}
 
 elif defined(JS):
@@ -3752,7 +3770,7 @@ when hasAlloc:
 proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} =
   ## generates a tuple constructor expression listing all the local variables
   ## in the current scope. This is quite fast as it does not rely
-  ## on any debug or runtime information. Note that in constrast to what
+  ## on any debug or runtime information. Note that in contrast to what
   ## the official signature says, the return type is not ``RootObj`` but a
   ## tuple of a structure that depends on the current scope. Example:
   ##
diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim
index 885b01621..8c3801687 100644
--- a/lib/system/atomics.nim
+++ b/lib/system/atomics.nim
@@ -172,7 +172,7 @@ elif defined(vcc) and hasThreadSupport:
         header: "<intrin.h>".}
     else:
       proc addAndFetch*(p: ptr int, val: int): int {.
-        importcpp: "_InterlockedExchangeAdd(static_cast<NI volatile *>(#), #)",
+        importcpp: "_InterlockedExchangeAdd(reinterpret_cast<LONG volatile *>(#), static_cast<LONG>(#))",
         header: "<intrin.h>".}
   else:
     when sizeof(int) == 8:
diff --git a/lib/system/cellsets.nim b/lib/system/cellsets.nim
index 776a2b7ec..f26cb86ab 100644
--- a/lib/system/cellsets.nim
+++ b/lib/system/cellsets.nim
@@ -30,13 +30,12 @@ type
     key: ByteAddress   # start address at bit 0
     bits: array[BitIndex, int] # a bit vector
 
-  PPageDescArray = ptr array[0..1000_000, PPageDesc]
+  PPageDescArray = ptr UncheckedArray[PPageDesc]
   CellSet {.final, pure.} = object
     counter, max: int
     head: PPageDesc
     data: PPageDescArray
-
-  PCellArray = ptr array[0..100_000_000, PCell]
+  PCellArray = ptr UncheckedArray[PCell]
   CellSeq {.final, pure.} = object
     len, cap: int
     d: PCellArray
diff --git a/lib/system/channels.nim b/lib/system/channels.nim
index 572f12e84..1b90e245f 100644
--- a/lib/system/channels.nim
+++ b/lib/system/channels.nim
@@ -22,7 +22,7 @@ when not declared(NimString):
 type
   pbytes = ptr array[0.. 0xffff, byte]
   RawChannel {.pure, final.} = object ## msg queue for a thread
-    rd, wr, count, mask: int
+    rd, wr, count, mask, maxItems: int
     data: pbytes
     lock: SysLock
     cond: SysCond
@@ -37,11 +37,12 @@ type
 
 const ChannelDeadMask = -2
 
-proc initRawChannel(p: pointer) =
+proc initRawChannel(p: pointer, maxItems: int) =
   var c = cast[PRawChannel](p)
   initSysLock(c.lock)
   initSysCond(c.cond)
   c.mask = -1
+  c.maxItems = maxItems
 
 proc deinitRawChannel(p: pointer) =
   var c = cast[PRawChannel](p)
@@ -203,28 +204,41 @@ proc rawRecv(q: PRawChannel, data: pointer, typ: PNimType) =
   storeAux(data, addr(q.data[q.rd * typ.size]), typ, q, mLoad)
   q.rd = (q.rd + 1) and q.mask
 
-template lockChannel(q: expr, action: stmt) {.immediate.} =
+template lockChannel(q, action): untyped =
   acquireSys(q.lock)
   action
   releaseSys(q.lock)
 
-template sendImpl(q: expr) {.immediate.} =
+proc sendImpl(q: PRawChannel, typ: PNimType, msg: pointer, noBlock: bool): bool =
   if q.mask == ChannelDeadMask:
     sysFatal(DeadThreadError, "cannot send message; thread died")
   acquireSys(q.lock)
-  var typ = cast[PNimType](getTypeInfo(msg))
-  rawSend(q, unsafeAddr(msg), typ)
+  if q.maxItems > 0:
+    # Wait until count is less than maxItems
+    if noBlock and q.count >= q.maxItems:
+      releaseSys(q.lock)
+      return
+
+    while q.count >= q.maxItems:
+      waitSysCond(q.cond, q.lock)
+
+  rawSend(q, msg, typ)
   q.elemType = typ
   releaseSys(q.lock)
   signalSysCond(q.cond)
+  result = true
 
-proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) =
+proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) {.inline.} =
   ## sends a message to a thread. `msg` is deeply copied.
-  var q = cast[PRawChannel](addr(c))
-  sendImpl(q)
+  discard sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), false)
+
+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`.
+  sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), true)
 
 proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
-  # to save space, the generic is as small as possible
   q.ready = true
   while q.count <= 0:
     waitSysCond(q.cond, q.lock)
@@ -233,6 +247,9 @@ proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
     releaseSys(q.lock)
     sysFatal(ValueError, "cannot receive message of wrong type")
   rawRecv(q, res, typ)
+  if q.maxItems > 0 and q.count == q.maxItems - 1:
+    # Parent thread is awaiting in send. Wake it up.
+    signalSysCond(q.cond)
 
 proc recv*[TMsg](c: var Channel[TMsg]): TMsg =
   ## receives a message from the channel `c`. This blocks until
@@ -267,9 +284,11 @@ proc peek*[TMsg](c: var Channel[TMsg]): int =
   else:
     result = -1
 
-proc open*[TMsg](c: var Channel[TMsg]) =
-  ## opens a channel `c` for inter thread communication.
-  initRawChannel(addr(c))
+proc open*[TMsg](c: var Channel[TMsg], maxItems: int = 0) =
+  ## opens a channel `c` for inter thread communication. The `send` operation
+  ## will block until number of unprocessed items is less than `maxItems`.
+  ## For unlimited queue set `maxItems` to 0.
+  initRawChannel(addr(c), maxItems)
 
 proc close*[TMsg](c: var Channel[TMsg]) =
   ## closes a channel `c` and frees its associated resources.
diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim
index e12f08842..65ba2278c 100644
--- a/lib/system/deepcopy.nim
+++ b/lib/system/deepcopy.nim
@@ -148,11 +148,11 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
             let realType = x.typ
             sysAssert realType == mt, " types do differ"
           # this version should work for any possible GC:
-          let size = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[].size else: mt.base.size
-          let z = newObj(mt, size)
+          let typ = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[] else: mt.base
+          let z = newObj(mt, typ.size)
           unsureAsgnRef(cast[PPointer](dest), z)
           tab.put(s2, z)
-          genericDeepCopyAux(z, s2, mt.base, tab)
+          genericDeepCopyAux(z, s2, typ, tab)
       else:
         unsureAsgnRef(cast[PPointer](dest), z)
   of tyPtr:
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index 6c163f711..db4d70613 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -293,11 +293,11 @@ proc raiseExceptionAux(e: ref Exception) =
           quitOrDebug()
       else:
         # ugly, but avoids heap allocations :-)
-        template xadd(buf, s, slen: expr) =
+        template xadd(buf, s, slen) =
           if L + slen < high(buf):
             copyMem(addr(buf[L]), cstring(s), slen)
             inc L, slen
-        template add(buf, s: expr) =
+        template add(buf, s) =
           xadd(buf, s, s.len)
         var buf: array[0..2000, char]
         var L = 0
@@ -404,7 +404,8 @@ when not defined(noSignalHandler):
       GC_enable()
     else:
       var msg: cstring
-      template asgn(y: expr) = msg = y
+      template asgn(y) =
+        msg = y
       processSignal(sign, asgn)
       showErrorMessage(msg)
     when defined(endb): dbgAborting = true
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index e37f601b4..80aa5cf1b 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -238,21 +238,6 @@ proc nimGCunref(p: pointer) {.compilerProc.} =
 
 include gc_common
 
-proc prepareDealloc(cell: PCell) =
-  when useMarkForDebug:
-    gcAssert(cell notin gch.marked, "Cell still alive!")
-  let t = cell.typ
-  if t.finalizer != nil:
-    # the finalizer could invoke something that
-    # allocates memory; this could trigger a garbage
-    # collection. Since we are already collecting we
-    # prevend recursive entering here by a lock.
-    # XXX: we should set the cell's children to nil!
-    inc(gch.recGcLock)
-    (cast[Finalizer](t.finalizer))(cellToUsr(cell))
-    dec(gch.recGcLock)
-  decTypeSize(cell, t)
-
 template beforeDealloc(gch: var GcHeap; c: PCell; msg: typed) =
   when false:
     for i in 0..gch.decStack.len-1:
@@ -274,6 +259,9 @@ proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} =
     sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 2")
   sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 5")
 
+proc nimGCunrefRC1(p: pointer) {.compilerProc, inline.} =
+  decRef(usrToCell(p))
+
 proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
   # the code generator calls this proc!
   gcAssert(not isOnStack(dest), "asgnRef")
@@ -754,8 +742,8 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
 
 #[
   This method is conditionally marked with an attribute so that it gets ignored by the LLVM ASAN
-  (Address SANitizer) intrumentation as it will raise false errors due to the implementation of 
-  garbage collection that is used by Nim. For more information, please see the documentation of 
+  (Address SANitizer) intrumentation as it will raise false errors due to the implementation of
+  garbage collection that is used by Nim. For more information, please see the documentation of
   `CLANG_NO_SANITIZE_ADDRESS` in `lib/nimbase.h`.
  ]#
 proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl, codegenDecl: "CLANG_NO_SANITIZE_ADDRESS $# $#$#".} =
@@ -920,11 +908,13 @@ when not defined(useNimRtl):
     else:
       inc(gch.recGcLock)
   proc GC_enable() =
-    if gch.recGcLock > 0:
-      when hasThreadSupport and hasSharedHeap:
-        discard atomicDec(gch.recGcLock, 1)
-      else:
-        dec(gch.recGcLock)
+    if gch.recGcLock <= 0:
+      raise newException(AssertionError,
+          "API usage error: GC_enable called but GC is already enabled")
+    when hasThreadSupport and hasSharedHeap:
+      discard atomicDec(gch.recGcLock, 1)
+    else:
+      dec(gch.recGcLock)
 
   proc GC_setStrategy(strategy: GC_Strategy) =
     discard
@@ -945,7 +935,6 @@ when not defined(useNimRtl):
     release(gch)
 
   proc GC_getStatistics(): string =
-    GC_disable()
     result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
              "[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
              "[GC] stack scans: " & $gch.stat.stackScans & "\n" &
@@ -961,6 +950,5 @@ when not defined(useNimRtl):
         result = result & "[GC]   stack " & stack.bottom.repr & "[GC]     max stack size " & cast[pointer](stack.maxStackSize).repr & "\n"
     else:
       result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
-    GC_enable()
 
 {.pop.} # profiler: off, stackTrace: off
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index cd03d2a54..220331e96 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -351,3 +351,35 @@ else:
 # ----------------------------------------------------------------------------
 # end of non-portable code
 # ----------------------------------------------------------------------------
+
+proc prepareDealloc(cell: PCell) =
+  when declared(useMarkForDebug):
+    when useMarkForDebug:
+      gcAssert(cell notin gch.marked, "Cell still alive!")
+  let t = cell.typ
+  if t.finalizer != nil:
+    # the finalizer could invoke something that
+    # allocates memory; this could trigger a garbage
+    # collection. Since we are already collecting we
+    # prevend recursive entering here by a lock.
+    # XXX: we should set the cell's children to nil!
+    inc(gch.recGcLock)
+    (cast[Finalizer](t.finalizer))(cellToUsr(cell))
+    dec(gch.recGcLock)
+  decTypeSize(cell, t)
+
+proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) =
+  ## Frees the thread local heap. Runs every finalizer if ``runFinalizers```
+  ## is true. If ``allowGcAfterwards`` is true, a minimal amount of allocation
+  ## happens to ensure the GC can continue to work after the call
+  ## to ``deallocHeap``.
+  if runFinalizers:
+    for x in allObjects(gch.region):
+      if isCell(x):
+        # cast to PCell is correct here:
+        var c = cast[PCell](x)
+        prepareDealloc(c)
+  deallocOsPages(gch.region)
+  zeroMem(addr gch.region, sizeof(gch.region))
+  if allowGcAfterwards:
+    initGC()
diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim
index a97e974a1..e03140d05 100644
--- a/lib/system/gc_ms.nim
+++ b/lib/system/gc_ms.nim
@@ -221,18 +221,6 @@ when defined(nimGcRefLeak):
 
 include gc_common
 
-proc prepareDealloc(cell: PCell) =
-  if cell.typ.finalizer != nil:
-    # the finalizer could invoke something that
-    # allocates memory; this could trigger a garbage
-    # collection. Since we are already collecting we
-    # prevend recursive entering here by a lock.
-    # XXX: we should set the cell's children to nil!
-    inc(gch.recGcLock)
-    (cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell))
-    dec(gch.recGcLock)
-  decTypeSize cell, cell.typ
-
 proc initGC() =
   when not defined(useNimRtl):
     gch.cycleThreshold = InitialThreshold
@@ -506,11 +494,13 @@ when not defined(useNimRtl):
     else:
       inc(gch.recGcLock)
   proc GC_enable() =
-    if gch.recGcLock > 0:
-      when hasThreadSupport and hasSharedHeap:
-        atomicDec(gch.recGcLock, 1)
-      else:
-        dec(gch.recGcLock)
+    if gch.recGcLock <= 0:
+      raise newException(AssertionError,
+          "API usage error: GC_enable called but GC is already enabled")
+    when hasThreadSupport and hasSharedHeap:
+      atomicDec(gch.recGcLock, 1)
+    else:
+      dec(gch.recGcLock)
 
   proc GC_setStrategy(strategy: GC_Strategy) = discard
 
@@ -530,7 +520,6 @@ when not defined(useNimRtl):
     release(gch)
 
   proc GC_getStatistics(): string =
-    GC_disable()
     result = "[GC] total memory: " & $getTotalMem() & "\n" &
              "[GC] occupied memory: " & $getOccupiedMem() & "\n" &
              "[GC] collections: " & $gch.stat.collections & "\n" &
@@ -542,6 +531,5 @@ when not defined(useNimRtl):
         result = result & "[GC]   stack " & stack.bottom.repr & "[GC]     max stack size " & $stack.maxStackSize & "\n"
     else:
       result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
-    GC_enable()
 
 {.pop.}
diff --git a/lib/system/gc_stack.nim b/lib/system/gc_regions.nim
index e7b9f65a7..e9efbdfb0 100644
--- a/lib/system/gc_stack.nim
+++ b/lib/system/gc_regions.nim
@@ -44,10 +44,6 @@ type
     typ: PNimType
     nextFinal: ptr ObjHeader # next object with finalizer
 
-  Hole = object # stacks can have holes. Otherwise 'growObj' would be insane.
-    zeroTyp: pointer # overlaid with 'typ' field. Always 'nil'.
-    size: int # size of the free slot
-
   Chunk = ptr BaseChunk
   BaseChunk = object
     next: Chunk
@@ -55,7 +51,15 @@ type
     head, tail: ptr ObjHeader # first and last object in chunk that
                               # has a finalizer attached to it
 
+const
+  MaxSmallObject = 128
+
 type
+  FreeEntry = ptr object
+    next: FreeEntry
+  SizedFreeEntry = ptr object
+    next: SizedFreeEntry
+    size: int
   StackPtr = object
     bump: pointer
     remaining: int
@@ -66,12 +70,21 @@ type
     bump: pointer
     head, tail: Chunk
     nextChunkSize, totalSize: int
-    hole: ptr Hole # we support individual freeing
+    freeLists: array[MaxSmallObject div MemAlign, FreeEntry]
+    holes: SizedFreeEntry
     when hasThreadSupport:
       lock: SysLock
 
+  SeqHeader = object # minor hack ahead: Since we know that seqs
+                     # and strings cannot have finalizers, we use the field
+                     # instead for a 'region' field so that they can grow
+                     # and shrink safely.
+    typ: PNimType
+    region: ptr MemRegion
+
 var
   tlRegion {.threadVar.}: MemRegion
+#  tempStrRegion {.threadVar.}: MemRegion  # not yet used
 
 template withRegion*(r: MemRegion; body: untyped) =
   let oldRegion = tlRegion
@@ -85,6 +98,9 @@ template withRegion*(r: MemRegion; body: untyped) =
 template inc(p: pointer, s: int) =
   p = cast[pointer](cast[int](p) +% s)
 
+template dec(p: pointer, s: int) =
+  p = cast[pointer](cast[int](p) -% s)
+
 template `+!`(p: pointer, s: int): pointer =
   cast[pointer](cast[int](p) +% s)
 
@@ -128,7 +144,22 @@ proc allocSlowPath(r: var MemRegion; size: int) =
   r.tail = fresh
   r.remaining = s - sizeof(BaseChunk)
 
-proc alloc(r: var MemRegion; size: int): pointer {.inline.} =
+proc alloc(r: var MemRegion; size: int): pointer =
+  if size <= MaxSmallObject:
+    var it = r.freeLists[size div MemAlign]
+    if it != nil:
+      r.freeLists[size div MemAlign] = it.next
+      return pointer(it)
+  else:
+    var it = r.holes
+    var prev: SizedFreeEntry = nil
+    while it != nil:
+      if it.size >= size:
+        if prev != nil: prev.next = it.next
+        else: r.holes = it.next
+        return pointer(it)
+      prev = it
+      it = it.next
   if size > r.remaining:
     allocSlowPath(r, size)
   sysAssert(size <= r.remaining, "size <= r.remaining")
@@ -145,12 +176,23 @@ proc runFinalizers(c: Chunk) =
       (cast[Finalizer](it.typ.finalizer))(it+!sizeof(ObjHeader))
     it = it.nextFinal
 
-when false:
-  proc dealloc(r: var MemRegion; p: pointer) =
-    let it = cast[ptr ObjHeader](p-!sizeof(ObjHeader))
-    if it.typ != nil and it.typ.finalizer != nil:
-      (cast[Finalizer](it.typ.finalizer))(p)
-    it.typ = nil
+proc dealloc(r: var MemRegion; p: pointer; size: int) =
+  let it = cast[ptr ObjHeader](p-!sizeof(ObjHeader))
+  if it.typ != nil and it.typ.finalizer != nil:
+    (cast[Finalizer](it.typ.finalizer))(p)
+  it.typ = nil
+  # it is benefitial to not use the free lists here:
+  if r.bump -! size == p:
+    dec r.bump, size
+  elif size <= MaxSmallObject:
+    let it = cast[FreeEntry](p)
+    it.next = r.freeLists[size div MemAlign]
+    r.freeLists[size div MemAlign] = it
+  else:
+    let it = cast[SizedFreeEntry](p)
+    it.size = size
+    it.next = r.holes
+    r.holes = it
 
 proc deallocAll(r: var MemRegion; head: Chunk) =
   var it = head
@@ -175,12 +217,15 @@ template computeRemaining(r): untyped =
 
 proc setObstackPtr*(r: var MemRegion; sp: StackPtr) =
   # free everything after 'sp':
-  if sp.current != nil:
+  if sp.current.next != nil:
     deallocAll(r, sp.current.next)
     sp.current.next = nil
-  else:
-    deallocAll(r, r.head)
-    r.head = nil
+    # better leak this memory than be sorry:
+    for i in 0..high(r.freeLists): r.freeLists[i] = nil
+    r.holes = nil
+  #else:
+  #  deallocAll(r, r.head)
+  #  r.head = nil
   r.bump = sp.bump
   r.tail = sp.current
   r.remaining = sp.remaining
@@ -191,17 +236,28 @@ proc deallocAll*() = tlRegion.deallocAll()
 
 proc deallocOsPages(r: var MemRegion) = r.deallocAll()
 
-proc joinRegion*(dest: var MemRegion; src: MemRegion) =
-  # merging is not hard.
-  if dest.head.isNil:
-    dest.head = src.head
-  else:
-    dest.tail.next = src.head
-  dest.tail = src.tail
-  dest.bump = src.bump
-  dest.remaining = src.remaining
-  dest.nextChunkSize = max(dest.nextChunkSize, src.nextChunkSize)
-  inc dest.totalSize, src.totalSize
+template withScratchRegion*(body: untyped) =
+  var scratch: MemRegion
+  let oldRegion = tlRegion
+  tlRegion = scratch
+  try:
+    body
+  finally:
+    tlRegion = oldRegion
+    deallocAll(scratch)
+
+when false:
+  proc joinRegion*(dest: var MemRegion; src: MemRegion) =
+    # merging is not hard.
+    if dest.head.isNil:
+      dest.head = src.head
+    else:
+      dest.tail.next = src.head
+    dest.tail = src.tail
+    dest.bump = src.bump
+    dest.remaining = src.remaining
+    dest.nextChunkSize = max(dest.nextChunkSize, src.nextChunkSize)
+    inc dest.totalSize, src.totalSize
 
 proc isOnHeap*(r: MemRegion; p: pointer): bool =
   # the tail chunk is the largest, so check it first. It's also special
@@ -213,159 +269,6 @@ proc isOnHeap*(r: MemRegion; p: pointer): bool =
     if it >= p and p <= it+!it.size: return true
     it = it.next
 
-when false:
-  # essential feature for later: copy data over from one region to another
-
-  proc isInteriorPointer(r: MemRegion; p: pointer): pointer =
-    discard " we cannot patch stack pointers anyway!"
-
-  type
-    PointerStackChunk = object
-      next, prev: ptr PointerStackChunk
-      len: int
-      data: array[128, pointer]
-
-  template head(s: PointerStackChunk): untyped = s.prev
-  template tail(s: PointerStackChunk): untyped = s.next
-
-  include chains
-
-  proc push(r: var MemRegion; s: var PointerStackChunk; x: pointer) =
-    if s.len < high(s.data):
-      s.data[s.len] = x
-      inc s.len
-    else:
-      let fresh = cast[ptr PointerStackChunk](alloc(r, sizeof(PointerStackChunk)))
-      fresh.len = 1
-      fresh.data[0] = x
-      fresh.next = nil
-      fresh.prev = nil
-      append(s, fresh)
-
-
-  proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk;
-                          dest, src: pointer, mt: PNimType) {.benign.}
-  proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk;
-                          dest, src: pointer, n: ptr TNimNode) {.benign.} =
-    var
-      d = cast[ByteAddress](dest)
-      s = cast[ByteAddress](src)
-    case n.kind
-    of nkSlot:
-      genericDeepCopyAux(cast[pointer](d +% n.offset),
-                         cast[pointer](s +% n.offset), n.typ)
-    of nkList:
-      for i in 0..n.len-1:
-        genericDeepCopyAux(dest, src, n.sons[i])
-    of nkCase:
-      var dd = selectBranch(dest, n)
-      var m = selectBranch(src, n)
-      # reset if different branches are in use; note different branches also
-      # imply that's not self-assignment (``x = x``)!
-      if m != dd and dd != nil:
-        genericResetAux(dest, dd)
-      copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
-              n.typ.size)
-      if m != nil:
-        genericDeepCopyAux(dest, src, m)
-    of nkNone: sysAssert(false, "genericDeepCopyAux")
-
-  proc copyDeepString(dr: var MemRegion; stack: var PointerStackChunk; src: NimString): NimString {.inline.} =
-    result = rawNewStringNoInit(dr, src.len)
-    result.len = src.len
-    copyMem(result.data, src.data, src.len + 1)
-
-  proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk;
-                          dest, src: pointer, mt: PNimType) =
-    var
-      d = cast[ByteAddress](dest)
-      s = cast[ByteAddress](src)
-    sysAssert(mt != nil, "genericDeepCopyAux 2")
-    case mt.kind
-    of tyString:
-      var x = cast[PPointer](dest)
-      var s2 = cast[PPointer](s)[]
-      if s2 == nil:
-        x[] = nil
-      else:
-        x[] = copyDeepString(cast[NimString](s2))
-    of tySequence:
-      var s2 = cast[PPointer](src)[]
-      var seq = cast[PGenericSeq](s2)
-      var x = cast[PPointer](dest)
-      if s2 == nil:
-        x[] = nil
-        return
-      sysAssert(dest != nil, "genericDeepCopyAux 3")
-      x[] = newSeq(mt, seq.len)
-      var dst = cast[ByteAddress](cast[PPointer](dest)[])
-      for i in 0..seq.len-1:
-        genericDeepCopyAux(dr, stack,
-          cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
-          cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
-                       GenericSeqSize),
-          mt.base)
-    of tyObject:
-      # we need to copy m_type field for tyObject, as it could be empty for
-      # sequence reallocations:
-      var pint = cast[ptr PNimType](dest)
-      pint[] = cast[ptr PNimType](src)[]
-      if mt.base != nil:
-        genericDeepCopyAux(dr, stack, dest, src, mt.base)
-      genericDeepCopyAux(dr, stack, dest, src, mt.node)
-    of tyTuple:
-      genericDeepCopyAux(dr, stack, dest, src, mt.node)
-    of tyArray, tyArrayConstr:
-      for i in 0..(mt.size div mt.base.size)-1:
-        genericDeepCopyAux(dr, stack,
-                           cast[pointer](d +% i*% mt.base.size),
-                           cast[pointer](s +% i*% mt.base.size), mt.base)
-    of tyRef:
-      let s2 = cast[PPointer](src)[]
-      if s2 == nil:
-        cast[PPointer](dest)[] = nil
-      else:
-        # we modify the header of the cell temporarily; instead of the type
-        # field we store a forwarding pointer. XXX This is bad when the cloning
-        # fails due to OOM etc.
-        let x = usrToCell(s2)
-        let forw = cast[int](x.typ)
-        if (forw and 1) == 1:
-          # we stored a forwarding pointer, so let's use that:
-          let z = cast[pointer](forw and not 1)
-          unsureAsgnRef(cast[PPointer](dest), z)
-        else:
-          let realType = x.typ
-          let z = newObj(realType, realType.base.size)
-
-          unsureAsgnRef(cast[PPointer](dest), z)
-          x.typ = cast[PNimType](cast[int](z) or 1)
-          genericDeepCopyAux(dr, stack, z, s2, realType.base)
-          x.typ = realType
-    else:
-      copyMem(dest, src, mt.size)
-
-  proc joinAliveDataFromRegion*(dest: var MemRegion; src: var MemRegion;
-                                root: pointer): pointer =
-    # we mark the alive data and copy only alive data over to 'dest'.
-    # This is O(liveset) but it nicely compacts memory, so it's fine.
-    # We use the 'typ' field as a forwarding pointer. The forwarding
-    # pointers have bit 0 set, so we can disambiguate them.
-    # We allocate a temporary stack in 'src' that we later free:
-    var s: PointerStackChunk
-    s.len = 1
-    s.data[0] = root
-    while s.len > 0:
-      var p: pointer
-      if s.tail == nil:
-        p = s.data[s.len-1]
-        dec s.len
-      else:
-        p = s.tail.data[s.tail.len-1]
-        dec s.tail.len
-        if s.tail.len == 0:
-          unlink(s, s.tail)
-
 proc rawNewObj(r: var MemRegion, typ: PNimType, size: int): pointer =
   var res = cast[ptr ObjHeader](alloc(r, size + sizeof(ObjHeader)))
   res.typ = typ
@@ -374,6 +277,12 @@ proc rawNewObj(r: var MemRegion, typ: PNimType, size: int): pointer =
     r.head.head = res
   result = res +! sizeof(ObjHeader)
 
+proc rawNewSeq(r: var MemRegion, typ: PNimType, size: int): pointer =
+  var res = cast[ptr SeqHeader](alloc(r, size + sizeof(SeqHeader)))
+  res.typ = typ
+  res.region = addr(r)
+  result = res +! sizeof(SeqHeader)
+
 proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
   result = rawNewObj(tlRegion, typ, size)
   zeroMem(result, size)
@@ -384,28 +293,37 @@ proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
   when defined(memProfiler): nimProfile(size)
 
 proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
-  result = newObj(typ, size)
+  let size = roundup(addInt(mulInt(len, typ.base.size), GenericSeqSize),
+                     MemAlign)
+  result = rawNewSeq(tlRegion, typ, size)
+  zeroMem(result, size)
   cast[PGenericSeq](result).len = len
   cast[PGenericSeq](result).reserved = len
 
+proc newStr(typ: PNimType, len: int; init: bool): pointer {.compilerRtl.} =
+  let size = roundup(addInt(len, GenericSeqSize), MemAlign)
+  result = rawNewSeq(tlRegion, typ, size)
+  if init: zeroMem(result, size)
+  cast[PGenericSeq](result).len = 0
+  cast[PGenericSeq](result).reserved = len
+
 proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
   result = rawNewObj(tlRegion, typ, size)
   zeroMem(result, size)
 
 proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
-  result = newObj(typ, size)
-  cast[PGenericSeq](result).len = len
-  cast[PGenericSeq](result).reserved = len
+  result = newSeq(typ, len)
 
-proc growObj(region: var MemRegion; old: pointer, newsize: int): pointer =
-  let typ = cast[ptr ObjHeader](old -! sizeof(ObjHeader)).typ
-  result = rawNewObj(region, typ, newsize)
+proc growObj(regionUnused: var MemRegion; old: pointer, newsize: int): pointer =
+  let sh = cast[ptr SeqHeader](old -! sizeof(SeqHeader))
+  let typ = sh.typ
+  result = rawNewSeq(sh.region[], typ,
+                     roundup(newsize, MemAlign))
   let elemSize = if typ.kind == tyString: 1 else: typ.base.size
   let oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
-  copyMem(result, old, oldsize)
   zeroMem(result +! oldsize, newsize-oldsize)
+  copyMem(result, old, oldsize)
+  dealloc(sh.region[], old, roundup(oldsize, MemAlign))
 
 proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
   result = growObj(tlRegion, old, newsize)
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 768f9bc17..f23be2d78 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -53,7 +53,7 @@ proc isNimException(): bool {.asmNoStackFrame.} =
   else:
     asm "return `lastJSError`.m_type;"
 
-proc getCurrentException*(): ref Exception =
+proc getCurrentException*(): ref Exception {.compilerRtl, benign.} =
   if isNimException(): result = cast[ref Exception](lastJSError)
 
 proc getCurrentExceptionMsg*(): string =
@@ -157,10 +157,10 @@ proc reraiseException() {.compilerproc, asmNoStackFrame.} =
 
     asm "throw lastJSError;"
 
-proc raiseOverflow {.exportc: "raiseOverflow", noreturn.} =
+proc raiseOverflow {.exportc: "raiseOverflow", noreturn, compilerProc.} =
   raise newException(OverflowError, "over- or underflow")
 
-proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn.} =
+proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn, compilerProc.} =
   raise newException(DivByZeroError, "division by zero")
 
 proc raiseRangeError() {.compilerproc, noreturn.} =
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index 5b5ba9490..d2160fdac 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -16,7 +16,7 @@
 const
   debugGC = false # we wish to debug the GC...
   logGC = false
-  traceGC = defined(smokeCycles) # extensive debugging
+  traceGC = false # extensive debugging
   alwaysCycleGC = defined(smokeCycles)
   alwaysGC = defined(fulldebug) # collect after every memory
                                 # allocation (for debugging)
@@ -34,7 +34,7 @@ const
 
 type
   PPointer = ptr pointer
-  ByteArray = array[0..ArrayDummySize, byte]
+  ByteArray = UncheckedArray[byte]
   PByte = ptr ByteArray
   PString = ptr string
 {.deprecated: [TByteArray: ByteArray].}
@@ -543,7 +543,7 @@ elif defined(nogc):
   include "system/cellsets"
 
 else:
-  when not defined(gcStack):
+  when not defined(gcRegions):
     include "system/alloc"
 
     include "system/cellsets"
@@ -551,9 +551,9 @@ else:
       sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell")
   when compileOption("gc", "v2"):
     include "system/gc2"
-  elif defined(gcStack):
+  elif defined(gcRegions):
     # XXX due to bootstrapping reasons, we cannot use  compileOption("gc", "stack") here
-    include "system/gc_stack"
+    include "system/gc_regions"
   elif defined(gcMarkAndSweep):
     # XXX use 'compileOption' here
     include "system/gc_ms"
diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim
index eeada5c51..8939615cd 100644
--- a/lib/system/platforms.nim
+++ b/lib/system/platforms.nim
@@ -24,6 +24,8 @@ type
     amd64,                     ## x86_64 (AMD64); 64 bit x86 compatible CPU
     mips,                      ## Mips based processor
     mipsel,                    ## Little Endian Mips based processor
+    mips64,                    ## 64-bit MIPS processor
+    mips64el,                  ## Little Endian 64-bit MIPS processor
     arm,                       ## ARM based processor
     arm64,                     ## ARM64 based processor
     vm,                        ## Some Virtual machine: Nim's VM or JavaScript
@@ -33,7 +35,8 @@ type
   OsPlatform* {.pure.} = enum ## the OS this program will run on.
     none, dos, windows, os2, linux, morphos, skyos, solaris,
     irix, netbsd, freebsd, openbsd, aix, palmos, qnx, amiga,
-    atari, netware, macos, macosx, haiku, js, nimVM, standalone
+    atari, netware, macos, macosx, haiku, android, js, nimVM,
+    standalone
 
 const
   targetOS* = when defined(windows): OsPlatform.windows
@@ -56,6 +59,7 @@ const
               elif defined(macosx): OsPlatform.macosx
               elif defined(macos): OsPlatform.macos
               elif defined(haiku): OsPlatform.haiku
+              elif defined(android): OsPlatform.android
               elif defined(js): OsPlatform.js
               elif defined(nimrodVM): OsPlatform.nimVM
               elif defined(standalone): OsPlatform.standalone
@@ -73,6 +77,8 @@ const
                elif defined(amd64): CpuPlatform.amd64
                elif defined(mips): CpuPlatform.mips
                elif defined(mipsel): CpuPlatform.mipsel
+               elif defined(mips64): CpuPlatform.mips64
+               elif defined(mips64el): CpuPlatform.mips64el
                elif defined(arm): CpuPlatform.arm
                elif defined(arm64): CpuPlatform.arm64
                elif defined(vm): CpuPlatform.vm
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index 7444661e3..7b6d93fe0 100644
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -86,11 +86,11 @@ proc writeBuffer(f: File, buffer: pointer, len: Natural): int =
   checkErr(f)
 
 proc writeBytes(f: File, a: openArray[int8|uint8], start, len: Natural): int =
-  var x = cast[ptr array[0..1000_000_000, int8]](a)
-  result = writeBuffer(f, addr(x[start]), len)
+  var x = cast[ptr UncheckedArray[int8]](a)
+  result = writeBuffer(f, addr(x[int(start)]), len)
 proc writeChars(f: File, a: openArray[char], start, len: Natural): int =
-  var x = cast[ptr array[0..1000_000_000, int8]](a)
-  result = writeBuffer(f, addr(x[start]), len)
+  var x = cast[ptr UncheckedArray[int8]](a)
+  result = writeBuffer(f, addr(x[int(start)]), len)
 
 proc write(f: File, s: string) =
   if writeBuffer(f, cstring(s), s.len) != s.len:
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index d3d3d5a95..c3150cb07 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -38,6 +38,13 @@ when declared(allocAtomic):
 
   template allocStrNoInit(size: untyped): untyped =
     cast[NimString](boehmAllocAtomic(size))
+elif defined(gcRegions):
+  template allocStr(size: untyped): untyped =
+    cast[NimString](newStr(addr(strDesc), size, true))
+
+  template allocStrNoInit(size: untyped): untyped =
+    cast[NimString](newStr(addr(strDesc), size, false))
+
 else:
   template allocStr(size: untyped): untyped =
     cast[NimString](newObj(addr(strDesc), size))
@@ -99,7 +106,7 @@ proc copyString(src: NimString): NimString {.compilerRtl.} =
 
 proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
   if src != nil:
-    when declared(newObjRC1):
+    when declared(newObjRC1) and not defined(gcRegions):
       var s = src.len
       if s < 7: s = 7
       result = cast[NimString](newObjRC1(addr(strDesc), sizeof(TGenericSeq) +
@@ -235,7 +242,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
     # we need to decref here, otherwise the GC leaks!
     when not defined(boehmGC) and not defined(nogc) and
          not defined(gcMarkAndSweep) and not defined(gogc) and
-         not defined(gcStack):
+         not defined(gcRegions):
       when false: # compileOption("gc", "v2"):
         for i in newLen..result.len-1:
           let len0 = gch.tempStack.len
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index 49b13576c..a7a811844 100644
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -115,10 +115,6 @@ when defined(windows):
   proc setThreadAffinityMask(hThread: SysThread, dwThreadAffinityMask: uint) {.
     importc: "SetThreadAffinityMask", stdcall, header: "<windows.h>".}
 
-  proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
-    result = int(getCurrentThreadId())
-
 elif defined(genode):
   const
     GenodeHeader = "genode_cpp/threads.h"
@@ -249,48 +245,6 @@ else:
   proc setAffinity(thread: SysThread; setsize: csize; s: var CpuSet) {.
     importc: "pthread_setaffinity_np", header: pthreadh.}
 
-  when defined(linux):
-    proc syscall(arg: clong): clong {.varargs, importc: "syscall", header: "<unistd.h>".}
-    var NR_gettid {.importc: "__NR_gettid", header: "<sys/syscall.h>".}: int
-
-    #type Pid {.importc: "pid_t", header: "<sys/types.h>".} = distinct int
-    #proc gettid(): Pid {.importc, header: "<sys/types.h>".}
-
-    proc getThreadId*(): int =
-      ## get the ID of the currently running thread.
-      result = int(syscall(NR_gettid))
-  elif defined(dragonfly):
-    proc lwp_gettid(): int32 {.importc, header: "unistd.h".}
-
-    proc getThreadId*(): int =
-      result = int(lwp_gettid())
-  elif defined(openbsd):
-    proc getthrid(): int32 {.importc: "getthrid", header: "<unistd.h>".}
-
-    proc getThreadId*(): int =
-      result = int(getthrid())
-  elif defined(netbsd):
-    proc lwp_self(): int32 {.importc: "_lwp_self", header: "<lwp.h>".}
-
-    proc getThreadId*(): int =
-      result = int(lwp_self())
-  elif defined(macosx) or defined(freebsd):
-    proc pthread_threadid_np(y: pointer; x: var uint64): cint {.importc, header: "pthread.h".}
-
-    proc getThreadId*(): int =
-      ## get the ID of the currently running thread.
-      var x: uint64
-      result = pthread_threadid_np(nil, x)
-      result = int(x)
-  elif defined(solaris):
-    # just a guess really:
-    type thread_t {.importc: "thread_t", header: "<thread.h>".} = distinct int
-    proc thr_self(): thread_t {.importc, header: "<thread.h>".}
-
-    proc getThreadId*(): int =
-      ## get the ID of the currently running thread.
-      result = int(thr_self())
-
 const
   emulatedThreadVars = compileOption("tlsEmulation")
 
@@ -302,8 +256,9 @@ when emulatedThreadVars:
 # we preallocate a fixed size for thread local storage, so that no heap
 # allocations are needed. Currently less than 7K are used on a 64bit machine.
 # We use ``float`` for proper alignment:
+const nimTlsSize {.intdefine.} = 8000
 type
-  ThreadLocalStorage = array[0..1_000, float]
+  ThreadLocalStorage = array[0..(nimTlsSize div sizeof(float)), float]
 
   PGcThread = ptr GcThread
   GcThread {.pure, inheritable.} = object
@@ -369,7 +324,11 @@ when not defined(useNimRtl):
 
   when emulatedThreadVars:
     if nimThreadVarsSize() > sizeof(ThreadLocalStorage):
-      echo "too large thread local storage size requested"
+      echo "too large thread local storage size requested ",
+           "(", nimThreadVarsSize(), "/", sizeof(ThreadLocalStorage), "). ",
+           "Use -d:\"nimTlsSize=", nimThreadVarsSize(),
+           "\" to preallocate sufficient storage."
+
       quit 1
 
   when hasSharedHeap and not defined(boehmgc) and not defined(gogc) and not defined(nogc):
@@ -437,7 +396,7 @@ template afterThreadRuns() =
   for i in countdown(threadDestructionHandlers.len-1, 0):
     threadDestructionHandlers[i]()
 
-when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcstack):
+when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions):
   proc deallocOsPages()
 
 when defined(boehmgc):
@@ -475,7 +434,7 @@ else:
 proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) =
   when defined(boehmgc):
     boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd)
-  elif not defined(nogc) and not defined(gogc) and not defined(gcstack):
+  elif not defined(nogc) and not defined(gogc) and not defined(gcRegions):
     var p {.volatile.}: proc(a: ptr Thread[TArg]) {.nimcall.} =
       threadProcWrapDispatch[TArg]
     when not hasSharedHeap:
@@ -493,7 +452,7 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) =
   else:
     threadProcWrapDispatch(thrd)
 
-template threadProcWrapperBody(closure: expr) {.immediate.} =
+template threadProcWrapperBody(closure: untyped): untyped =
   var thrd = cast[ptr Thread[TArg]](closure)
   var core = thrd.core
   when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core)
@@ -665,3 +624,82 @@ when useStackMaskHack:
     var mainThread: Thread[pointer]
     createThread(mainThread, tp)
     joinThread(mainThread)
+
+## we need to cache current threadId to not perform syscall all the time
+var threadId {.threadvar.}: int
+
+when defined(windows):
+  proc getThreadId*(): int =
+    ## get the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(getCurrentThreadId())
+    result = threadId
+
+elif defined(linux):
+  proc syscall(arg: clong): clong {.varargs, importc: "syscall", header: "<unistd.h>".}
+  var NR_gettid {.importc: "__NR_gettid", header: "<sys/syscall.h>".}: int
+
+  proc getThreadId*(): int =
+    ## get the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(syscall(NR_gettid))
+    result = threadId
+
+elif defined(dragonfly):
+  proc lwp_gettid(): int32 {.importc, header: "unistd.h".}
+
+  proc getThreadId*(): int =
+    ## get the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(lwp_gettid())
+    result = threadId
+
+elif defined(openbsd):
+  proc getthrid(): int32 {.importc: "getthrid", header: "<unistd.h>".}
+
+  proc getThreadId*(): int =
+    ## get the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(getthrid())
+    result = threadId
+
+elif defined(netbsd):
+  proc lwp_self(): int32 {.importc: "_lwp_self", header: "<lwp.h>".}
+
+  proc getThreadId*(): int =
+    ## get the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(lwp_self())
+    result = threadId
+
+elif defined(freebsd):
+  proc syscall(arg: cint, arg0: ptr cint): cint {.varargs, importc: "syscall", header: "<unistd.h>".}
+  var SYS_thr_self {.importc:"SYS_thr_self", header:"<sys/syscall.h>"}: cint
+
+  proc getThreadId*(): int =
+    ## get the ID of the currently running thread.
+    var tid = 0.cint
+    if threadId == 0:
+      discard syscall(SYS_thr_self, addr tid)
+      threadId = tid
+    result = threadId
+
+elif defined(macosx):
+  proc syscall(arg: cint): cint {.varargs, importc: "syscall", header: "<unistd.h>".}
+  var SYS_thread_selfid {.importc:"SYS_thread_selfid", header:"<sys/syscall.h>".}: cint
+
+  proc getThreadId*(): int =
+    ## get the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(syscall(SYS_thread_selfid))
+    result = threadId
+
+elif defined(solaris):
+  type thread_t {.importc: "thread_t", header: "<thread.h>".} = distinct int
+  proc thr_self(): thread_t {.importc, header: "<thread.h>".}
+
+  proc getThreadId*(): int =
+    ## get the ID of the currently running thread.
+    if threadId == 0:
+      threadId = int(thr_self())
+    result = threadId
diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim
index dda547abe..a8b28c279 100644
--- a/lib/system/widestrs.nim
+++ b/lib/system/widestrs.nim
@@ -15,7 +15,7 @@ when not declared(NimString):
 
 type
   Utf16Char* = distinct int16
-  WideCString* = ref array[0.. 1_000_000, Utf16Char]
+  WideCString* = ref UncheckedArray[Utf16Char]
 {.deprecated: [TUtf16Char: Utf16Char].}
 
 proc len*(w: WideCString): int =
diff --git a/lib/upcoming/asyncdispatch.nim b/lib/upcoming/asyncdispatch.nim
index 1623d8375..4e3b06173 100644
--- a/lib/upcoming/asyncdispatch.nim
+++ b/lib/upcoming/asyncdispatch.nim
@@ -9,11 +9,13 @@
 
 include "system/inclrtl"
 
-import os, tables, strutils, times, heapqueue, lists, options
+import os, tables, strutils, times, heapqueue, lists, options, asyncstreams
+import asyncfutures except callSoon
 
 import nativesockets, net, deques
 
 export Port, SocketFlag
+export asyncfutures, asyncstreams
 
 #{.injectStmt: newGcInvariant().}
 
@@ -130,8 +132,6 @@ export Port, SocketFlag
 
 # TODO: Check if yielded future is nil and throw a more meaningful exception
 
-include "../includes/asyncfutures"
-
 type
   PDispatcherBase = ref object of RootRef
     timers: HeapQueue[tuple[finishAt: float, fut: Future[void]]]
@@ -161,6 +161,12 @@ proc adjustedTimeout(p: PDispatcherBase, timeout: int): int {.inline.} =
       result = int((timerTimeout - curTime) * 1000)
       if result < 0: result = 0
 
+proc callSoon(cbproc: proc ()) {.gcsafe.}
+
+proc initCallSoonProc =
+  if asyncfutures.getCallSoonProc().isNil:
+    asyncfutures.setCallSoonProc(callSoon)
+
 when defined(windows) or defined(nimdoc):
   import winlean, sets, hashes
   type
@@ -214,15 +220,17 @@ when defined(windows) or defined(nimdoc):
     result.callbacks = initDeque[proc ()](64)
 
   var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
-  proc getGlobalDispatcher*(): PDispatcher =
-    ## Retrieves the global thread-local dispatcher.
-    if gDisp.isNil: gDisp = newDispatcher()
-    result = gDisp
 
   proc setGlobalDispatcher*(disp: PDispatcher) =
     if not gDisp.isNil:
       assert gDisp.callbacks.len == 0
     gDisp = disp
+    initCallSoonProc()
+
+  proc getGlobalDispatcher*(): PDispatcher =
+    if gDisp.isNil:
+      setGlobalDispatcher(newDispatcher())
+    result = gDisp
 
   proc register*(fd: AsyncFD) =
     ## Registers ``fd`` with the dispatcher.
@@ -1081,14 +1089,17 @@ else:
     result.callbacks = initDeque[proc ()](InitDelayedCallbackListSize)
 
   var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
-  proc getGlobalDispatcher*(): PDispatcher =
-    if gDisp.isNil: gDisp = newDispatcher()
-    result = gDisp
 
   proc setGlobalDispatcher*(disp: PDispatcher) =
     if not gDisp.isNil:
       assert gDisp.callbacks.len == 0
     gDisp = disp
+    initCallSoonProc()
+
+  proc getGlobalDispatcher*(): PDispatcher =
+    if gDisp.isNil:
+      setGlobalDispatcher(newDispatcher())
+    result = gDisp
 
   proc register*(fd: AsyncFD) =
     let p = getGlobalDispatcher()
@@ -1601,7 +1612,7 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
       return
     add(result, c)
 
-proc callSoon*(cbproc: proc ()) =
+proc callSoon(cbproc: proc ()) =
   ## Schedule `cbproc` to be called as soon as possible.
   ## The callback is called when control returns to the event loop.
   getGlobalDispatcher().callbacks.addLast(cbproc)
diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim
index ff18fc2c2..40b54b08d 100644
--- a/lib/wrappers/openssl.nim
+++ b/lib/wrappers/openssl.nim
@@ -588,13 +588,13 @@ proc md5*(d: ptr cuchar; n: csize; md: ptr cuchar): ptr cuchar{.importc: "MD5".}
 proc md5_Transform*(c: var MD5_CTX; b: ptr cuchar){.importc: "MD5_Transform".}
 {.pop.}
 
-from strutils import toHex,toLower
+from strutils import toHex, toLowerAscii
 
 proc hexStr (buf:cstring): string =
   # turn md5s output into a nice hex str
   result = newStringOfCap(32)
   for i in 0 .. <16:
-    result.add toHex(buf[i].ord, 2).toLower
+    result.add toHex(buf[i].ord, 2).toLowerAscii
 
 proc md5_File* (file: string): string {.raises: [IOError,Exception].} =
   ## Generate MD5 hash for a file. Result is a 32 character
diff --git a/readme.md b/readme.md
index 5e50bbc41..44570880a 100644
--- a/readme.md
+++ b/readme.md
@@ -84,8 +84,14 @@ within the [doc/koch.rst](doc/koch.rst) file.
 ``bin`` directory to your PATH, you may install Nimble from source by running
 ``koch nimble`` within the root of the cloned repository.
 
+
+## Contributors
+
+This project exists thanks to all the people who contribute. [Read on to find out how to contribute](#contributing).
+<a href="https://github.com/nim-lang/Nim/graphs/contributors"><img src="https://opencollective.com/Nim/contributors.svg?width=890" /></a>
+
 ## Contributing
-[![Contribute to Nim via Gratipay][badge-nim-gratipay]][nim-gratipay]
+[![Backers on Open Collective](https://opencollective.com/nim/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/nim/sponsors/badge.svg)](#sponsors) [![Contribute to Nim via Gratipay][badge-nim-gratipay]][nim-gratipay]
 [![Setup a bounty via Bountysource][badge-nim-bountysource]][nim-bountysource]
 [![Donate Bitcoins][badge-nim-bitcoin]][nim-bitcoin]
 
@@ -128,6 +134,7 @@ be a good starting point for an initial contribution to Nim.
 You can also help with the development of Nim by making donations. Donations can be
 made using:
 
+* [Open Collective](https://opencollective.com/nim)
 * [Gratipay][nim-gratipay]
 * [Bountysource][nim-bountysource]
 * [Bitcoin][nim-bitcoin]
@@ -135,6 +142,31 @@ made using:
 If you have any questions feel free to submit a question on the
 [Nim forum][nim-forum], or via IRC on [the \#nim channel][nim-irc].
 
+
+## Backers
+
+Thank you to all our backers! [[Become a backer](https://opencollective.com/Nim#backer)]
+
+<a href="https://opencollective.com/Nim#backers" target="_blank"><img src="https://opencollective.com/Nim/backers.svg?width=890"></a>
+
+
+## Sponsors
+
+Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/Nim#sponsor)]
+
+<a href="https://opencollective.com/Nim/sponsor/0/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/0/avatar.svg"></a>
+<a href="https://opencollective.com/Nim/sponsor/1/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/1/avatar.svg"></a>
+<a href="https://opencollective.com/Nim/sponsor/2/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/2/avatar.svg"></a>
+<a href="https://opencollective.com/Nim/sponsor/3/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/3/avatar.svg"></a>
+<a href="https://opencollective.com/Nim/sponsor/4/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/4/avatar.svg"></a>
+<a href="https://opencollective.com/Nim/sponsor/5/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/5/avatar.svg"></a>
+<a href="https://opencollective.com/Nim/sponsor/6/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/6/avatar.svg"></a>
+<a href="https://opencollective.com/Nim/sponsor/7/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/7/avatar.svg"></a>
+<a href="https://opencollective.com/Nim/sponsor/8/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/8/avatar.svg"></a>
+<a href="https://opencollective.com/Nim/sponsor/9/website" target="_blank"><img src="https://opencollective.com/Nim/sponsor/9/avatar.svg"></a>
+
+You can also see a list of all our sponsors/backers from various payment services on the [sponsors page](https://nim-lang.org/sponsors.html) of our website.
+
 ## License
 The compiler and the standard library are licensed under the MIT license, except
 for some modules which explicitly state otherwise. As a result you may use any
diff --git a/tests/alias/talias.nim b/tests/alias/talias.nim
index 810ea2095..a8f1b0dd0 100644
--- a/tests/alias/talias.nim
+++ b/tests/alias/talias.nim
@@ -8,17 +8,17 @@ proc isPartOf*[S, T](a: S, b: T): TAnalysisResult {.
   magic: "IsPartOf", noSideEffect.}
   ## not yet exported properly.
 
-template compileTimeAssert(cond: expr) =
+template compileTimeAssert(cond) =
   when not cond:
     {.compile: "is false: " & astToStr(cond).}
 
-template `<|` (a, b: expr) =
+template `<|` (a, b) =
   compileTimeAssert isPartOf(a, b) == arYes
 
-template `!<|` (a, b: expr) =
+template `!<|` (a, b) =
   compileTimeAssert isPartOf(a, b) == arNo
 
-template `?<|` (a, b: expr) =
+template `?<|` (a, b) =
   compileTimeAssert isPartOf(a, b) == arMaybe
 
 type
diff --git a/tests/arithm/tshr.nim b/tests/arithm/tshr.nim
index e9b72f1df..4ba34aed9 100644
--- a/tests/arithm/tshr.nim
+++ b/tests/arithm/tshr.nim
@@ -3,18 +3,18 @@ discard """
 """
 
 proc T() =
-    let VI = -8
-    let VI64 = -8'i64
-    let VI32 = -8'i32
-    let VI16 = -8'i16
-    let VI8 = -8'i8
-    doAssert( (VI shr 1) == 9223372036854775804)
-    doAssert( (VI64 shr 1) == 9223372036854775804)
-    doAssert( (VI32 shr 1) == 2147483644)
-    doAssert( (VI16 shr 1) == 32764)
-    doAssert( (VI8 shr 1) == 124)
+  # let VI = -8
+  let VI64 = -8'i64
+  let VI32 = -8'i32
+  let VI16 = -8'i16
+  let VI8 = -8'i8
+  # doAssert( (VI shr 1) == 9_223_372_036_854_775_804, "Actual: " & $(VI shr 1))
+  doAssert( (VI64 shr 1) == 9_223_372_036_854_775_804, "Actual: " & $(VI64 shr 1))
+  doAssert( (VI32 shr 1) == 2_147_483_644, "Actual: " & $(VI32 shr 1))
+  doAssert( (VI16 shr 1) == 32_764, "Actual: " & $(VI16 shr 1))
+  doAssert( (VI8 shr 1) == 124, "Actual: " & $(VI8 shr 1))
 
 
 T()
 static:
-    T()
+  T()
diff --git a/tests/array/tunchecked.nim b/tests/array/tunchecked.nim
new file mode 100644
index 000000000..f5ac3642d
--- /dev/null
+++ b/tests/array/tunchecked.nim
@@ -0,0 +1,5 @@
+{.boundchecks: on.}
+type Unchecked {.unchecked.} = array[0, char]
+
+var x = cast[ptr Unchecked](alloc(100))
+x[5] = 'x'
diff --git a/tests/assert/tuserassert.nim b/tests/assert/tuserassert.nim
index 7bb0a7fc0..274c78921 100644
--- a/tests/assert/tuserassert.nim
+++ b/tests/assert/tuserassert.nim
@@ -2,7 +2,7 @@ discard """
   output: "x == 45ugh"
 """
 
-template myAssert(cond: expr) =
+template myAssert(cond: untyped) =
   when 3 <= 3:
     let c = cond.astToStr
     if not cond:
diff --git a/tests/async/tasync_gcsafe.nim b/tests/async/tasync_gcsafe.nim
new file mode 100644
index 000000000..89df6456a
--- /dev/null
+++ b/tests/async/tasync_gcsafe.nim
@@ -0,0 +1,36 @@
+discard """
+  cmd: "nim c --threads:on $file"
+  output: '''
+1
+2
+3
+'''
+"""
+
+assert compileOption("threads"), "this test will not do anything useful without --threads:on"
+
+import asyncdispatch
+
+var globalDummy: ref int
+proc gcUnsafeProc() =
+    if not globalDummy.isNil:
+        echo globalDummy[]
+    echo "1"
+
+proc gcSafeAsyncProcWithNoAnnotation() {.async.} =
+    echo "2"
+
+proc gcSafeAsyncProcWithAnnotation() {.gcsafe, async.} =
+    echo "3"
+
+proc gcUnsafeAsyncProc() {.async.} =
+    # We should be able to call gcUnsafe
+    gcUnsafeProc()
+
+    # We should be able to call async implicitly gcsafe
+    await gcSafeAsyncProcWithNoAnnotation()
+
+    # We should be able to call async explicitly gcsafe
+    await gcSafeAsyncProcWithAnnotation()
+
+waitFor gcUnsafeAsyncProc()
diff --git a/tests/async/tasync_gcunsafe.nim b/tests/async/tasync_gcunsafe.nim
new file mode 100644
index 000000000..f4e2cdcf2
--- /dev/null
+++ b/tests/async/tasync_gcunsafe.nim
@@ -0,0 +1,30 @@
+discard """
+  cmd: "nim c --threads:on $file"
+  file: "asyncmacro.nim"
+  errormsg: "'anotherGCSafeAsyncProcIter' is not GC-safe as it calls 'asyncGCUnsafeProc'"
+"""
+
+assert compileOption("threads"), "this test will not do anything useful without --threads:on"
+
+import asyncdispatch
+
+var globalDummy: ref int
+proc gcUnsafeProc() =
+    if not globalDummy.isNil:
+        echo globalDummy[]
+
+proc asyncExplicitlyGCSafeProc() {.gcsafe, async.} =
+    echo "hi"
+
+proc asyncImplicitlyGCSafeProc() {.async.} =
+    echo "hi"
+
+proc asyncGCUnsafeProc() {.async.} =
+    gcUnsafeProc()
+
+proc anotherGCSafeAsyncProc() {.async, gcsafe.} =
+    # We should be able to call other gcsafe procs
+    await asyncExplicitlyGCSafeProc()
+    await asyncImplicitlyGCSafeProc()
+    # But we can't call gcunsafe procs
+    await asyncGCUnsafeProc()
diff --git a/tests/async/tasyncrecursion.nim b/tests/async/tasyncrecursion.nim
index 54482edab..1aeebe9b4 100644
--- a/tests/async/tasyncrecursion.nim
+++ b/tests/async/tasyncrecursion.nim
@@ -17,5 +17,6 @@ proc asyncRecursionTest*(): Future[int] {.async.} =
     inc(i)
 
 when isMainModule:
+  setGlobalDispatcher(newDispatcher())
   var i = waitFor asyncRecursionTest()
   echo i
diff --git a/tests/async/tcallbacks.nim b/tests/async/tcallbacks.nim
new file mode 100644
index 000000000..8c08032cd
--- /dev/null
+++ b/tests/async/tcallbacks.nim
@@ -0,0 +1,20 @@
+discard """
+  exitcode: 0
+  output: '''3
+2
+1
+5
+'''
+"""
+import asyncfutures
+
+let f1: Future[int] = newFuture[int]()
+f1.addCallback(proc() = echo 1)
+f1.addCallback(proc() = echo 2)
+f1.addCallback(proc() = echo 3)
+f1.complete(10)
+
+let f2: Future[int] = newFuture[int]()
+f2.addCallback(proc() = echo 4)
+f2.callback = proc() = echo 5
+f2.complete(10)
diff --git a/tests/bind/mbind3.nim b/tests/bind/mbind3.nim
index aad080223..1a7d3b63b 100644
--- a/tests/bind/mbind3.nim
+++ b/tests/bind/mbind3.nim
@@ -2,7 +2,7 @@
 var
   lastId = 0
 
-template genId*: expr =
+template genId*: int =
   bind lastId
   inc(lastId)
   lastId
diff --git a/tests/bind/tbind1.nim b/tests/bind/tbind1.nim
index 2665d0b5c..9b13a7d11 100644
--- a/tests/bind/tbind1.nim
+++ b/tests/bind/tbind1.nim
@@ -6,7 +6,7 @@ discard """
 
 proc p1(x: int8, y: int): int = return x + y
 
-template tempBind(x, y: expr): expr =
+template tempBind(x, y): untyped =
   bind p1
   p1(x, y)
 
diff --git a/tests/bind/tbind2.nim b/tests/bind/tbind2.nim
index 0e0cbd788..799b14381 100644
--- a/tests/bind/tbind2.nim
+++ b/tests/bind/tbind2.nim
@@ -8,7 +8,7 @@ discard """
 proc p1(x: int8, y: int): int = return x + y
 proc p1(x: int, y: int8): int = return x - y
 
-template tempBind(x, y: expr): expr =
+template tempBind(x, y): untyped =
   (bind p1(x, y))  #ERROR_MSG ambiguous call
 
 echo tempBind(1'i8, 2'i8)
diff --git a/tests/ccgbugs/t6279.nim b/tests/ccgbugs/t6279.nim
new file mode 100644
index 000000000..37345d807
--- /dev/null
+++ b/tests/ccgbugs/t6279.nim
@@ -0,0 +1,9 @@
+discard """
+cmd: "nim c -r -d:fulldebug -d:smokeCycles --gc:refc $file"
+output: '''@[a]'''
+"""
+
+# bug #6279
+var foo = newSeq[tuple[a: seq[string], b: seq[string]]]()
+foo.add((@["a"], @["b"]))
+echo foo[0].a # Crashes on this line
diff --git a/tests/ccgbugs/taddhigh.nim b/tests/ccgbugs/taddhigh.nim
index d6ac8f650..549eb8caa 100644
--- a/tests/ccgbugs/taddhigh.nim
+++ b/tests/ccgbugs/taddhigh.nim
@@ -1,5 +1,5 @@
 discard """
-  output: '''@[5, 5, 5]'''
+  output: '''@[5, 5, 5, 5, 5]'''
 """
 
 # bug #1832
@@ -13,4 +13,7 @@ s.add x
 # Causes the 0 to appear:
 s.add s[s.high]
 
+s.add s[s.len-1]
+s.add s[s.xlen-1]
+
 echo s # @[5, 5, 0]
diff --git a/tests/ccgbugs/tnocodegen_for_compiletime.nim b/tests/ccgbugs/tnocodegen_for_compiletime.nim
index a88ba4b32..b44e9f8c9 100644
--- a/tests/ccgbugs/tnocodegen_for_compiletime.nim
+++ b/tests/ccgbugs/tnocodegen_for_compiletime.nim
@@ -1,7 +1,7 @@
 # bug #1679
 import macros, tables, hashes
 proc hash(v: NimNode): Hash = 4  # performance is for suckers
-macro test(body: stmt): stmt {.immediate.} =
+macro test(body: untyped): typed =
   var a = initCountTable[NimNode]()
   a.inc(body)
 
diff --git a/tests/ccgbugs/tuple_canon.nim b/tests/ccgbugs/tuple_canon.nim
index a607f9cab..45fed8eee 100644
--- a/tests/ccgbugs/tuple_canon.nim
+++ b/tests/ccgbugs/tuple_canon.nim
@@ -59,7 +59,7 @@ let
             ((1,1), hiA),
             ((0,1), hiB)]
 
-template odd*(i: int) : expr =
+template odd*(i: int) : untyped =
     (i and 1) != 0
 
 proc vidx(hg: HexGrid; col, row: int; i: HexVtxIndex) : Index =
diff --git a/tests/ccgbugs/twrongrefcounting.nim b/tests/ccgbugs/twrongrefcounting.nim
new file mode 100644
index 000000000..c0c3e0fc2
--- /dev/null
+++ b/tests/ccgbugs/twrongrefcounting.nim
@@ -0,0 +1,22 @@
+discard """
+  output: '''ok'''
+  cmd: "nim c -r --gc:refc -d:useGcAssert -d:useSysAssert -d:fulldebug -d:smokeCycles $file"
+"""
+# bug #6234
+type
+    Foo = ref object
+        s: seq[Bar]
+    Bar = ref object
+        f: Foo
+
+proc test() =
+    var f = Foo.new()
+    for i in 0 .. 5:
+        f.s = @[]
+        for j in 0 .. 5:
+            var b = Bar.new()
+            b.f = f
+            f.s.add(b)
+
+test()
+echo "ok"
diff --git a/tests/closure/tboehmdeepcopy.nim b/tests/closure/tboehmdeepcopy.nim
new file mode 100644
index 000000000..7c937ca10
--- /dev/null
+++ b/tests/closure/tboehmdeepcopy.nim
@@ -0,0 +1,18 @@
+discard """
+  cmd: "nim c --gc:boehm $options $file"
+  output: '''meep'''
+  disabled: "windows"
+"""
+
+proc callit(it: proc ()) =
+  it()
+
+proc main =
+  var outer = "meep"
+  proc x =
+    echo outer
+  var y: proc()
+  deepCopy(y, x)
+  callit(y)
+
+main()
diff --git a/tests/closure/tdonotation.nim b/tests/closure/tdonotation.nim
index 94eba8ddb..cc4f46bab 100644
--- a/tests/closure/tdonotation.nim
+++ b/tests/closure/tdonotation.nim
@@ -5,7 +5,9 @@ lost focus 1
 lost focus 2
 registered handler for UserEvent 1
 registered handler for UserEvent 2
-registered handler for UserEvent 3'''
+registered handler for UserEvent 3
+registered handler for UserEvent 4
+'''
 """
 
 import future
@@ -35,11 +37,14 @@ b.onFocusLost:
 b.onFocusLost do:
   echo "lost focus 2"
 
-b.onUserEvent "UserEvent 1" do:
+b.onUserEvent("UserEvent 1") do:
   discard
 
 b.onUserEvent "UserEvent 2":
   discard
 
-b.onUserEvent("UserEvent 3", () => echo "event 3")
+b.onUserEvent("UserEvent 3"):
+  discard
+
+b.onUserEvent("UserEvent 4", () => echo "event 4")
 
diff --git a/tests/closure/tmacrobust1512.nim b/tests/closure/tmacrobust1512.nim
index 95681e750..5f13e8286 100644
--- a/tests/closure/tmacrobust1512.nim
+++ b/tests/closure/tmacrobust1512.nim
@@ -2,109 +2,109 @@ import macros, strutils
 
 # https://github.com/nim-lang/Nim/issues/1512
 
-proc macrobust0 (raw_input: string) =
+proc macrobust0(raw_input: string) =
   var output = ""
-  proc p1 (a:string) =
-    output.add (a)
-
-  proc p2 (a:string) = p1 (a)
-  proc p3 (a:string) = p2 (a)
-  proc p4 (a:string) = p3 (a)
-  proc p5 (a:string) = p4 (a)
-  proc p6 (a:string) = p5 (a)
-  proc p7 (a:string) = p6 (a)
-  proc p8 (a:string) = p7 (a)
-  proc p9 (a:string) = p8 (a)
-  proc p10 (a:string) = p9 (a)
-  proc p11 (a:string) = p10 (a)
-  proc p12 (a:string) = p11 (a)
-  proc p13 (a:string) = p12 (a)
-  proc p14 (a:string) = p13 (a)
-  proc p15 (a:string) = p14 (a)
-  proc p16 (a:string) = p15 (a)
-  proc p17 (a:string) = p16 (a)
-  proc p18 (a:string) = p17 (a)
-  proc p19 (a:string) = p18 (a)
-  proc p20 (a:string) = p19 (a)
+  proc p1(a:string) =
+    output.add(a)
+
+  proc p2(a:string) = p1(a)
+  proc p3(a:string) = p2(a)
+  proc p4(a:string) = p3(a)
+  proc p5(a:string) = p4(a)
+  proc p6(a:string) = p5(a)
+  proc p7(a:string) = p6(a)
+  proc p8(a:string) = p7(a)
+  proc p9(a:string) = p8(a)
+  proc p10(a:string) = p9(a)
+  proc p11(a:string) = p10(a)
+  proc p12(a:string) = p11(a)
+  proc p13(a:string) = p12(a)
+  proc p14(a:string) = p13(a)
+  proc p15(a:string) = p14(a)
+  proc p16(a:string) = p15(a)
+  proc p17(a:string) = p16(a)
+  proc p18(a:string) = p17(a)
+  proc p19(a:string) = p18(a)
+  proc p20(a:string) = p19(a)
 
   let input = $raw_input
 
-  for a in input.split ():
-    p20 (a)
-    p19 (a)
-
-
-    p18 (a)
-    p17 (a)
-    p16 (a)
-    p15 (a)
-    p14 (a)
-    p13 (a)
-    p12 (a)
-    p11 (a)
-    p10 (a)
-    p9 (a)
-    p8 (a)
-    p7 (a)
-    p6 (a)
-    p5 (a)
-    p4 (a)
-    p3 (a)
-    p2 (a)
-    p1 (a)
+  for a in input.split():
+    p20(a)
+    p19(a)
+
+
+    p18(a)
+    p17(a)
+    p16(a)
+    p15(a)
+    p14(a)
+    p13(a)
+    p12(a)
+    p11(a)
+    p10(a)
+    p9(a)
+    p8(a)
+    p7(a)
+    p6(a)
+    p5(a)
+    p4(a)
+    p3(a)
+    p2(a)
+    p1(a)
 
 
   echo output
 
-macro macrobust (raw_input: expr) : stmt =
+macro macrobust(raw_input: untyped): untyped =
 
   var output = ""
-  proc p1 (a:string) =
-    output.add (a)
-
-  proc p2 (a:string) = p1 (a)
-  proc p3 (a:string) = p2 (a)
-  proc p4 (a:string) = p3 (a)
-  proc p5 (a:string) = p4 (a)
-  proc p6 (a:string) = p5 (a)
-  proc p7 (a:string) = p6 (a)
-  proc p8 (a:string) = p7 (a)
-  proc p9 (a:string) = p8 (a)
-  proc p10 (a:string) = p9 (a)
-  proc p11 (a:string) = p10 (a)
-  proc p12 (a:string) = p11 (a)
-  proc p13 (a:string) = p12 (a)
-  proc p14 (a:string) = p13 (a)
-  proc p15 (a:string) = p14 (a)
-  proc p16 (a:string) = p15 (a)
-  proc p17 (a:string) = p16 (a)
-  proc p18 (a:string) = p17 (a)
-  proc p19 (a:string) = p18 (a)
-  proc p20 (a:string) = p19 (a)
+  proc p1(a:string) =
+    output.add(a)
+
+  proc p2(a:string) = p1(a)
+  proc p3(a:string) = p2(a)
+  proc p4(a:string) = p3(a)
+  proc p5(a:string) = p4(a)
+  proc p6(a:string) = p5(a)
+  proc p7(a:string) = p6(a)
+  proc p8(a:string) = p7(a)
+  proc p9(a:string) = p8(a)
+  proc p10(a:string) = p9(a)
+  proc p11(a:string) = p10(a)
+  proc p12(a:string) = p11(a)
+  proc p13(a:string) = p12(a)
+  proc p14(a:string) = p13(a)
+  proc p15(a:string) = p14(a)
+  proc p16(a:string) = p15(a)
+  proc p17(a:string) = p16(a)
+  proc p18(a:string) = p17(a)
+  proc p19(a:string) = p18(a)
+  proc p20(a:string) = p19(a)
 
   let input = $raw_input
 
-  for a in input.split ():
-    p20 (a)
-    p19 (a)
-
-    p18 (a)
-    p17 (a)
-    p16 (a)
-    p15 (a)
-    p14 (a)
-    p13 (a)
-    p12 (a)
-    p11 (a)
-    p10 (a)
-    p9 (a)
-    p8 (a)
-    p7 (a)
-    p6 (a)
-    p5 (a)
-    p4 (a)
-    p3 (a)
-    p2 (a)
+  for a in input.split():
+    p20(a)
+    p19(a)
+
+    p18(a)
+    p17(a)
+    p16(a)
+    p15(a)
+    p14(a)
+    p13(a)
+    p12(a)
+    p11(a)
+    p10(a)
+    p9(a)
+    p8(a)
+    p7(a)
+    p6(a)
+    p5(a)
+    p4(a)
+    p3(a)
+    p2(a)
 
   echo output
   discard result
@@ -134,4 +134,4 @@ macrobust0 """
   sdafsdaffsda sdfasadfsadf
   fsdasdafsdfa sdfasdfafsda
   sdfasdafsadf sdfasdafsdaf sdfasdafsdaf
-"""
\ No newline at end of file
+"""
diff --git a/tests/collections/ttableconstr.nim b/tests/collections/ttableconstr.nim
index a9262e70e..884378a76 100644
--- a/tests/collections/ttableconstr.nim
+++ b/tests/collections/ttableconstr.nim
@@ -1,6 +1,6 @@
 # Test if the new table constructor syntax works:
 
-template ignoreExpr(e: expr): stmt {.immediate.} =
+template ignoreExpr(e) =
   discard
 
 # test first class '..' syntactical citizen:
diff --git a/tests/compiles/tcompiles.nim b/tests/compiles/tcompiles.nim
index b3d9c17ce..1a21315c8 100644
--- a/tests/compiles/tcompiles.nim
+++ b/tests/compiles/tcompiles.nim
@@ -1,13 +1,15 @@
 # test the new 'compiles' feature:
 
-template supports(opr, x: expr): bool {.immediate.} =
+template supports(opr, x: untyped): bool =
   compiles(opr(x)) or compiles(opr(x, x))
 
-template ok(x: expr): stmt =
-  static: assert(x)
+template ok(x) =
+  static:
+    assert(x)
 
-template no(x: expr): stmt =
-  static: assert(not x)
+template no(x) =
+  static:
+    assert(not x)
 
 type
   TObj = object
diff --git a/tests/concepts/matrix.nim b/tests/concepts/matrix.nim
index a30cbe4f3..47a709f80 100644
--- a/tests/concepts/matrix.nim
+++ b/tests/concepts/matrix.nim
@@ -9,7 +9,6 @@ proc `[]=`*(M: var Matrix; m, n: int; v: M.T) =
   M.data[m * M.N + n] = v
 
 # Adapt the Matrix type to the concept's requirements
-template Rows*(M: type Matrix): expr = M.M
-template Cols*(M: type Matrix): expr = M.N
+template Rows*(M: type Matrix): untyped = M.M
+template Cols*(M: type Matrix): untyped = M.N
 template ValueType*(M: type Matrix): typedesc = M.T
-
diff --git a/tests/concepts/t3330.nim b/tests/concepts/t3330.nim
index 04add2b6f..fcd5054ef 100644
--- a/tests/concepts/t3330.nim
+++ b/tests/concepts/t3330.nim
@@ -2,16 +2,16 @@ discard """
 errormsg: "type mismatch: got (Bar[system.int])"
 nimout: '''
 t3330.nim(40, 4) Error: type mismatch: got (Bar[system.int])
-but expected one of: 
+but expected one of:
 proc test(foo: Foo[int])
 t3330.nim(25, 8) Hint: Non-matching candidates for add(k, string, T)
-proc add[T](x: var seq[T]; y: T)
-proc add(result: var string; x: float)
 proc add(x: var string; y: string)
-proc add(x: var string; y: cstring)
 proc add(x: var string; y: char)
 proc add(result: var string; x: int64)
+proc add(x: var string; y: cstring)
+proc add(result: var string; x: float)
 proc add[T](x: var seq[T]; y: openArray[T])
+proc add[T](x: var seq[T]; y: T)
 
 t3330.nim(25, 8) template/generic instantiation from here
 t3330.nim(32, 6) Foo: 'bar.value' cannot be assigned to
diff --git a/tests/concepts/tmanual.nim b/tests/concepts/tmanual.nim
index 43290a6ad..c917f5022 100644
--- a/tests/concepts/tmanual.nim
+++ b/tests/concepts/tmanual.nim
@@ -14,10 +14,10 @@ t
 '''
 """
 
-template accept(e: expr) =
+template accept(e) =
   static: assert compiles(e)
 
-template reject(e: expr) =
+template reject(e) =
   static: assert(not compiles(e))
 
 type
@@ -40,4 +40,3 @@ takesContainer(@[4, 5, 6])
 takesContainer(@["a", "b"])
 takesContainer "test"
 reject takesContainer(10)
-
diff --git a/tests/concepts/tmodifiersinplace.nim b/tests/concepts/tmodifiersinplace.nim
new file mode 100644
index 000000000..db5583929
--- /dev/null
+++ b/tests/concepts/tmodifiersinplace.nim
@@ -0,0 +1,30 @@
+type
+  VarContainer[T] = concept c
+    put(var c, T)
+
+  AltVarContainer[T] = concept var c
+    put(c, T)
+
+  NonVarContainer[T] = concept c
+    put(c, T)
+
+  GoodContainer = object
+    x: int
+
+  BadContainer = object
+    x: int
+
+proc put(x: BadContainer, y: int) = discard
+proc put(x: var GoodContainer, y: int) = discard
+
+template ok(x) = assert(x)
+template no(x) = assert(not(x))
+
+static:
+  ok GoodContainer is VarContainer[int]
+  ok GoodContainer is AltVarContainer[int]
+  no BadContainer is VarContainer[int]
+  no BadContainer is AltVarContainer[int]
+  ok GoodContainer is NonVarContainer[int]
+  ok BadContainer is NonVarContainer[int]
+
diff --git a/tests/concepts/tstackconcept.nim b/tests/concepts/tstackconcept.nim
index b6ead2c2b..2238dacb6 100644
--- a/tests/concepts/tstackconcept.nim
+++ b/tests/concepts/tstackconcept.nim
@@ -12,7 +12,7 @@ IMPLICIT VALUE TYPE NAME INT INT
 
 import typetraits, strutils
 
-template reject(e: expr) =
+template reject(e) =
   static: assert(not compiles(e))
 
 type
@@ -60,4 +60,3 @@ reject s.genericAlgorithm "x"
 reject s.genericAlgorithm 1.0
 reject "str".implicitGeneric
 reject implicitGeneric(10)
-
diff --git a/tests/constructors/t5965_1.nim b/tests/constructors/t5965_1.nim
new file mode 100644
index 000000000..9f947f859
--- /dev/null
+++ b/tests/constructors/t5965_1.nim
@@ -0,0 +1,10 @@
+discard """
+  file: "t5965_1.nim"
+  line: 10
+  errormsg: "incorrect object construction syntax"
+"""
+
+type Foo = object
+  a, b, c: int
+
+discard Foo(a: 1, 2)
diff --git a/tests/constructors/t5965_2.nim b/tests/constructors/t5965_2.nim
new file mode 100644
index 000000000..a3f7174c9
--- /dev/null
+++ b/tests/constructors/t5965_2.nim
@@ -0,0 +1,10 @@
+discard """
+  file: "t5965_2.nim"
+  line: 10
+  errormsg: "incorrect object construction syntax"
+"""
+
+type Foo = object
+  a: int
+
+discard Foo(a: 1, 2)
diff --git a/tests/cpp/ttypeinfo.nim b/tests/cpp/ttypeinfo.nim
index 1529c86e9..282c682b2 100644
--- a/tests/cpp/ttypeinfo.nim
+++ b/tests/cpp/ttypeinfo.nim
@@ -1,5 +1,22 @@
 discard """
+  output: '''100'''
   cmd: "nim cpp $file"
 """
 
 import typeinfo
+
+#bug #6016
+type
+  Onion {.union.} = object
+    field1: int
+    field2: uint64
+
+  Stroom  = Onion
+
+  PStroom = ptr Stroom
+
+proc pstruct(u: PStroom) =
+  echo u.field2
+
+var x = Onion(field1: 100)
+pstruct(x.addr)
\ No newline at end of file
diff --git a/tests/discard/tdiscardable.nim b/tests/discard/tdiscardable.nim
index 99adcfd30..662d2725a 100644
--- a/tests/discard/tdiscardable.nim
+++ b/tests/discard/tdiscardable.nim
@@ -13,7 +13,7 @@ q[float](0.8, 0.2)
 
 # bug #942
 
-template maybeMod(x: Tinteger, module:Natural):expr =
+template maybeMod(x: Tinteger, module:Natural): untyped =
   if module > 0: x mod module
   else: x
 
diff --git a/tests/exception/tdefer1.nim b/tests/exception/tdefer1.nim
index cb3d09b01..b84ba7681 100644
--- a/tests/exception/tdefer1.nim
+++ b/tests/exception/tdefer1.nim
@@ -10,7 +10,7 @@ A'''
 
 # bug #1742
 
-template test(): expr =
+template test(): untyped =
     let a = 0
     defer: echo "hi"
     a
@@ -29,7 +29,7 @@ template atFuncEnd =
   defer:
     echo "B"
 
-template testB(): expr =
+template testB(): untyped =
     let a = 0
     defer: echo "hi" # Delete this line to make it work
     a
diff --git a/tests/exception/tfinally3.nim b/tests/exception/tfinally3.nim
index 037ca9553..6098672a2 100644
--- a/tests/exception/tfinally3.nim
+++ b/tests/exception/tfinally3.nim
@@ -1,10 +1,7 @@
 discard """
   file: "tfinally3.nim"
-  output: '''false
-Within finally->try
-Traceback (most recent call last)
-tfinally3.nim(24)        tfinally3
-Error: unhandled exception: First [Exception]'''
+  outputsub: '''false
+Within finally->try'''
   exitCode: 1
 """
 # Test break in try statement:
diff --git a/tests/fields/tfields_in_template.nim b/tests/fields/tfields_in_template.nim
index 9352a7a51..b7d5d2343 100644
--- a/tests/fields/tfields_in_template.nim
+++ b/tests/fields/tfields_in_template.nim
@@ -9,7 +9,7 @@ for name, value in (n: "v").fieldPairs:
   echo name
 
 # This doesn't compile - "expression 'name' has no type (or is ambiguous)".
-template wrapper: stmt =
+template wrapper: typed =
   for name, value in (n: "v").fieldPairs:
     echo name
 wrapper()
diff --git a/tests/float/tfloat7.nim b/tests/float/tfloat7.nim
index 2337d1dd4..5fd0d43d9 100644
--- a/tests/float/tfloat7.nim
+++ b/tests/float/tfloat7.nim
@@ -10,7 +10,7 @@ passed.'''
 """
 
 import strutils
-template expect_fail(x: expr) =
+template expect_fail(x) =
   try:
     discard x
     echo("expected to fail!")
diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim
index 0dbd5b03c..d014eb998 100644
--- a/tests/generics/tgeneric3.nim
+++ b/tests/generics/tgeneric3.nim
@@ -66,7 +66,7 @@ proc setItem[T,D](Akey: T, Avalue: D, ANode: PNode[T,D]): ref TItem[T,D] {.inlin
 proc cmp[T:int8|int16|int32|int64|int] (a,b: T): T {.inline.} =
   return a-b
 
-template binSearchImpl *(docmp: expr) {.immediate.} =
+template binSearchImpl *(docmp: untyped) =
   var bFound = false
   result = 0
   var H = haystack.len - 1
diff --git a/tests/generics/tgenerictmpl.nim b/tests/generics/tgenerictmpl.nim
index c71ce4e2e..e18f020c2 100644
--- a/tests/generics/tgenerictmpl.nim
+++ b/tests/generics/tgenerictmpl.nim
@@ -5,7 +5,7 @@ discard """
 
 # bug #3498
 
-template defaultOf[T](t: T): expr = (var d: T; d)
+template defaultOf[T](t: T): untyped = (var d: T; d)
 
 echo defaultOf(1) #<- excpected 0
 
diff --git a/tests/generics/tgenerictmpl2.nim b/tests/generics/tgenerictmpl2.nim
index 0ecaf9ded..ac92d3281 100644
--- a/tests/generics/tgenerictmpl2.nim
+++ b/tests/generics/tgenerictmpl2.nim
@@ -27,5 +27,5 @@ ttmpl[int]    #<- crash case #3
 
 # but still allow normal use of [] on non-generic templates
 
-template tarr: expr = [1, 2, 3, 4]
+template tarr: untyped = [1, 2, 3, 4]
 echo tarr[1]
diff --git a/tests/generics/tunique_type.nim b/tests/generics/tunique_type.nim
index da2f9e4b2..ccb367ac8 100644
--- a/tests/generics/tunique_type.nim
+++ b/tests/generics/tunique_type.nim
@@ -36,7 +36,7 @@ proc derefExpr(exprRef: string): NimNode {.compileTime.} =
 type Mapped[Input; predicate: static[string]] = object
   input: Input
 
-macro map(input, predicate: expr): expr =
+macro map(input, predicate: untyped): untyped =
   let predicate = callsite()[2]
   newNimNode(nnkObjConstr).add(
     newNimNode(nnkBracketExpr).add(
@@ -47,7 +47,7 @@ macro map(input, predicate: expr): expr =
       ident"input", input))
 
 proc `[]`(m: Mapped, i: int): auto =
-  macro buildResult: expr =
+  macro buildResult: untyped =
     newCall(
       derefExpr(m.predicate),
       newNimNode(nnkBracketExpr).add(
diff --git a/tests/gensym/tgensym.nim b/tests/gensym/tgensym.nim
index 3c85b0b83..e33a2783f 100644
--- a/tests/gensym/tgensym.nim
+++ b/tests/gensym/tgensym.nim
@@ -2,7 +2,7 @@ discard """
   output: "123100"
 """
 
-template hygienic(val: expr) =
+template hygienic(val) =
   var x = val
   stdout.write x
 
diff --git a/tests/gensym/tgensymgeneric.nim b/tests/gensym/tgensymgeneric.nim
index 54390a4ef..9963ba808 100644
--- a/tests/gensym/tgensymgeneric.nim
+++ b/tests/gensym/tgensymgeneric.nim
@@ -1,3 +1,7 @@
+discard """
+  output: '''true'''
+"""
+
 # We need to open the gensym'ed symbol again so that the instantiation
 # creates a fresh copy; but this is wrong the very first reason for gensym
 # is that scope rules cannot be used! So simply removing 'sfGenSym' does
@@ -28,4 +32,23 @@ var
 let x = gen(a)
 let y = gen(b)
 
-echo x.x, " ", y.x
+doAssert x.x == 123
+doAssert y.x == "abc"
+
+# test symbol equality
+
+import macros
+
+static:
+  let sym1   = genSym()
+  let sym2   = genSym()
+  let sym3   = sym1
+  let nimsym = sym1.symbol
+  doAssert sym1        == sym1
+  doAssert sym2        != sym3
+  doAssert sym2.symbol != sym3.symbol
+  doAssert sym3        == sym1
+  doAssert sym1.symbol == sym1.symbol
+  doAssert nimsym      == nimsym
+
+echo "true"
diff --git a/tests/iter/tclosureiters.nim b/tests/iter/tclosureiters.nim
index 0eb624a8c..37313d4d7 100644
--- a/tests/iter/tclosureiters.nim
+++ b/tests/iter/tclosureiters.nim
@@ -18,7 +18,8 @@ discard """
 0
 0
 1
-2'''
+2
+70'''
 """
 
 when true:
@@ -71,3 +72,10 @@ for x in infinite.take(3):
 let inf = infinite
 for x in inf.take(3):
   echo x
+
+# bug #3583
+proc foo(f: (iterator(): int)) =
+  for i in f(): echo i
+
+let fIt = iterator(): int = yield 70
+foo fIt
diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim
index e4aad4b99..325ab6366 100644
--- a/tests/js/tjsffi.nim
+++ b/tests/js/tjsffi.nim
@@ -309,11 +309,11 @@ block:
   on("click") do (e: Event):
     console.log e
 
-  jslib.on "reloaded" do:
+  jslib.on("reloaded") do:
     console.log jsarguments[0]
 
   # this test case is different from the above, because
   # `subscribe` is not overloaded in the current scope
-  jslib.subscribe "updates":
+  jslib.subscribe("updates"):
     console.log jsarguments[0]
 
diff --git a/tests/js/ttryexceptnewsyntax.nim b/tests/js/ttryexceptnewsyntax.nim
new file mode 100644
index 000000000..2573c3727
--- /dev/null
+++ b/tests/js/ttryexceptnewsyntax.nim
@@ -0,0 +1,13 @@
+discard """
+  output: '''hello'''
+"""
+
+type
+  MyException = ref Exception
+
+#bug #5986
+
+try:
+  raise MyException(msg: "hello")
+except MyException as e:
+  echo e.msg
diff --git a/tests/lexer/tind1.nim b/tests/lexer/tind1.nim
index 6a975d5be..8a2aea9b2 100644
--- a/tests/lexer/tind1.nim
+++ b/tests/lexer/tind1.nim
@@ -11,17 +11,15 @@ var x = if 4 != 5:
   else:
     "no"
 
-macro mymacro(n: expr): stmt {.immediate.} = nil
+macro mymacro(n): untyped {.immediate.} =
+  discard
 
 mymacro:
   echo "test"
 else:
   echo "else part"
 
-
 if 4 == 3:
   echo "bug"
   else:
   echo "no bug"
-
-
diff --git a/tests/macros/tbindsym.nim b/tests/macros/tbindsym.nim
index e1e3b5112..6289d3eb2 100644
--- a/tests/macros/tbindsym.nim
+++ b/tests/macros/tbindsym.nim
@@ -11,7 +11,7 @@ type
   TTextKind = enum
     TFoo, TBar
 
-macro test: stmt =
+macro test: untyped =
   var x = @[TFoo, TBar]
   result = newStmtList()
   for i in x:
diff --git a/tests/macros/tbugs.nim b/tests/macros/tbugs.nim
index 1bfce6bc4..990edf1e2 100644
--- a/tests/macros/tbugs.nim
+++ b/tests/macros/tbugs.nim
@@ -26,7 +26,7 @@ iterator test2(f: string): Foo =
   for i in f:
     yield Foo(s: i)
 
-macro test(): stmt =
+macro test(): untyped =
   for i in test2("asdf"):
     echo i.s
 
@@ -39,7 +39,7 @@ import macros
 
 type TType = tuple[s: string]
 
-macro echotest(): stmt =
+macro echotest(): untyped =
   var t: TType
   t.s = ""
   t.s.add("test")
@@ -61,7 +61,7 @@ proc get_data(d: Td) : string {.compileTime.} =
     #result.add("aa")       # B
     #result = result & "aa" # C
 
-macro m(s:static[Td]) : stmt =
+macro m(s:static[Td]) : untyped =
     echo get_data(s)
     echo get_data(s)
     result = newEmptyNode()
@@ -77,7 +77,7 @@ proc nilcheck(): NimNode {.compileTime.} =
   echo(result.isNil) # true
   echo(repr(result)) # nil
 
-macro testnilcheck(): stmt =
+macro testnilcheck(): untyped =
   result = newNimNode(nnkStmtList)
   discard nilcheck()
 
@@ -95,10 +95,10 @@ echo c[0]
 
 # bug #3046
 
-macro sampleMacroInt(i: int): stmt =
+macro sampleMacroInt(i: int): untyped =
   echo i.intVal
 
-macro sampleMacroBool(b: bool): stmt =
+macro sampleMacroBool(b: bool): untyped =
   echo b.boolVal
 
 sampleMacroInt(42)
diff --git a/tests/macros/tcomplexecho.nim b/tests/macros/tcomplexecho.nim
index f7f933c1c..0b70a3ef7 100644
--- a/tests/macros/tcomplexecho.nim
+++ b/tests/macros/tcomplexecho.nim
@@ -10,7 +10,7 @@ OK
 import macros
 
 # Bug from the forum
-macro addEcho1(s: untyped): stmt =
+macro addEcho1(s: untyped): untyped =
   s.body.add(newCall("echo", newStrLitNode("OK")))
   result = s
 
@@ -32,7 +32,7 @@ proc foo(): seq[NimNode] {.compiletime.} =
   result.add test()
   result.add parseExpr("echo(5+56)")
 
-macro bar(): stmt =
+macro bar(): typed =
   result = newNimNode(nnkStmtList)
   let x = foo()
   for xx in x:
diff --git a/tests/macros/tdebugstmt.nim b/tests/macros/tdebugstmt.nim
index 421f8fd14..740ae7b05 100644
--- a/tests/macros/tdebugstmt.nim
+++ b/tests/macros/tdebugstmt.nim
@@ -6,7 +6,7 @@ x: some string'''
 
 import macros
 
-macro debug(n: varargs[expr]): stmt =
+macro debug(n: varargs[untyped]): untyped =
   # `n` is a Nim AST that contains the whole macro invocation
   # this macro returns a list of statements:
   result = newNimNode(nnkStmtList, n)
diff --git a/tests/macros/tdumpast2.nim b/tests/macros/tdumpast2.nim
index 6b694fa77..c4c591b2a 100644
--- a/tests/macros/tdumpast2.nim
+++ b/tests/macros/tdumpast2.nim
@@ -21,7 +21,7 @@ proc dumpit(n: NimNode): string {.compileTime.} =
       add(result, dumpit(n[j]))
   add(result, ")")
 
-macro dumpAST(n: stmt): stmt {.immediate.} =
+macro dumpAST(n): untyped =
   # dump AST as a side-effect and return the inner node
   let n = callsite()
   echo dumpit(n)
diff --git a/tests/macros/tdumpastgen.nim b/tests/macros/tdumpastgen.nim
new file mode 100644
index 000000000..faed77225
--- /dev/null
+++ b/tests/macros/tdumpastgen.nim
@@ -0,0 +1,25 @@
+discard """
+msg: '''nnkStmtList.newTree(
+  nnkVarSection.newTree(
+    nnkIdentDefs.newTree(
+      newIdentNode(!"x"),
+      newEmptyNode(),
+      nnkCall.newTree(
+        nnkDotExpr.newTree(
+          newIdentNode(!"foo"),
+          newIdentNode(!"create")
+        ),
+        newLit(56)
+      )
+    )
+  )
+)'''
+"""
+
+# disabled; can't work as the output is done by the compiler
+
+import macros
+
+dumpAstGen:
+  var x = foo.create(56)
+
diff --git a/tests/macros/texprcolonexpr.nim b/tests/macros/texprcolonexpr.nim
index 3b2c86b77..59c799771 100644
--- a/tests/macros/texprcolonexpr.nim
+++ b/tests/macros/texprcolonexpr.nim
@@ -13,7 +13,7 @@ Infix
 """
 import macros
 
-macro def(x: stmt): stmt {.immediate.} =
+macro def(x): untyped =
   echo treeRepr(x)
 
 def name(a, b:cint) => nil
diff --git a/tests/macros/tgenericparams.nim b/tests/macros/tgenericparams.nim
new file mode 100644
index 000000000..d656f045a
--- /dev/null
+++ b/tests/macros/tgenericparams.nim
@@ -0,0 +1,13 @@
+discard """
+output: '''proc foo[T, N: static[int]]()
+proc foo[T; N: static[int]]()'''
+"""
+import macros
+
+macro test():string =
+    let expr0 = "proc foo[T, N: static[int]]()"
+    let expr1 = "proc foo[T; N: static[int]]()"
+
+    $toStrLit(parseExpr(expr0)) & "\n" & $toStrLit(parseExpr(expr1))
+    
+echo test()
diff --git a/tests/macros/tgensym.nim b/tests/macros/tgensym.nim
index a4d1a3606..955168939 100644
--- a/tests/macros/tgensym.nim
+++ b/tests/macros/tgensym.nim
@@ -11,7 +11,7 @@ proc convertReturns(node, retFutureSym: NimNode): NimNode {.compileTime.} =
     for i in 0 .. <node.len:
       result[i] = convertReturns(node[i], retFutureSym)
 
-macro async2(prc: stmt): stmt {.immediate.} =
+macro async2(prc: untyped): untyped =
   expectKind(prc, nnkProcDef)
 
   var outerProcBody = newNimNode(nnkStmtList)
diff --git a/tests/macros/tgentemplates.nim b/tests/macros/tgentemplates.nim
index 764b94bc7..301d58c6a 100644
--- a/tests/macros/tgentemplates.nim
+++ b/tests/macros/tgentemplates.nim
@@ -20,7 +20,7 @@ proc parse_template(node: NimNode, value: string) {.compiletime.} =
     while index < value.len and
         parse_until_symbol(node, value, index): discard
 
-macro tmpli*(body: expr): stmt =
+macro tmpli*(body: untyped): typed =
     result = newStmtList()
     result.add parseExpr("result = \"\"")
     result.parse_template body[1].strVal
diff --git a/tests/macros/tgettype.nim b/tests/macros/tgettype.nim
index 0eab6c0a0..fa02bce57 100644
--- a/tests/macros/tgettype.nim
+++ b/tests/macros/tgettype.nim
@@ -10,11 +10,18 @@ type
     name : string
     password : string
 
-macro testUser: expr =
-  return newLit(User.getType.lispRepr)
+macro testUser: string =
+  result = newLit(User.getType.lispRepr)
 
-macro testGeneric(T: typedesc[Model]): expr =
-  return newLit(T.getType.lispRepr)
+macro testGeneric(T: typedesc[Model]): string=
+  result = newLit(T.getType.lispRepr)
 
 echo testUser
 echo User.testGeneric
+
+macro assertVoid(e: typed): untyped =
+  assert(getTypeInst(e).typeKind == ntyVoid)
+
+proc voidProc() = discard
+
+assertVoid voidProc()
diff --git a/tests/macros/tgettypeinst.nim b/tests/macros/tgettypeinst.nim
index f89aa5e0b..8e1d9bc13 100644
--- a/tests/macros/tgettypeinst.nim
+++ b/tests/macros/tgettypeinst.nim
@@ -22,7 +22,7 @@ proc symToIdent(x: NimNode): NimNode =
         result.add symToIdent(c)
 
 # check getTypeInst and getTypeImpl for given symbol x
-macro testX(x,inst0: typed; recurse: static[bool]; implX: stmt): typed =
+macro testX(x,inst0: typed; recurse: static[bool]; implX: typed): typed =
   # check that getTypeInst(x) equals inst0
   let inst = x.getTypeInst
   let instr = inst.symToIdent.treeRepr
diff --git a/tests/macros/tidgen.nim b/tests/macros/tidgen.nim
index 2fe9e0f82..88322b227 100644
--- a/tests/macros/tidgen.nim
+++ b/tests/macros/tidgen.nim
@@ -8,7 +8,7 @@ import macros
 
 var gid {.compileTime.} = 3
 
-macro genId(): expr =
+macro genId(): int =
   result = newIntLitNode(gid)
   inc gid
 
diff --git a/tests/macros/tmacro1.nim b/tests/macros/tmacro1.nim
index 2dd5c31df..ac6bd02a5 100644
--- a/tests/macros/tmacro1.nim
+++ b/tests/macros/tmacro1.nim
@@ -2,7 +2,7 @@ import  macros
 
 from uri import `/`
 
-macro test*(a: stmt): stmt {.immediate.} =
+macro test*(a: untyped): untyped =
   var nodes: tuple[a, b: int]
   nodes.a = 4
   nodes[1] = 45
@@ -20,4 +20,3 @@ macro test*(a: stmt): stmt {.immediate.} =
 
 test:
   "hi"
-
diff --git a/tests/macros/tmacro2.nim b/tests/macros/tmacro2.nim
index 17f312925..72972c0c1 100644
--- a/tests/macros/tmacro2.nim
+++ b/tests/macros/tmacro2.nim
@@ -12,7 +12,7 @@ proc testBlock(): string {.compileTime.} =
     echo "outer block"
   result = "ta-da"
 
-macro mac(n: expr): expr =
+macro mac(n: typed): string =
   let n = callsite()
   expectKind(n, nnkCall)
   expectLen(n, 2)
diff --git a/tests/macros/tmacro3.nim b/tests/macros/tmacro3.nim
index d7421ff7f..a1dc4f371 100644
--- a/tests/macros/tmacro3.nim
+++ b/tests/macros/tmacro3.nim
@@ -8,7 +8,7 @@ type
     TA = tuple[a: int]
     PA = ref TA
 
-macro test*(a: stmt): stmt {.immediate.} =
+macro test*(a: untyped): untyped =
   var val: PA
   new(val)
   val.a = 4
@@ -16,7 +16,7 @@ macro test*(a: stmt): stmt {.immediate.} =
 test:
   "hi"
 
-macro test2*(a: stmt): stmt {.immediate.} =
+macro test2*(a: untyped): untyped =
   proc testproc(recurse: int) =
     echo "Thats weird"
     var o : NimNode = nil
@@ -28,4 +28,3 @@ macro test2*(a: stmt): stmt {.immediate.} =
 
 test2:
   "hi"
-
diff --git a/tests/macros/tmacro4.nim b/tests/macros/tmacro4.nim
index fb07941a9..164afaeb7 100644
--- a/tests/macros/tmacro4.nim
+++ b/tests/macros/tmacro4.nim
@@ -5,7 +5,7 @@ discard """
 import
   macros, strutils
 
-macro test_macro*(s: string, n: stmt): stmt {.immediate.} =
+macro test_macro*(s: string, n: untyped): untyped =
   result = newNimNode(nnkStmtList)
   var ass : NimNode = newNimNode(nnkAsgn)
   add(ass, newIdentNode("str"))
@@ -16,4 +16,3 @@ when isMainModule:
   test_macro(str):
     var i : integer = 123
   echo str
-
diff --git a/tests/macros/tmacro5.nim b/tests/macros/tmacro5.nim
index d7a4fe8c8..1c60e1ffd 100644
--- a/tests/macros/tmacro5.nim
+++ b/tests/macros/tmacro5.nim
@@ -3,7 +3,7 @@ import macros,json
 var decls{.compileTime.}: seq[NimNode] = @[]
 var impls{.compileTime.}: seq[NimNode] = @[]
 
-macro importImpl_forward(name, returns): stmt {.immediate.} =
+macro importImpl_forward(name, returns: untyped): untyped =
   result = newNimNode(nnkEmpty)
   var func_name = newNimNode(nnkAccQuoted)
   func_name.add newIdentNode("import")
@@ -19,7 +19,7 @@ macro importImpl_forward(name, returns): stmt {.immediate.} =
   res[3].add returns
   var p1 = newNimNode(nnkIdentDefs)
   p1.add newIdentNode("dat")
-  p1.add newIdentNOde("PJsonNode")
+  p1.add newIdentNOde("JsonNode")
   p1.add newNimNode(nnkEmpty)
   res[3].add p1
   var p2 = newNimNode(nnkIdentDefs)
@@ -38,7 +38,7 @@ macro importImpl_forward(name, returns): stmt {.immediate.} =
   decls.add res
   echo(repr(res))
 
-macro importImpl(name, returns: expr, body: stmt): stmt {.immediate.} =
+macro importImpl(name, returns, body: untyped): typed =
   #var res = getAST(importImpl_forward(name, returns))
   discard getAST(importImpl_forward(name, returns))
   var res = copyNimTree(decls[decls.high])
@@ -46,7 +46,7 @@ macro importImpl(name, returns: expr, body: stmt): stmt {.immediate.} =
   echo repr(res)
   impls.add res
 
-macro okayy:stmt =
+macro okayy: typed =
   result = newNimNode(nnkStmtList)
   for node in decls: result.add node
   for node in impls: result.add node
diff --git a/tests/macros/tmacro_in_template.nim b/tests/macros/tmacro_in_template.nim
index 8f7753cea..9668495df 100644
--- a/tests/macros/tmacro_in_template.nim
+++ b/tests/macros/tmacro_in_template.nim
@@ -2,8 +2,8 @@
 # bug #1944
 import macros
 
-template t(e: expr): stmt =
-  macro m(eNode: expr): stmt =
+template t(e: untyped): untyped =
+  macro m(eNode: untyped): untyped =
     echo eNode.treeRepr
   m e
 
diff --git a/tests/macros/tmacroaspragma.nim b/tests/macros/tmacroaspragma.nim
index 0e5c352b3..5f06f2425 100644
--- a/tests/macros/tmacroaspragma.nim
+++ b/tests/macros/tmacroaspragma.nim
@@ -1,8 +1,7 @@
 import macros
 
-macro foo(x: stmt): stmt =
+macro foo(x: untyped): untyped =
   echo treerepr(callsite())
   result = newNimNode(nnkStmtList)
 
 proc zoo() {.foo.} = echo "hi"
-
diff --git a/tests/macros/tmacros1.nim b/tests/macros/tmacros1.nim
index 1a1073a44..9e3ab028b 100644
--- a/tests/macros/tmacros1.nim
+++ b/tests/macros/tmacros1.nim
@@ -5,7 +5,7 @@ discard """
 import
   macros, strutils
 
-macro outterMacro*(n: stmt): stmt {.immediate.} =
+macro outterMacro*(n, blck: untyped): untyped =
   let n = callsite()
   var j : string = "hi"
   proc innerProc(i: int): string =
@@ -27,5 +27,3 @@ var str: string
 outterMacro(str):
   "hellow"
 echo str
-
-
diff --git a/tests/macros/tmacrostmt.nim b/tests/macros/tmacrostmt.nim
index 23e2f358c..6f648958f 100644
--- a/tests/macros/tmacrostmt.nim
+++ b/tests/macros/tmacrostmt.nim
@@ -1,5 +1,5 @@
 import macros
-macro case_token(n: stmt): stmt {.immediate.} =
+macro case_token(n: untyped): untyped {.immediate.} =
   # creates a lexical analyzer from regular expressions
   # ... (implementation is an exercise for the reader :-)
   nil
@@ -18,7 +18,7 @@ case_token: inc i
 
 #bug #488
 
-macro foo: stmt =
+macro foo: typed =
   var exp = newCall("whatwhat", newIntLitNode(1))
   if compiles(getAst(exp)): return exp
   else: echo "Does not compute!"
diff --git a/tests/macros/tmacrotypes.nim b/tests/macros/tmacrotypes.nim
index 991668930..e8a68c34d 100644
--- a/tests/macros/tmacrotypes.nim
+++ b/tests/macros/tmacrotypes.nim
@@ -5,7 +5,7 @@ int'''
 
 import macros
 
-macro checkType(ex: stmt; expected: expr): stmt =
+macro checkType(ex: typed; expected: string): untyped =
   var t = ex.getType()
   echo t
 
diff --git a/tests/macros/tnewlit.nim b/tests/macros/tnewlit.nim
index 69245d076..3ba1e09e1 100644
--- a/tests/macros/tnewlit.nim
+++ b/tests/macros/tnewlit.nim
@@ -138,3 +138,12 @@ macro test_newLit_ComposedType: untyped =
   result = newLit(ct)
 
 doAssert test_newLit_ComposedType == ComposedType(mt: MyType(a: 123, b:"abc"), arr: [1,2,3,4], data: @[1.byte, 3, 7, 127])
+
+macro test_newLit_empty_seq_string: untyped =
+  var strSeq = newSeq[string](0)
+  result = newLit(strSeq)
+
+block:
+  # x needs to be of type seq[string]
+  var x = test_newLit_empty_seq_string
+  x.add("xyz")
diff --git a/tests/macros/tnodecompare.nim b/tests/macros/tnodecompare.nim
index b9cf7df48..5ffb495b1 100644
--- a/tests/macros/tnodecompare.nim
+++ b/tests/macros/tnodecompare.nim
@@ -11,7 +11,7 @@ static:
   nodeB.strVal = "this is a different comment"
   doAssert nodeA != nodeB
 
-macro test(a: typed, b: typed): expr =
+macro test(a: typed, b: typed): untyped =
   newLit(a == b)
 
 doAssert test(1, 1) == true
@@ -29,10 +29,10 @@ var a, b: int
 doAssert test(a, a) == true
 doAssert test(a, b) == false
 
-macro test2: expr =
+macro test2: untyped =
   newLit(bindSym"Obj" == bindSym"Obj")
 
-macro test3: expr =
+macro test3: untyped =
   newLit(bindSym"Obj" == bindSym"Other")
 
 doAssert test2() == true
diff --git a/tests/macros/tquotewords.nim b/tests/macros/tquotewords.nim
index 48fcafd62..fa00ba9de 100644
--- a/tests/macros/tquotewords.nim
+++ b/tests/macros/tquotewords.nim
@@ -6,7 +6,7 @@ discard """
 
 import macros
 
-macro quoteWords(n: varargs[expr]): expr {.immediate.} =
+macro quoteWords(n: varargs[untyped]): untyped =
   let n = callsite()
   result = newNimNode(nnkBracket, n)
   for i in 1..n.len-1:
@@ -21,6 +21,3 @@ for w in items(myWordList):
   s.add(w)
 
 echo s #OUT thisanexample
-
-
-
diff --git a/tests/macros/trecmacro.nim b/tests/macros/trecmacro.nim
index 28b6db530..69ebe3da3 100644
--- a/tests/macros/trecmacro.nim
+++ b/tests/macros/trecmacro.nim
@@ -4,7 +4,7 @@ discard """
   errormsg: "recursive dependency: 'dump'"
 """
 
-macro dump(n: stmt): stmt =
+macro dump(n: untyped): untyped =
   dump(n)
   if kind(n) == nnkNone:
     nil
diff --git a/tests/macros/treturnsempty.nim b/tests/macros/treturnsempty.nim
index 7af26a747..a5a678af3 100644
--- a/tests/macros/treturnsempty.nim
+++ b/tests/macros/treturnsempty.nim
@@ -3,10 +3,9 @@ discard """
   line: 11
 """
 # bug #2372
-macro foo(dummy: int): stmt =
+macro foo(dummy: int): untyped =
   discard
 
 proc takeStr(s: string) = echo s
 
 takeStr foo(12)
-
diff --git a/tests/macros/tsametype.nim b/tests/macros/tsametype.nim
index 34296015f..865fb4cb8 100644
--- a/tests/macros/tsametype.nim
+++ b/tests/macros/tsametype.nim
@@ -13,7 +13,7 @@ false'''
 
 import macros
 
-macro same(a: typedesc, b: typedesc): expr =
+macro same(a: typedesc, b: typedesc): untyped =
   newLit(a.getType[1].sameType b.getType[1])
 
 echo same(int, int)
diff --git a/tests/macros/tstaticparamsmacro.nim b/tests/macros/tstaticparamsmacro.nim
index f6ecb3a9f..ea59936e0 100644
--- a/tests/macros/tstaticparamsmacro.nim
+++ b/tests/macros/tstaticparamsmacro.nim
@@ -25,7 +25,7 @@ type
 
 const data: Tconfig = (@["aa", "bb"], @[11, 22])
 
-macro mymacro(data: static[TConfig]): stmt =
+macro mymacro(data: static[TConfig]): untyped =
   echo "letters"
   for s in items(data.letters):
     echo s
@@ -43,10 +43,10 @@ const
   a : Ta = @[(11, 22), (33, 44)]
   b : Tb = (@[55,66], @[77, 88])
 
-macro mA(data: static[Ta]): stmt =
+macro mA(data: static[Ta]): untyped =
   echo "AST a \n", repr(data)
 
-macro mB(data: static[Tb]): stmt =
+macro mB(data: static[Tb]): untyped =
   echo "AST b \n", repr(data)
   echo data.e[0]
 
@@ -56,13 +56,13 @@ mB(b)
 type
   Foo[N: static[int], Z: static[string]] = object
 
-macro staticIntMacro(f: static[int]): stmt = echo f
+macro staticIntMacro(f: static[int]): untyped = echo f
 staticIntMacro 10
 
 var
   x: Foo[20, "Test"]
 
-macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12): stmt =
+macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12): untyped =
   echo N, Z
 
 genericMacro x
diff --git a/tests/macros/tstringinterp.nim b/tests/macros/tstringinterp.nim
index bc79cdaba..305f40ac5 100644
--- a/tests/macros/tstringinterp.nim
+++ b/tests/macros/tstringinterp.nim
@@ -9,7 +9,7 @@ proc concat(strings: varargs[string]): string =
   result = newString(0)
   for s in items(strings): result.add(s)
 
-template processInterpolations(e: expr) =
+template processInterpolations(e) =
   var s = e[1].strVal
   for f in interpolatedFragments(s):
     case f.kind
@@ -17,7 +17,7 @@ template processInterpolations(e: expr) =
     of ikDollar:      addDollar()
     of ikVar, ikExpr: addExpr(newCall("$", parseExpr(f.value)))
 
-macro formatStyleInterpolation(e: expr): expr =
+macro formatStyleInterpolation(e: untyped): untyped =
   let e = callsite()
   var
     formatString = ""
@@ -41,7 +41,7 @@ macro formatStyleInterpolation(e: expr): expr =
   result[1].strVal = formatString
   result[2] = arrayNode
 
-macro concatStyleInterpolation(e: expr): expr =
+macro concatStyleInterpolation(e: untyped): untyped =
   let e = callsite()
   var args: seq[NimNode]
   newSeq(args, 0)
@@ -71,4 +71,3 @@ var
   s2 = formatStyleInterpolation"Hello ${bob}, ${sum(alice.len, bob.len, 2)}$$"
 
 write(stdout, s1 & " | " & s2)
-
diff --git a/tests/macros/ttryparseexpr.nim b/tests/macros/ttryparseexpr.nim
index c7bbc8e5b..54442b662 100644
--- a/tests/macros/ttryparseexpr.nim
+++ b/tests/macros/ttryparseexpr.nim
@@ -5,7 +5,7 @@ discard """
 # feature request #1473
 import macros
 
-macro test(text: string): expr =
+macro test(text: string): untyped =
   try:
     result = parseExpr(text.strVal)
   except ValueError:
diff --git a/tests/macros/tvarnimnode.nim b/tests/macros/tvarnimnode.nim
index ab0f66caa..26c9b1f1a 100644
--- a/tests/macros/tvarnimnode.nim
+++ b/tests/macros/tvarnimnode.nim
@@ -10,7 +10,7 @@ proc test(f: var NimNode) {.compileTime.} =
   f = newNimNode(nnkStmtList)
   f.add newCall(newIdentNode("echo"), newLit(10))
 
-macro blah(prc: stmt): stmt =
+macro blah(prc: untyped): untyped =
   result = prc
 
   test(result)
diff --git a/tests/macros/typesapi.nim b/tests/macros/typesapi.nim
index 670b39c9e..cdbfc0ad2 100644
--- a/tests/macros/typesapi.nim
+++ b/tests/macros/typesapi.nim
@@ -8,7 +8,7 @@ proc (x: int) => typeDesc[proc[void, int]]'''
 
 import macros
 
-macro showType(t:stmt): stmt =
+macro showType(t:typed): untyped =
   let ty = t.getType
   echo t.repr, " => ", ty.repr
 
diff --git a/tests/macros/typesapi2.nim b/tests/macros/typesapi2.nim
index 2e59d2154..0130049c0 100644
--- a/tests/macros/typesapi2.nim
+++ b/tests/macros/typesapi2.nim
@@ -2,7 +2,7 @@
 # be used as a type
 import macros
 
-macro testTypesym (t:stmt): expr =
+macro testTypesym (t:typed): untyped =
     var ty = t.getType
     if ty.typekind == ntyTypedesc:
         # skip typedesc get to the real type
@@ -31,7 +31,7 @@ static: assert(ref int is testTypesym(ref int))
 static: assert(void is testTypesym(void))
 
 
-macro tts2 (t:stmt, idx:int): expr =
+macro tts2 (t:typed, idx:int): untyped =
     var ty = t.getType
     if ty.typekind == ntyTypedesc:
         # skip typedesc get to the real type
@@ -46,4 +46,3 @@ static:
     assert(tts2(TestFN2, 1) is string)
     assert(tts2(TestFN2, 2) is int)
     assert(tts2(TestFN2, 3) is float)
-
diff --git a/tests/manyloc/keineschweine/keineschweine.nim b/tests/manyloc/keineschweine/keineschweine.nim
index 804a22852..c0f1bc031 100644
--- a/tests/manyloc/keineschweine/keineschweine.nim
+++ b/tests/manyloc/keineschweine/keineschweine.nim
@@ -212,9 +212,10 @@ proc free(obj: PLiveBullet) =
   obj.record = nil
 
 
-template newExplosion(obj, animation): stmt =
+template newExplosion(obj, animation) =
   explosions.add(newAnimation(animation, AnimOnce, obj.body.getPos.cp2sfml, obj.body.getAngle))
-template newExplosion(obj, animation, angle): stmt =
+
+template newExplosion(obj, animation, angle) =
   explosions.add(newAnimation(animation, AnimOnce, obj.body.getPos.cp2sfml, angle))
 
 proc explode*(b: PLiveBullet) =
diff --git a/tests/metatype/tbindtypedesc.nim b/tests/metatype/tbindtypedesc.nim
index 4f027407b..b287aad01 100644
--- a/tests/metatype/tbindtypedesc.nim
+++ b/tests/metatype/tbindtypedesc.nim
@@ -16,10 +16,10 @@ type
   TBar = tuple
     x, y: int
 
-template accept(e: expr) =
+template accept(e) =
   static: assert(compiles(e))
 
-template reject(e: expr) =
+template reject(e) =
   static: assert(not compiles(e))
 
 proc genericParamRepeated[T: typedesc](a: T, b: T) =
diff --git a/tests/metatype/tmatrix.nim b/tests/metatype/tmatrix.nim
index 5acd4389e..4fd32a932 100644
--- a/tests/metatype/tmatrix.nim
+++ b/tests/metatype/tmatrix.nim
@@ -9,7 +9,7 @@ type
     data: seq[float]
     fWidth, fHeight: int
 
-template `|`(x, y: int): expr = y * m.fWidth + x
+template `|`(x, y: int): untyped = y * m.fWidth + x
 
 proc createMatrix*(width, height: int): TMatrix =
   result.fWidth = width
diff --git a/tests/metatype/tsemistatic.nim b/tests/metatype/tsemistatic.nim
index a13175ba8..3f36abde9 100644
--- a/tests/metatype/tsemistatic.nim
+++ b/tests/metatype/tsemistatic.nim
@@ -7,7 +7,7 @@ type
   semistatic[T] =
     static[T] or T
 
-template isStatic*(x): expr =
+template isStatic*(x): bool =
   compiles(static(x))
 
 proc foo(x: semistatic[int]) =
diff --git a/tests/metatype/tstatic_overloading.nim b/tests/metatype/tstatic_overloading.nim
index ce51052b7..9c065e48d 100644
--- a/tests/metatype/tstatic_overloading.nim
+++ b/tests/metatype/tstatic_overloading.nim
@@ -5,6 +5,6 @@ import macros
 proc impl(op: static[int]) = echo "impl 1 called"
 proc impl(op: static[int], init: int) = echo "impl 2 called"
 
-macro wrapper2: stmt = newCall(bindSym"impl", newLit(0), newLit(0))
+macro wrapper2: untyped = newCall(bindSym"impl", newLit(0), newLit(0))
 
 wrapper2() # Code generation for this fails.
diff --git a/tests/metatype/tstaticparammacro.nim b/tests/metatype/tstaticparammacro.nim
index 5c7c5e6af..bd3295874 100644
--- a/tests/metatype/tstaticparammacro.nim
+++ b/tests/metatype/tstaticparammacro.nim
@@ -26,7 +26,7 @@ type
 
 const data: Tconfig = (@["aa", "bb"], @[11, 22])
 
-macro mymacro(data: static[TConfig]): stmt =
+macro mymacro(data: static[TConfig]) =
   echo "letters"
   for s in items(data.letters):
     echo s
@@ -44,10 +44,10 @@ const
   a : Ta = @[(11, 22), (33, 44)]
   b : Tb = (@[55,66], @[77, 88])
 
-macro mA(data: static[Ta]): stmt =
+macro mA(data: static[Ta]) =
   echo "AST a \n", repr(data)
 
-macro mB(data: static[Tb]): stmt =
+macro mB(data: static[Tb]) =
   echo "AST b \n", repr(data)
   echo data.e[0]
 
@@ -57,13 +57,13 @@ mB(b)
 type
   Foo[N: static[int], Z: static[string]] = object
 
-macro staticIntMacro(f: static[int]): stmt = echo f
+macro staticIntMacro(f: static[int]) = echo f
 staticIntMacro 10
 
 var
   x: Foo[20, "Test"]
 
-macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12): stmt =
+macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12) =
   echo N, Z
 
 genericMacro x
diff --git a/tests/metatype/tstaticparams.nim b/tests/metatype/tstaticparams.nim
index 69b62e4a6..ad6aa6589 100644
--- a/tests/metatype/tstaticparams.nim
+++ b/tests/metatype/tstaticparams.nim
@@ -74,7 +74,7 @@ matrix_2(tmat, ar2)
 matrix_3(tmat, ar1)
 matrix_4(tmat, ar2)
 
-template reject(x): stmt =
+template reject(x): untyped =
   static: assert(not compiles(x))
 
 # test with arrays of wrong size
diff --git a/tests/metatype/ttypedesc1.nim b/tests/metatype/ttypedesc1.nim
index d78c62a94..e9eee581f 100644
--- a/tests/metatype/ttypedesc1.nim
+++ b/tests/metatype/ttypedesc1.nim
@@ -18,7 +18,7 @@ proc foo(T: typedesc[int or bool]): string =
   a = 10
   result = "int or bool " & ($a)
 
-template foo(T: typedesc[seq]): expr = "seq"
+template foo(T: typedesc[seq]): string = "seq"
 
 test "types can be used as proc params":
   # XXX: `check` needs to know that TFoo[int, float] is a type and
diff --git a/tests/metatype/ttypedesc2.nim b/tests/metatype/ttypedesc2.nim
index e576ec91a..7650a6f6b 100644
--- a/tests/metatype/ttypedesc2.nim
+++ b/tests/metatype/ttypedesc2.nim
@@ -18,11 +18,10 @@ when true:
       uoffset_t* = uint32
       FlatBufferBuilder* = object
 
-      uarray* {.unchecked.} [T]  = array [0..0, T]
       Array* [T] = object
           o*: uoffset_t
           len*: int
-          data*: ptr uarray[T]
+          data*: ptr UncheckedArray[T]
 
   proc ca* (fbb: ptr FlatBufferBuilder, T: typedesc, len: int): Array[T] {.noinit.} =
       result.len = len
diff --git a/tests/metatype/tymatrix.nim b/tests/metatype/tymatrix.nim
index 7d3d52f85..14c4d8c88 100644
--- a/tests/metatype/tymatrix.nim
+++ b/tests/metatype/tymatrix.nim
@@ -1,6 +1,6 @@
 import typetraits
 
-template reject(e: expr) =
+template reject(e) =
   static: assert(not compiles(e))
 
 type
diff --git a/tests/method/tmapper.nim b/tests/method/tmapper.nim
index 0008d9033..a5d03f700 100644
--- a/tests/method/tmapper.nim
+++ b/tests/method/tmapper.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "invalid declaration order; cannot attach 'step' to method defined here: tmapper.nim(22,7)"
+  errormsg: "invalid declaration order; cannot attach 'step' to method defined here: tmapper.nim(22, 7)"
   line: 25
 """
 
diff --git a/tests/misc/thallo.nim b/tests/misc/thallo.nim
index 17e955f03..17e6089ed 100644
--- a/tests/misc/thallo.nim
+++ b/tests/misc/thallo.nim
@@ -15,7 +15,7 @@ proc fac[T](x: T): T =
   if x <= 1: return 1
   else: return x.`*`(fac(x-1))
 
-macro macrotest(n: expr): stmt {.immediate.} =
+macro macrotest(n: varargs[untyped]): untyped =
   let n = callsite()
   expectKind(n, nnkCall)
   expectMinLen(n, 2)
@@ -24,7 +24,7 @@ macro macrotest(n: expr): stmt {.immediate.} =
     result.add(newCall("write", n[1], n[i]))
   result.add(newCall("writeLine", n[1], newStrLitNode("")))
 
-macro debug(n: expr): stmt {.immediate.} =
+macro debug(n: untyped): untyped {.immediate.} =
   let n = callsite()
   result = newNimNode(nnkStmtList, n)
   for i in 1..n.len-1:
@@ -82,4 +82,3 @@ for i in 2..6:
 
 when isMainModule:
   {.hint: "this is the main file".}
-
diff --git a/tests/misc/tints.nim b/tests/misc/tints.nim
index 5bfb8a17c..d5374a543 100644
--- a/tests/misc/tints.nim
+++ b/tests/misc/tints.nim
@@ -8,7 +8,7 @@ Success'''
 
 var testNumber = 0
 
-template test(opr, a, b, c: expr): stmt {.immediate.} =
+template test(opr, a, b, c: untyped): untyped =
   # test the expression at compile and runtime
   block:
     const constExpr = opr(a, b)
diff --git a/tests/misc/tsimplesort.nim b/tests/misc/tsimplesort.nim
index 9c6ad1207..3115863d5 100644
--- a/tests/misc/tsimplesort.nim
+++ b/tests/misc/tsimplesort.nim
@@ -118,7 +118,7 @@ proc toTable*[A, B](pairs: openarray[tuple[key: A,
   result = initTable[A, B](nextPowerOfTwo(pairs.len+10))
   for key, val in items(pairs): result[key] = val
 
-template dollarImpl(): stmt =
+template dollarImpl(): typed =
   if t.len == 0:
     result = "{:}"
   else:
@@ -305,5 +305,3 @@ proc countTableTest1 =
 
 countTableTest1()
 echo true
-
-
diff --git a/tests/modules/tmismatchedvisibility.nim b/tests/modules/tmismatchedvisibility.nim
index 91b639a27..2e8636d1e 100644
--- a/tests/modules/tmismatchedvisibility.nim
+++ b/tests/modules/tmismatchedvisibility.nim
@@ -1,6 +1,6 @@
 discard """
   line: 8
-  errormsg: "public implementation 'tmismatchedvisibility.foo(a: int)[declared in tmismatchedvisibility.nim(6,5)]' has non-public forward declaration in "
+  errormsg: "public implementation 'tmismatchedvisibility.foo(a: int)[declared in tmismatchedvisibility.nim(6, 5)]' has non-public forward declaration in "
 """
 
 proc foo(a: int): int
diff --git a/tests/modules/treorder.nim b/tests/modules/treorder.nim
new file mode 100644
index 000000000..25280c429
--- /dev/null
+++ b/tests/modules/treorder.nim
@@ -0,0 +1,46 @@
+discard """
+  cmd: "nim -d:testdef $target $file"
+  output: '''works 34
+34
+defined
+3'''
+"""
+
+{.reorder: on.}
+{.experimental.}
+
+{.push callconv: stdcall.}
+proc bar(x: T)
+
+proc foo() =
+  bar(34)
+  whendep()
+
+proc foo(dummy: int) = echo dummy
+
+proc bar(x: T) =
+  echo "works ", x
+  foo(x)
+
+foo()
+
+type
+  T = int
+
+when defined(testdef):
+  proc whendep() = echo "defined"
+else:
+  proc whendep() = echo "undefined"
+
+when not declared(goo):
+  proc goo(my, omy) = echo my
+
+when not declared(goo):
+  proc goo(my, omy) = echo omy
+
+using
+  my, omy: int
+
+goo(3, 4)
+
+{.pop.}
diff --git a/tests/newconfig/tfoo.nims b/tests/newconfig/tfoo.nims
index 8a709914f..8d0ed2c42 100644
--- a/tests/newconfig/tfoo.nims
+++ b/tests/newconfig/tfoo.nims
@@ -22,3 +22,5 @@ task default, "default target":
   --define: definedefine
   setCommand "c"
 
+# bug #6327
+discard existsEnv("dummy")
diff --git a/tests/nimble/nimbleDir/linkedPkgs/pkgA-0.1.0/pkgA.nimble-link b/tests/nimble/nimbleDir/linkedPkgs/pkgA-0.1.0/pkgA.nimble-link
new file mode 100644
index 000000000..8dc825fc9
--- /dev/null
+++ b/tests/nimble/nimbleDir/linkedPkgs/pkgA-0.1.0/pkgA.nimble-link
@@ -0,0 +1,2 @@
+../../simplePkgs/pkgA-0.1.0/pkgA.nimble
+../../simplePkgs/pkgA-0.1.0/
\ No newline at end of file
diff --git a/tests/nimble/nimbleDir/linkedPkgs/pkgB-#head/pkgB.nimble-link b/tests/nimble/nimbleDir/linkedPkgs/pkgB-#head/pkgB.nimble-link
new file mode 100644
index 000000000..a57a3cb66
--- /dev/null
+++ b/tests/nimble/nimbleDir/linkedPkgs/pkgB-#head/pkgB.nimble-link
@@ -0,0 +1,2 @@
+../../simplePkgs/pkgB-#head/pkgB.nimble
+../../simplePkgs/pkgB-#head/
\ No newline at end of file
diff --git a/tests/nimble/nimbleDir/linkedPkgs/pkgB-0.1.0/pkgB.nimble-link b/tests/nimble/nimbleDir/linkedPkgs/pkgB-0.1.0/pkgB.nimble-link
new file mode 100644
index 000000000..9643c0fa0
--- /dev/null
+++ b/tests/nimble/nimbleDir/linkedPkgs/pkgB-0.1.0/pkgB.nimble-link
@@ -0,0 +1,2 @@
+../../simplePkgs/pkgB-0.1.0/pkgB.nimble
+../../simplePkgs/pkgB-0.1.0/
\ No newline at end of file
diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgA-0.1.0/pkgA.nimble b/tests/nimble/nimbleDir/simplePkgs/pkgA-0.1.0/pkgA.nimble
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/nimble/nimbleDir/simplePkgs/pkgA-0.1.0/pkgA.nimble
diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgA-0.1.0/pkgA/module.nim b/tests/nimble/nimbleDir/simplePkgs/pkgA-0.1.0/pkgA/module.nim
new file mode 100644
index 000000000..6cb3b77d9
--- /dev/null
+++ b/tests/nimble/nimbleDir/simplePkgs/pkgA-0.1.0/pkgA/module.nim
@@ -0,0 +1 @@
+proc pkgATest*(): int = 1
\ No newline at end of file
diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgB-#head/pkgB.nimble b/tests/nimble/nimbleDir/simplePkgs/pkgB-#head/pkgB.nimble
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/nimble/nimbleDir/simplePkgs/pkgB-#head/pkgB.nimble
diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgB-#head/pkgB/module.nim b/tests/nimble/nimbleDir/simplePkgs/pkgB-#head/pkgB/module.nim
new file mode 100644
index 000000000..03d2298a2
--- /dev/null
+++ b/tests/nimble/nimbleDir/simplePkgs/pkgB-#head/pkgB/module.nim
@@ -0,0 +1 @@
+proc pkgBTest*(): int64 = 0xDEADBEEF
\ No newline at end of file
diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgB-0.1.0/pkgB.nimble b/tests/nimble/nimbleDir/simplePkgs/pkgB-0.1.0/pkgB.nimble
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/nimble/nimbleDir/simplePkgs/pkgB-0.1.0/pkgB.nimble
diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgB-0.1.0/pkgB/module.nim b/tests/nimble/nimbleDir/simplePkgs/pkgB-0.1.0/pkgB/module.nim
new file mode 100644
index 000000000..56ff64197
--- /dev/null
+++ b/tests/nimble/nimbleDir/simplePkgs/pkgB-0.1.0/pkgB/module.nim
@@ -0,0 +1 @@
+proc pkgBTest*(): int64 = 0
\ No newline at end of file
diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgC-#aa11/pkgC.nimble b/tests/nimble/nimbleDir/simplePkgs/pkgC-#aa11/pkgC.nimble
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/nimble/nimbleDir/simplePkgs/pkgC-#aa11/pkgC.nimble
diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgC-#aa11/pkgC/module.nim b/tests/nimble/nimbleDir/simplePkgs/pkgC-#aa11/pkgC/module.nim
new file mode 100644
index 000000000..24a67bbe2
--- /dev/null
+++ b/tests/nimble/nimbleDir/simplePkgs/pkgC-#aa11/pkgC/module.nim
@@ -0,0 +1 @@
+proc pkgCTest*(): int64 = 1
\ No newline at end of file
diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgC-#head/pkgC.nimble b/tests/nimble/nimbleDir/simplePkgs/pkgC-#head/pkgC.nimble
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/nimble/nimbleDir/simplePkgs/pkgC-#head/pkgC.nimble
diff --git a/tests/nimble/nimbleDir/simplePkgs/pkgC-#head/pkgC/module.nim b/tests/nimble/nimbleDir/simplePkgs/pkgC-#head/pkgC/module.nim
new file mode 100644
index 000000000..2f243ad82
--- /dev/null
+++ b/tests/nimble/nimbleDir/simplePkgs/pkgC-#head/pkgC/module.nim
@@ -0,0 +1 @@
+proc pkgCTest*(): int64 = 0xDEADBEEF
\ No newline at end of file
diff --git a/tests/nimble/readme.md b/tests/nimble/readme.md
new file mode 100644
index 000000000..10fd21a83
--- /dev/null
+++ b/tests/nimble/readme.md
@@ -0,0 +1,2 @@
+This directory contains tests for the --nimblePath feature.
+
diff --git a/tests/nimble/tnimblepath.nim b/tests/nimble/tnimblepath.nim
new file mode 100644
index 000000000..6ba1be1d1
--- /dev/null
+++ b/tests/nimble/tnimblepath.nim
@@ -0,0 +1,11 @@
+discard """
+  action: run
+  cmd: "nim $target --nimblePath:$fileDir/nimbleDir/simplePkgs $options $file"
+"""
+import pkgA/module as A
+import pkgB/module as B
+import pkgC/module as C
+
+doAssert pkgATest() == 1, "Simple pkgA-0.1.0 wasn't added to path correctly."
+doAssert pkgBTest() == 0xDEADBEEF, "pkgB-#head wasn't picked over pkgB-0.1.0"
+doAssert pkgCTest() == 0xDEADBEEF, "pkgC-#head wasn't picked over pkgC-#aa11"
\ No newline at end of file
diff --git a/tests/nimble/tnimblepathlink.nim b/tests/nimble/tnimblepathlink.nim
new file mode 100644
index 000000000..5b2c7cb5b
--- /dev/null
+++ b/tests/nimble/tnimblepathlink.nim
@@ -0,0 +1,9 @@
+discard """
+  action: run
+  cmd: "nim $target --nimblePath:$fileDir/nimbleDir/linkedPkgs $options $file"
+"""
+import pkgA/module as A
+import pkgB/module as B
+
+doAssert pkgATest() == 1, "Simple linked pkgA-0.1.0 wasn't added to path correctly."
+doAssert pkgBTest() == 0xDEADBEEF, "linked pkgB-#head wasn't picked over pkgB-0.1.0"
\ No newline at end of file
diff --git a/tests/objects/tobjconstr.nim b/tests/objects/tobjconstr.nim
index 226fe98f7..12478f621 100644
--- a/tests/objects/tobjconstr.nim
+++ b/tests/objects/tobjconstr.nim
@@ -8,7 +8,14 @@ discard """
 (k: kindA, a: (x: abc, z: [1, 7, 3]), method: ())
 (k: kindA, a: (x: abc, z: [1, 8, 3]), method: ())
 (k: kindA, a: (x: abc, z: [1, 9, 3]), method: ())
-(k: kindA, a: (x: abc, z: [1, 10, 3]), method: ())'''
+(k: kindA, a: (x: abc, z: [1, 10, 3]), method: ())
+(x: 123)
+(x: 123)
+(z: 89, y: 0, x: 128)
+(y: 678, x: 123)
+(y: 678, x: 123)
+(y: 0, x: 123)
+(y: 678, x: 123)'''
 """
 
 type
@@ -39,3 +46,32 @@ proc main() =
 
 main()
 
+# bug #6294
+type
+  A = object of RootObj
+    x*: int
+  B = object of A
+    y*: int
+  BS = object of B
+  C = object of BS
+    z*: int
+# inherited fields are ignored, so no fields are set
+when true:
+  var
+    o: A
+  o = B(x: 123)
+  echo o
+  o = B(y: 678, x: 123)
+  echo o
+
+# inherited fields are ignored
+echo C(x: 128, z: 89)          # (y: 0, x: 0)
+echo B(y: 678, x: 123)  # (y: 678, x: 0)
+echo B(x: 123, y: 678)  # (y: 678, x: 0)
+
+when true:
+  # correct, both with `var` and `let`;
+  var b=B(x: 123)
+  echo b                  # (y: 0, x: 123)
+  b=B(y: 678, x: 123)
+  echo b                  # (y: 678, x: 123)
diff --git a/tests/objects/tobject3.nim b/tests/objects/tobject3.nim
index 15dd8ea24..a24a48c8b 100644
--- a/tests/objects/tobject3.nim
+++ b/tests/objects/tobject3.nim
@@ -1,3 +1,13 @@
+discard """
+  output: '''TBar2
+TFoo
+16
+12
+16
+12'''
+"""
+
+## XXX this output needs to be adapated for VCC which produces different results.
 
 # It turned out that it's hard to generate correct for these two test cases at
 # the same time.
@@ -57,3 +67,44 @@ var aa = makeWindow()
 
 thisCausesError(dd, aa)
 
+# bug #4763
+type
+  testObject_1 = object
+    size: int32
+    value: int64
+
+  testObject_2 {.packed.} = object
+    size: int32
+    value: int64
+
+  testObject_3[T] = object
+    size: int32
+    value: T
+
+  testObject_4 {.packed.} [T] = object
+    size: int32
+    value: T
+
+echo sizeof(testObject_1)
+echo sizeof(testObject_2)
+echo sizeof(testObject_3[int64])
+echo sizeof(testObject_4[int64])
+
+# bug  #5892
+type
+    Foo6 = distinct array[4, float32]
+    AnotherFoo = distinct array[4, float32]
+
+    AbstractAnimationSampler* = ref object of RootObj
+
+    AnimationSampler*[T] = ref object of AbstractAnimationSampler
+        sampleImpl: proc(s: AnimationSampler[T], p: float): T
+
+    ArrayAnimationSampler*[T] = ref object of AnimationSampler[T]
+
+proc newArrayAnimationSampler*[T](): ArrayAnimationSampler[T] =
+    result.new()
+    result.sampleImpl = nil
+
+discard newArrayAnimationSampler[Foo6]()
+discard newArrayAnimationSampler[AnotherFoo]()
diff --git a/tests/objects/toop1.nim b/tests/objects/toop1.nim
index 4727d146d..f4880e3c6 100644
--- a/tests/objects/toop1.nim
+++ b/tests/objects/toop1.nim
@@ -35,7 +35,7 @@ proc init(my: var TRectangle) =
   my.height = 10
   my.draw = cast[proc (my: var TFigure) {.nimcall.}](drawRectangle)
 
-macro `!` (n: expr): stmt {.immediate.} =
+macro `!` (n: untyped): typed {.immediate.}=
   let n = callsite()
   result = newNimNode(nnkCall, n)
   var dot = newNimNode(nnkDotExpr, n)
diff --git a/tests/osproc/tworkingdir.nim b/tests/osproc/tworkingdir.nim
index 84ba3375c..eeed9240d 100644
--- a/tests/osproc/tworkingdir.nim
+++ b/tests/osproc/tworkingdir.nim
@@ -9,7 +9,11 @@ when defined(windows):
   discard
 else:
   let dir1 = getCurrentDir()
-  var process = startProcess("/usr/bin/env", "/usr/bin", ["true"])
+  var process: Process
+  when defined(android):
+    process = startProcess("/system/bin/env", "/system/bin", ["true"])
+  else:
+    process = startProcess("/usr/bin/env", "/usr/bin", ["true"])
   let dir2 = getCurrentDir()
   discard process.waitForExit()
   process.close()
diff --git a/tests/overload/tparams_after_varargs.nim b/tests/overload/tparams_after_varargs.nim
index a93e280b9..ad8f19ad3 100644
--- a/tests/overload/tparams_after_varargs.nim
+++ b/tests/overload/tparams_after_varargs.nim
@@ -1,7 +1,8 @@
 discard """
   output: '''a 1 b 2 x @[3, 4, 5] y 6 z 7
 yay
-12'''
+12
+'''
 """
 
 proc test(a, b: int, x: varargs[int]; y, z: int) =
@@ -9,9 +10,10 @@ proc test(a, b: int, x: varargs[int]; y, z: int) =
 
 test 1, 2, 3, 4, 5, 6, 7
 
-template takesBlock(a, b: int, x: varargs[expr]; blck: stmt) =
+# XXX maybe this should also work with ``varargs[untyped]``
+template takesBlockA(a, b: untyped; x: varargs[typed]; blck: untyped): untyped =
   blck
   echo a, b
 
-takesBlock 1, 2, "some", 0.90, "random stuff":
+takesBlockA 1, 2, "some", 0.90, "random stuff":
   echo "yay"
diff --git a/tests/overload/tspec.nim b/tests/overload/tspec.nim
index f2002a390..a84bac244 100644
--- a/tests/overload/tspec.nim
+++ b/tests/overload/tspec.nim
@@ -71,7 +71,7 @@ var ri: ref int
 gen(ri) # "ref T"
 
 
-template rem(x: expr) = discard
+template rem(x) = discard
 #proc rem[T](x: T) = discard
 
 rem unresolvedExpression(undeclaredIdentifier)
diff --git a/tests/overload/tstmtoverload.nim b/tests/overload/tstmtoverload.nim
index 75584bcab..7c0194e60 100644
--- a/tests/overload/tstmtoverload.nim
+++ b/tests/overload/tstmtoverload.nim
@@ -2,17 +2,17 @@
 # bug #2481
 import math
 
-template test(loopCount: int, extraI: int, testBody: stmt): stmt =
+template test(loopCount: int, extraI: int, testBody: untyped): typed =
   block:
     for i in 0..loopCount-1:
       testBody
     echo "done extraI=", extraI
 
-template test(loopCount: int, extraF: float, testBody: stmt): stmt =
+template test(loopCount: int, extraF: float, testBody: untyped): typed =
   block:
     test(loopCount, round(extraF).int, testBody)
 
-template test(loopCount: int, testBody: stmt): stmt =
+template test(loopCount: int, testBody: untyped): typed =
   block:
     test(loopCount, 0, testBody)
     echo "done extraI passed 0"
diff --git a/tests/parallel/tblocking_channel.nim b/tests/parallel/tblocking_channel.nim
new file mode 100644
index 000000000..8b8b49454
--- /dev/null
+++ b/tests/parallel/tblocking_channel.nim
@@ -0,0 +1,37 @@
+discard """
+output: ""
+"""
+import threadpool, os
+
+var chan: Channel[int]
+
+chan.open(2)
+chan.send(1)
+chan.send(2)
+doAssert(not chan.trySend(3)) # At this point chan is at max capacity
+
+proc receiver() =
+    doAssert(chan.recv() == 1)
+    doAssert(chan.recv() == 2)
+    doAssert(chan.recv() == 3)
+    doAssert(chan.recv() == 4)
+    doAssert(chan.recv() == 5)
+
+var msgSent = false
+
+proc emitter() =
+    chan.send(3)
+    msgSent = true
+
+spawn emitter()
+# At this point emitter should be stuck in `send`
+sleep(100) # Sleep a bit to ensure that it is still stuck
+doAssert(not msgSent)
+
+spawn receiver()
+sleep(100) # Sleep a bit to let receicer consume the messages
+doAssert(msgSent) # Sender should be unblocked
+
+doAssert(chan.trySend(4))
+chan.send(5)
+sync()
diff --git a/tests/parallel/tconvexhull.nim b/tests/parallel/tconvexhull.nim
index dffe5339b..c4990bf2d 100644
--- a/tests/parallel/tconvexhull.nim
+++ b/tests/parallel/tconvexhull.nim
@@ -20,10 +20,10 @@ proc cmpPoint(a, b: Point): int =
   if result == 0:
     result = cmp(a.y, b.y)
 
-template cross[T](o, a, b: T): expr =
+template cross[T](o, a, b: T): untyped =
   (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)
 
-template pro(): expr =
+template pro(): untyped =
   while lr1 > 0 and cross(result[lr1 - 1], result[lr1], p[i]) <= 0:
     discard result.pop
     lr1 -= 1
diff --git a/tests/parallel/tguard1.nim b/tests/parallel/tguard1.nim
index 3e0c131c5..c7972d225 100644
--- a/tests/parallel/tguard1.nim
+++ b/tests/parallel/tguard1.nim
@@ -24,7 +24,7 @@ var
 
 c.i = 89
 
-template atomicRead(L, x): expr =
+template atomicRead(L, x): untyped =
   {.locks: [L].}:
     x
 
diff --git a/tests/parallel/tguard2.nim b/tests/parallel/tguard2.nim
index b69ea3371..661893bb5 100644
--- a/tests/parallel/tguard2.nim
+++ b/tests/parallel/tguard2.nim
@@ -13,7 +13,7 @@ var
 
 c.i = 89
 
-template atomicRead(L, x): expr =
+template atomicRead(L, x): untyped =
   {.locks: [L].}:
     x
 
diff --git a/tests/parallel/tptr_to_ref.nim b/tests/parallel/tptr_to_ref.nim
index 229c247ce..fee210dcd 100644
--- a/tests/parallel/tptr_to_ref.nim
+++ b/tests/parallel/tptr_to_ref.nim
@@ -14,7 +14,7 @@ type
     processes {.guard: lock.}: array[0..MAX_WORKERS-1, foreign ptr Process]
 
 # Hold a lock for a statement.
-template hold(lock: Lock, body: stmt) =
+template hold(lock: Lock, body: untyped) =
   lock.acquire
   defer: lock.release
   {.locks: [lock].}:
diff --git a/tests/parser/toprprec.nim b/tests/parser/toprprec.nim
index 2c22f5b80..1acd381e7 100644
--- a/tests/parser/toprprec.nim
+++ b/tests/parser/toprprec.nim
@@ -4,9 +4,11 @@ discard """
 """
 # Test operator precedence:
 
-template `@` (x: expr): expr {.immediate.} = self.x
-template `@!` (x: expr): expr {.immediate.} = x
-template `===` (x: expr): expr {.immediate.} = x
+template `@` (x: untyped): untyped {.immediate.} =
+  `self`.x
+
+template `@!` (x: untyped): untyped = x
+template `===` (x: untyped): untyped = x
 
 type
   TO = object
@@ -34,6 +36,3 @@ var s: TA
 assert init(s) == "4"
 
 echo "done"
-
-
-
diff --git a/tests/parser/tpostexprblocks.nim b/tests/parser/tpostexprblocks.nim
index 85f2628bf..341ca737a 100644
--- a/tests/parser/tpostexprblocks.nim
+++ b/tests/parser/tpostexprblocks.nim
@@ -1,51 +1,59 @@
 discard """
 nimout: '''
 StmtList
-  Ident !"foo"
+  Ident !"foo010"
   Call
-    Ident !"foo"
+    Ident !"foo020"
   Call
-    Ident !"foo"
+    Ident !"foo030"
     Ident !"x"
   Command
-    Ident !"foo"
+    Ident !"foo040"
     Ident !"x"
   Call
-    Ident !"foo"
+    Ident !"foo050"
     StmtList
       DiscardStmt
         Empty
   Call
-    Ident !"foo"
+    Ident !"foo060"
     StmtList
       DiscardStmt
         Empty
   Call
-    Ident !"foo"
+    Ident !"foo070"
     StrLit test
     StmtList
       DiscardStmt
         Empty
   Call
-    Ident !"foo"
+    Ident !"foo080"
     StrLit test
     StmtList
       DiscardStmt
         Empty
   Command
-    Ident !"foo"
+    Ident !"foo090"
     StrLit test
     StmtList
       DiscardStmt
         Empty
   Command
-    Ident !"foo"
-    StrLit test
-    StmtList
-      DiscardStmt
-        Empty
+    Ident !"foo100"
+    Call
+      StrLit test
+      StmtList
+        DiscardStmt
+          Empty
   Command
-    Ident !"foo"
+    Ident !"foo101"
+    Call
+      IntLit 10
+      StmtList
+        DiscardStmt
+          Empty
+  Command
+    Ident !"foo110"
     IntLit 1
     Par
       Infix
@@ -56,18 +64,19 @@ StmtList
       DiscardStmt
         Empty
   Command
-    Ident !"foo"
+    Ident !"foo120"
     IntLit 1
-    Par
-      Infix
-        Ident !"+"
-        IntLit 2
-        IntLit 3
-    StmtList
-      DiscardStmt
-        Empty
+    Call
+      Par
+        Infix
+          Ident !"+"
+          IntLit 2
+          IntLit 3
+      StmtList
+        DiscardStmt
+          Empty
   Call
-    Ident !"foo"
+    Ident !"foo130"
     Do
       Empty
       Empty
@@ -84,7 +93,7 @@ StmtList
         DiscardStmt
           Empty
   Call
-    Ident !"foo"
+    Ident !"foo140"
     Do
       Empty
       Empty
@@ -101,7 +110,7 @@ StmtList
         DiscardStmt
           Empty
   Call
-    Ident !"foo"
+    Ident !"foo150"
     Do
       Empty
       Empty
@@ -118,25 +127,26 @@ StmtList
         DiscardStmt
           Empty
   Command
-    Ident !"foo"
-    Ident !"x"
-    Do
-      Empty
-      Empty
-      Empty
-      FormalParams
+    Ident !"foo160"
+    Call
+      Ident !"x"
+      Do
         Empty
-        IdentDefs
-          Ident !"y"
-          Empty
-          Empty
-      Empty
-      Empty
-      StmtList
-        DiscardStmt
+        Empty
+        Empty
+        FormalParams
           Empty
+          IdentDefs
+            Ident !"y"
+            Empty
+            Empty
+        Empty
+        Empty
+        StmtList
+          DiscardStmt
+            Empty
   Call
-    Ident !"foo"
+    Ident !"foo170"
     StmtList
       DiscardStmt
         Empty
@@ -145,7 +155,7 @@ StmtList
         DiscardStmt
           Empty
   Call
-    Ident !"foo"
+    Ident !"foo180"
     StmtList
       DiscardStmt
         Empty
@@ -157,62 +167,63 @@ StmtList
         DiscardStmt
           Empty
   Command
-    Ident !"foo"
-    Ident !"x"
-    Do
-      Empty
-      Empty
-      Empty
-      FormalParams
+    Ident !"foo190"
+    Call
+      Ident !"x"
+      Do
         Empty
-        IdentDefs
-          Ident !"y"
-          Empty
-          Empty
-      Empty
-      Empty
-      StmtList
-        DiscardStmt
-          Empty
-    Do
-      Empty
-      Empty
-      Empty
-      FormalParams
-        Ident !"int"
-        IdentDefs
-          Ident !"z"
-          Empty
-          Empty
-      Empty
-      Empty
-      StmtList
-        DiscardStmt
+        Empty
+        Empty
+        FormalParams
           Empty
-    Do
-      Empty
-      Empty
-      Empty
-      FormalParams
-        Ident !"int"
-        IdentDefs
-          Ident !"w"
+          IdentDefs
+            Ident !"y"
+            Empty
+            Empty
+        Empty
+        Empty
+        StmtList
+          DiscardStmt
+            Empty
+      Do
+        Empty
+        Empty
+        Empty
+        FormalParams
           Ident !"int"
-          Empty
-      Empty
-      Empty
-      StmtList
-        DiscardStmt
-          Empty
-    StmtList
-      DiscardStmt
+          IdentDefs
+            Ident !"z"
+            Empty
+            Empty
         Empty
-    Else
+        Empty
+        StmtList
+          DiscardStmt
+            Empty
+      Do
+        Empty
+        Empty
+        Empty
+        FormalParams
+          Ident !"int"
+          IdentDefs
+            Ident !"w"
+            Ident !"int"
+            Empty
+        Empty
+        Empty
+        StmtList
+          DiscardStmt
+            Empty
       StmtList
         DiscardStmt
           Empty
+      Else
+        StmtList
+          DiscardStmt
+            Empty
   Call
-    Ident !"foo"
+    Ident !"foo200"
     Ident !"x"
     Call
       Ident !"bar"
@@ -227,33 +238,33 @@ StmtList
     IdentDefs
       Ident !"a"
       Empty
-      Ident !"foo"
+      Ident !"foo210"
   VarSection
     IdentDefs
       Ident !"a"
       Empty
       Call
-        Ident !"foo"
+        Ident !"foo220"
   VarSection
     IdentDefs
       Ident !"a"
       Empty
       Call
-        Ident !"foo"
+        Ident !"foo230"
         Ident !"x"
   VarSection
     IdentDefs
       Ident !"a"
       Empty
       Command
-        Ident !"foo"
+        Ident !"foo240"
         Ident !"x"
   VarSection
     IdentDefs
       Ident !"a"
       Empty
       Call
-        Ident !"foo"
+        Ident !"foo250"
         StmtList
           DiscardStmt
             Empty
@@ -262,7 +273,7 @@ StmtList
       Ident !"a"
       Empty
       Call
-        Ident !"foo"
+        Ident !"foo260"
         StmtList
           DiscardStmt
             Empty
@@ -271,7 +282,7 @@ StmtList
       Ident !"a"
       Empty
       Call
-        Ident !"foo"
+        Ident !"foo270"
         StmtList
           DiscardStmt
             Empty
@@ -284,62 +295,63 @@ StmtList
       Ident !"a"
       Empty
       Command
-        Ident !"foo"
-        Ident !"x"
-        Do
-          Empty
-          Empty
-          Empty
-          FormalParams
+        Ident !"foo280"
+        Call
+          Ident !"x"
+          Do
             Empty
-            IdentDefs
-              Ident !"y"
-              Empty
-              Empty
-          Empty
-          Empty
-          StmtList
-            DiscardStmt
-              Empty
-        Else
-          StmtList
-            DiscardStmt
+            Empty
+            Empty
+            FormalParams
               Empty
+              IdentDefs
+                Ident !"y"
+                Empty
+                Empty
+            Empty
+            Empty
+            StmtList
+              DiscardStmt
+                Empty
+          Else
+            StmtList
+              DiscardStmt
+                Empty
   Asgn
     Ident !"a"
-    Ident !"foo"
+    Ident !"foo290"
   Asgn
     Ident !"a"
     Call
-      Ident !"foo"
+      Ident !"foo300"
   Asgn
     Ident !"a"
     Call
-      Ident !"foo"
+      Ident !"foo310"
       Ident !"x"
   Asgn
     Ident !"a"
     Command
-      Ident !"foo"
+      Ident !"foo320"
       Ident !"x"
   Asgn
     Ident !"a"
     Call
-      Ident !"foo"
+      Ident !"foo330"
       StmtList
         DiscardStmt
           Empty
   Asgn
     Ident !"a"
     Call
-      Ident !"foo"
+      Ident !"foo340"
       StmtList
         DiscardStmt
           Empty
   Asgn
     Ident !"a"
     Call
-      Ident !"foo"
+      Ident !"foo350"
       StmtList
         DiscardStmt
           Empty
@@ -350,30 +362,42 @@ StmtList
   Asgn
     Ident !"a"
     Command
-      Ident !"foo"
-      Ident !"x"
-      Do
-        Empty
-        Empty
-        Empty
-        FormalParams
+      Ident !"foo360"
+      Call
+        DotExpr
+          Ident !"x"
+          Ident !"bar"
+        Do
           Empty
-          IdentDefs
-            Ident !"y"
-            Empty
-            Empty
-        Empty
-        Empty
-        StmtList
-          DiscardStmt
-            Empty
-      Else
-        StmtList
-          DiscardStmt
+          Empty
+          Empty
+          FormalParams
             Empty
+            IdentDefs
+              Ident !"y"
+              Empty
+              Empty
+          Empty
+          Empty
+          StmtList
+            DiscardStmt
+              Empty
+        Else
+          StmtList
+            DiscardStmt
+              Empty
+  Command
+    DotExpr
+      Ident !"foo370"
+      Ident !"add"
+    Call
+      Ident !"quote"
+      StmtList
+        DiscardStmt
+          Empty
   Call
     DotExpr
-      Ident !"result"
+      Ident !"foo380"
       Ident !"add"
     BracketExpr
       Call
@@ -389,62 +413,65 @@ import macros
 
 dumpTree:
   # simple calls
-  foo
-  foo()
-  foo(x)
-  foo x
+  foo010
+  foo020()
+  foo030(x)
+  foo040 x
+
+  foo050:
+    discard
 
-  foo:
+  foo060 do:
     discard
 
-  foo do:
+  foo070("test"):
     discard
 
-  foo("test"):
+  foo080("test") do:
     discard
 
-  foo("test") do:
+  foo090 "test":
     discard
 
-  foo "test":
+  foo100 "test" do:
     discard
 
-  foo "test" do:
+  foo101 10 do:
     discard
 
   # more complicated calls
-  foo 1, (2+3):
+  foo110 1, (2+3):
     discard
 
-  foo 1, (2+3) do:
+  foo120 1, (2+3) do:
     discard
 
-  foo do (x):
+  foo130 do (x):
     discard
 
-  foo do (x: int):
+  foo140 do (x: int):
     discard
 
-  foo do (x: int) -> int:
+  foo150 do (x: int) -> int:
     discard
 
-  foo x do (y):
+  foo160 x do (y):
     discard
 
   # extra blocks
-  foo:
+  foo170:
     discard
   else:
     discard
 
-  foo do:
+  foo180 do:
     discard
   do:
     discard
   else:
     discard
 
-  foo x do (y):
+  foo190 x do (y):
     discard
   do (z) -> int:
     discard
@@ -456,58 +483,61 @@ dumpTree:
     discard
 
   # call with blocks as a param
-  foo(x, bar do:
+  foo200(x, bar do:
     discard
   else:
     discard
   )
 
   # introduce a variable
-  var a = foo
-  var a = foo()
-  var a = foo(x)
-  var a = foo x
+  var a = foo210
+  var a = foo220()
+  var a = foo230(x)
+  var a = foo240 x
 
-  var a = foo:
+  var a = foo250:
     discard
 
-  var a = foo do:
+  var a = foo260 do:
     discard
 
-  var a = foo do:
+  var a = foo270 do:
     discard
   else:
     discard
 
-  var a = foo x do (y):
+  var a = foo280 x do (y):
     discard
   else:
     discard
 
   # assignments
-  a = foo
-  a = foo()
-  a = foo(x)
-  a = foo x
+  a = foo290
+  a = foo300()
+  a = foo310(x)
+  a = foo320 x
 
-  a = foo:
+  a = foo330:
     discard
 
-  a = foo do:
+  a = foo340 do:
     discard
 
-  a = foo do:
+  a = foo350 do:
     discard
   else:
     discard
 
-  a = foo x do (y):
+  a = foo360 x.bar do (y):
     discard
   else:
     discard
 
+  foo370.add quote do:
+    discard
+
   # some edge cases
-  result.add((quote do:
+  foo380.add((quote do:
     discard
   )[0])
 
diff --git a/tests/parser/tstrongspaces.nim b/tests/parser/tstrongspaces.nim
index cb0219976..adab7f709 100644
--- a/tests/parser/tstrongspaces.nim
+++ b/tests/parser/tstrongspaces.nim
@@ -40,9 +40,9 @@ echo $foo
 
 echo (1, 2, 2)
 
-template `&`(a, b: int): expr = a and b
-template `|`(a, b: int): expr = a - b
-template `++`(a, b: int): expr = a + b == 8009
+template `&`(a, b: int): int = a and b
+template `|`(a, b: int): int = a - b
+template `++`(a, b: int): bool = a + b == 8009
 
 when true:
   let b = 66
@@ -62,7 +62,7 @@ when true:
   echo booA == booB
 
 
-template `|`(a, b): expr = (if a.len > 0: a else: b)
+template `|`(a, b): untyped = (if a.len > 0: a else: b)
 
 const
   tester = "tester"
@@ -74,7 +74,7 @@ echo "all"|tester & " " & args
 
 # Test arrow like operators. See also tests/macros/tclosuremacro.nim
 proc `+->`(a, b: int): int = a + b*4
-template `===>`(a, b: int): expr = a - b shr 1
+template `===>`(a, b: int): int = a - b shr 1
 
 echo 3 +-> 2 + 2 and 4
 var arrowed = 3+->2 + 2 and 4  # arrowed = 4
diff --git a/tests/pragmas/tsym_as_pragma.nim b/tests/pragmas/tsym_as_pragma.nim
index f6ba44dc9..788311244 100644
--- a/tests/pragmas/tsym_as_pragma.nim
+++ b/tests/pragmas/tsym_as_pragma.nim
@@ -1,7 +1,7 @@
 
 # bug #3171
 
-template newDataWindow(): stmt =
+template newDataWindow(): untyped =
     let eventClosure = proc (closure: pointer): bool {.closure, cdecl.} =
         discard
 
diff --git a/tests/pragmas/tused.nim b/tests/pragmas/tused.nim
index 389863aef..83c62b7bb 100644
--- a/tests/pragmas/tused.nim
+++ b/tests/pragmas/tused.nim
@@ -1,7 +1,7 @@
 discard """
   nimout: '''
 compile start
-tused.nim(15, 8) Hint: 'tused.echoSub(a: int, b: int)[declared in tused.nim(15,7)]' is declared but not used [XDeclaredButNotUsed]
+tused.nim(15, 8) Hint: 'tused.echoSub(a: int, b: int)[declared in tused.nim(15, 7)]' is declared but not used [XDeclaredButNotUsed]
 compile end'''
   output: "8\n8"
 """
diff --git a/tests/range/compilehelpers.nim b/tests/range/compilehelpers.nim
index cb26ca5b5..08f97a5bf 100644
--- a/tests/range/compilehelpers.nim
+++ b/tests/range/compilehelpers.nim
@@ -1,6 +1,5 @@
-template accept(e: expr) =
+template accept(e) =
   static: assert(compiles(e))
 
-template reject(e: expr) =
+template reject(e) =
   static: assert(not compiles(e))
-
diff --git a/tests/readme.md b/tests/readme.md
new file mode 100644
index 000000000..34a2c4bfb
--- /dev/null
+++ b/tests/readme.md
@@ -0,0 +1,62 @@
+This directory contains the test cases.
+
+Each test must have a filename of the form: ``t*.nim``
+
+**Note:** Tests are only compiled by default. In order to get the tester to
+execute the compiled binary, you need to specify a spec with an ``action`` key
+(see below for details).
+
+# Specs
+
+Each test can contain a spec in a ``discard """ ... """`` block.
+
+**Check out the [``parseSpec`` procedure](https://github.com/nim-lang/Nim/blob/devel/tests/testament/specs.nim#L124) in the ``specs`` module for a full and reliable reference**
+
+## action
+
+Specifies what action this test should take.
+
+**Default: compile**
+
+Options:
+
+* ``compile`` - compiles the module and fails the test if compilations fails.
+* ``run`` - compiles and runs the module, fails the test if compilation or
+            execution of test code fails.
+* ``reject`` - compiles the module and fails the test if compilation succeeds.
+
+There are certain spec keys that imply ``run``, including ``output`` and
+``outputsub``.
+
+## cmd
+
+Specifies the Nim command to use for compiling the test.
+
+There are a number of variables that are replaced in this spec option:
+
+* ``$target`` - the compilation target, e.g. ``c``.
+* ``$options`` - the options for the compiler.
+* ``$file`` - the filename of the test.
+* ``$filedir`` - the directory of the test file.
+
+Example:
+
+```nim
+discard """
+  cmd: "nim $target --nimblePath:./nimbleDir/simplePkgs $options $file"
+"""
+```
+
+# Categories
+
+Each folder under this directory represents a test category, which can be
+tested by running `koch tests cat <category>`.
+
+The folder ``rodfiles`` contains special tests that test incremental
+compilation via symbol files.
+
+The folder ``dll`` contains simple DLL tests.
+
+The folder ``realtimeGC`` contains a test for validating that the realtime GC
+can run properly without linking against the nimrtl.dll/so. It includes a C
+client and platform specific build files for manual compilation.
diff --git a/tests/readme.txt b/tests/readme.txt
deleted file mode 100644
index 0ff9e11c6..000000000
--- a/tests/readme.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-This directory contains the test cases.
-Each test must have a filename of the form: ``t*.nim``
-
-Each test can contain a spec in a ``discard """"""`` block.
-
-The folder ``rodfiles`` contains special tests that test incremental
-compilation via symbol files.
-
-The folder ``dll`` contains simple DLL tests.
-
-The folder ``realtimeGC`` contains a test for validating that the realtime GC
-can run properly without linking against the nimrtl.dll/so. It includes a C
-client and platform specific build files for manual compilation.
diff --git a/tests/showoff/tdrdobbs_examples.nim b/tests/showoff/tdrdobbs_examples.nim
index 78f711325..8687ee529 100644
--- a/tests/showoff/tdrdobbs_examples.nim
+++ b/tests/showoff/tdrdobbs_examples.nim
@@ -105,10 +105,10 @@ proc pat2kind(pattern: string): FormulaKind =
 import macros
 
 proc matchAgainst(n, pattern: NimNode): NimNode {.compileTime.} =
-  template `@`(current, field: expr): expr =
+  template `@`(current, field: untyped): untyped =
     newDotExpr(current, newIdentNode(astToStr(field)))
 
-  template `==@`(n, pattern: expr): expr =
+  template `==@`(n, pattern: untyped): untyped =
     newCall("==", n@kind, newIdentNode($pat2kind($pattern.ident)))
 
   case pattern.kind
@@ -126,7 +126,7 @@ proc matchAgainst(n, pattern: NimNode): NimNode {.compileTime.} =
   else:
     error "invalid pattern"
 
-macro `=~` (n: Formula, pattern: expr): bool =
+macro `=~` (n: Formula, pattern: untyped): bool =
   result = matchAgainst(n, pattern)
 
 proc isPolyTerm2(n: Formula): bool = n =~ c * x^c
diff --git a/tests/showoff/tformatopt.nim b/tests/showoff/tformatopt.nim
index f33ed6921..6e790c38e 100644
--- a/tests/showoff/tformatopt.nim
+++ b/tests/showoff/tformatopt.nim
@@ -10,7 +10,7 @@ import macros
 proc invalidFormatString() =
   echo "invalidFormatString"
 
-template formatImpl(handleChar: expr) =
+template formatImpl(handleChar: untyped) =
   var i = 0
   while i < f.len:
     if f[i] == '$':
@@ -29,11 +29,11 @@ template formatImpl(handleChar: expr) =
       i += 1
 
 proc `%`*(f: string, a: openArray[string]): string =
-  template identity(x: expr): expr = x
+  template identity(x: untyped): untyped = x
   result = ""
   formatImpl(identity)
 
-macro optFormat{`%`(f, a)}(f: string{lit}, a: openArray[string]): expr =
+macro optFormat{`%`(f, a)}(f: string{lit}, a: openArray[string]): untyped =
   result = newNimNode(nnkBracket)
   let f = f.strVal
   formatImpl(newLit)
diff --git a/tests/showoff/thtml1.nim b/tests/showoff/thtml1.nim
index c7a083c21..fe0cd3b1e 100644
--- a/tests/showoff/thtml1.nim
+++ b/tests/showoff/thtml1.nim
@@ -2,7 +2,7 @@ discard """
   output: "<br>"
 """
 
-template htmlTag(tag: expr) {.immediate.} =
+template htmlTag(tag: untyped) =
   proc tag(): string = "<" & astToStr(tag) & ">"
 
 htmlTag(br)
diff --git a/tests/showoff/thtml2.nim b/tests/showoff/thtml2.nim
index faeb4e50d..dcf6534a5 100644
--- a/tests/showoff/thtml2.nim
+++ b/tests/showoff/thtml2.nim
@@ -4,20 +4,20 @@ discard """
 
 import strutils
 
-template html(name: expr, matter: stmt) {.immediate.} =
+template html(name, matter: untyped) =
   proc name(): string =
     result = "<html>"
     matter
     result.add("</html>")
 
-template nestedTag(tag: expr) {.immediate.} =
-  template tag(matter: stmt) {.immediate.} =
+template nestedTag(tag: untyped) =
+  template tag(matter: untyped) =
     result.add("<" & astToStr(tag) & ">")
     matter
     result.add("</" & astToStr(tag) & ">")
 
-template simpleTag(tag: expr) {.immediate.} =
-  template tag(matter: expr) {.immediate.} =
+template simpleTag(tag: untyped) =
+  template tag(matter: untyped) =
     result.add("<$1>$2</$1>" % [astToStr(tag), matter])
 
 nestedTag body
diff --git a/tests/showoff/tonce.nim b/tests/showoff/tonce.nim
index 6fc372e87..ed2684dcf 100644
--- a/tests/showoff/tonce.nim
+++ b/tests/showoff/tonce.nim
@@ -5,7 +5,7 @@ new instantiation
 some call of p'''
 """
 
-template once(body: stmt) =
+template once(body) =
   var x {.global.} = false
   if not x:
     x = true
diff --git a/tests/showoff/tquasiquote.nim b/tests/showoff/tquasiquote.nim
index df7fccc33..404712a02 100644
--- a/tests/showoff/tquasiquote.nim
+++ b/tests/showoff/tquasiquote.nim
@@ -1,10 +1,10 @@
 discard """
-  outputsub: '''tquasiquote.nim(14,8): Check failed: 1 > 2'''
+  outputsub: '''tquasiquote.nim(14, 8): Check failed: 1 > 2'''
 """
 
 import macros
 
-macro check(ex: expr): stmt =
+macro check(ex: untyped): untyped =
   var info = ex.lineInfo
   var expString = ex.toStrLit
   result = quote do:
diff --git a/tests/specialops/tdotops.nim b/tests/specialops/tdotops.nim
index ce5b3942d..bca949922 100644
--- a/tests/specialops/tdotops.nim
+++ b/tests/specialops/tdotops.nim
@@ -31,7 +31,7 @@ proc `.=`(x: var T1, f: string{lit}, v: int) =
   echo "assigning ", f, " = ", v
   x.x = v
 
-template `.()`(x: T1, f: string, args: varargs[expr]): string =
+template `.()`(x: T1, f: string, args: varargs[typed]): string =
   echo "call to ", f
   "dot call"
 
@@ -63,4 +63,3 @@ echo tt.c(10)
 
 assert(not compiles(tt.d("x")))
 assert(not compiles(tt.d(1, 2)))
-
diff --git a/tests/stdlib/tlocks.nim b/tests/stdlib/tlocks.nim
new file mode 100644
index 000000000..e567cfde8
--- /dev/null
+++ b/tests/stdlib/tlocks.nim
@@ -0,0 +1,10 @@
+discard """
+  output: '''3'''
+  cmd: "nim $target --threads:on $options $file"
+"""
+
+#bug #6049
+import uselocks
+
+var m = createMyType[int]()
+echo $m.use()
diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim
index 6a53a2964..434caa281 100644
--- a/tests/stdlib/tmarshal.nim
+++ b/tests/stdlib/tmarshal.nim
@@ -9,7 +9,7 @@ omega 200
 
 import marshal
 
-template testit(x: expr) = discard $$to[type(x)]($$x)
+template testit(x) = discard $$to[type(x)]($$x)
 
 var x: array[0..4, array[0..4, string]] = [
   ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
diff --git a/tests/stdlib/tnre.nim b/tests/stdlib/tnre.nim
index 030319ebf..fabbb69a8 100644
--- a/tests/stdlib/tnre.nim
+++ b/tests/stdlib/tnre.nim
@@ -20,12 +20,13 @@ discard """
 
 [Suite] Misc tests'''
 """
+
 import nre
-import nre.init
-import nre.captures
-import nre.find
-import nre.split
-import nre.match
-import nre.replace
-import nre.escape
-import nre.misc
+import nre/init
+import nre/captures
+import nre/find
+import nre/split
+import nre/match
+import nre/replace
+import nre/escape
+import nre/misc
diff --git a/tests/stdlib/tparsecfg.nim b/tests/stdlib/tparsecfg.nim
new file mode 100644
index 000000000..1c214e5d2
--- /dev/null
+++ b/tests/stdlib/tparsecfg.nim
@@ -0,0 +1,23 @@
+discard """
+  output: '''OK'''
+"""
+
+#bug #6046
+import parsecfg
+
+var config = newConfig()
+config.setSectionKey("foo","bar","-1")
+config.setSectionKey("foo","foo","abc")
+config.writeConfig("test.ini")
+
+# test.ini now contains
+# [foo]
+# bar=-1
+# foo=abc
+
+var config2 = loadConfig("test.ini")
+let bar = config2.getSectionValue("foo","bar")
+let foo = config2.getSectionValue("foo","foo")
+assert(bar == "-1")
+assert(foo == "abc")
+echo "OK"
diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim
index ec839e288..e2a5a1715 100644
--- a/tests/stdlib/tpegs.nim
+++ b/tests/stdlib/tpegs.nim
@@ -149,7 +149,7 @@ proc addChoice(dest: var TPeg, elem: TPeg) =
     else: add(dest, elem)
   else: add(dest, elem)
 
-template multipleOp(k: TPegKind, localOpt: expr) =
+template multipleOp(k: TPegKind, localOpt) =
   result.kind = k
   result.sons = @[]
   for x in items(a):
@@ -350,32 +350,32 @@ proc newNonTerminal*(name: string, line, column: int): PNonTerminal {.
   result.line = line
   result.col = column
 
-template letters*: expr =
+template letters*: TPeg =
   ## expands to ``charset({'A'..'Z', 'a'..'z'})``
   charset({'A'..'Z', 'a'..'z'})
 
-template digits*: expr =
+template digits*: TPeg =
   ## expands to ``charset({'0'..'9'})``
   charset({'0'..'9'})
 
-template whitespace*: expr =
+template whitespace*: TPeg =
   ## expands to ``charset({' ', '\9'..'\13'})``
   charset({' ', '\9'..'\13'})
 
-template identChars*: expr =
+template identChars*: TPeg =
   ## expands to ``charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})``
   charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})
 
-template identStartChars*: expr =
+template identStartChars*: TPeg =
   ## expands to ``charset({'A'..'Z', 'a'..'z', '_'})``
   charset({'a'..'z', 'A'..'Z', '_'})
 
-template ident*: expr =
+template ident*: TPeg =
   ## same as ``[a-zA-Z_][a-zA-z_0-9]*``; standard identifier
   sequence(charset({'a'..'z', 'A'..'Z', '_'}),
            *charset({'a'..'z', 'A'..'Z', '0'..'9', '_'}))
 
-template natural*: expr =
+template natural*: TPeg =
   ## same as ``\d+``
   +digits
 
@@ -534,10 +534,10 @@ proc bounds*(c: TCaptures,
 when not useUnicode:
   type
     Rune = char
-  template fastRuneAt(s, i, ch: expr) =
+  template fastRuneAt(s, i, ch) =
     ch = s[i]
     inc(i)
-  template runeLenAt(s, i: expr): expr = 1
+  template runeLenAt(s, i): untyped = 1
 
   proc isAlpha(a: char): bool {.inline.} = return a in {'a'..'z','A'..'Z'}
   proc isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'}
@@ -847,7 +847,7 @@ proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
   ## If it does not match, @[] is returned.
   accumulateResult(findAll(s, pattern, start))
 
-template `=~`*(s: string, pattern: TPeg): expr =
+template `=~`*(s: string, pattern: TPeg): untyped =
   ## This calls ``match`` with an implicit declared ``matches`` array that
   ## can be used in the scope of the ``=~`` call:
   ##
diff --git a/tests/stdlib/uselocks.nim b/tests/stdlib/uselocks.nim
new file mode 100644
index 000000000..cde9641b2
--- /dev/null
+++ b/tests/stdlib/uselocks.nim
@@ -0,0 +1,11 @@
+import locks
+
+type MyType* [T] = object
+  lock: Lock
+
+proc createMyType*[T]: MyType[T] =
+  initLock(result.lock)
+
+proc use* (m: var MyType): int =
+  withLock m.lock:
+    result = 3
diff --git a/tests/template/annotate.nim b/tests/template/annotate.nim
index 5f395557b..a7e2f8fdb 100644
--- a/tests/template/annotate.nim
+++ b/tests/template/annotate.nim
@@ -1,7 +1,7 @@
 import macros, parseutils
 
 # Generate tags
-macro make(names: openarray[expr]): stmt {.immediate.} =
+macro make(names: untyped{nkBracket}): untyped =
     result = newStmtList()
 
     for i in 0 .. names.len-1:
diff --git a/tests/template/mcan_access_hidden_field.nim b/tests/template/mcan_access_hidden_field.nim
index bf3592701..2c0026ec1 100644
--- a/tests/template/mcan_access_hidden_field.nim
+++ b/tests/template/mcan_access_hidden_field.nim
@@ -5,5 +5,4 @@ type
 
 proc createFoo*(a, b: int): Foo = Foo(fooa: a, foob: b)
 
-template geta*(f: Foo): expr = f.fooa
-
+template geta*(f: Foo): untyped = f.fooa
diff --git a/tests/template/t2do.nim b/tests/template/t2do.nim
index ec364c5f3..f5f6393dc 100644
--- a/tests/template/t2do.nim
+++ b/tests/template/t2do.nim
@@ -7,7 +7,7 @@ discard """
 proc mpf_get_d(x: int): float = float(x)
 proc mpf_cmp_d(a: int; b: float): int = 0
 
-template toFloatHelper(result: expr; tooSmall, tooLarge: stmt) {.immediate.} =
+template toFloatHelper(result, tooSmall, tooLarge: untyped) =
   result = mpf_get_d(a)
   if result == 0.0 and mpf_cmp_d(a,0.0) != 0:
     tooSmall
diff --git a/tests/template/t_otemplates.nim b/tests/template/t_otemplates.nim
index 6c419f72f..6597bd37a 100644
--- a/tests/template/t_otemplates.nim
+++ b/tests/template/t_otemplates.nim
@@ -126,7 +126,7 @@ iterator parse_compound_statements(value, identifier: string, index: int): strin
     ## and returns the initialization of each as an empty statement
     ## i.e. if x == 5 { ... } becomes if x == 5: nil.
 
-    template get_next_ident(expected): stmt =
+    template get_next_ident(expected) =
         var nextIdent: string
         discard value.parseWhile(nextIdent, {'$'} + identChars, i)
 
@@ -316,10 +316,10 @@ proc parse_template(node: NimNode, value: string) =
     ## Nim code into the input `node` AST.
     var index = 0
     while index < value.len and
-          parse_until_symbol(node, value, index): nil
+          parse_until_symbol(node, value, index): discard
 
 
-macro tmpli*(body: expr): stmt =
+macro tmpli*(body: untyped): untyped =
     result = newStmtList()
 
     result.add parseExpr("result = \"\"")
@@ -330,7 +330,7 @@ macro tmpli*(body: expr): stmt =
     parse_template(result, reindent(value))
 
 
-macro tmpl*(body: expr): stmt =
+macro tmpl*(body: untyped): untyped =
     result = newStmtList()
 
     var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal
diff --git a/tests/template/tdefault_nil.nim b/tests/template/tdefault_nil.nim
index 891166306..c5c372d9e 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): expr =
+template glob_rst(basedir: string = nil): untyped =
   if baseDir.isNil:
     to_seq(walk_files("*.rst"))
   else:
diff --git a/tests/template/thygienictempl.nim b/tests/template/thygienictempl.nim
index 5e4f534f8..de40450aa 100644
--- a/tests/template/thygienictempl.nim
+++ b/tests/template/thygienictempl.nim
@@ -2,7 +2,7 @@
 var
   e = "abc"
 
-raise newException(EIO, e & "ha!")
+raise newException(IOError, e & "ha!")
 
 template t() = echo(foo)
 
@@ -10,7 +10,7 @@ var foo = 12
 t()
 
 
-template test_in(a, b, c: expr): bool {.immediate, dirty.} =
+template test_in(a, b, c: untyped): bool {.dirty.} =
   var result {.gensym.}: bool = false
   false
 
diff --git a/tests/template/tissue909.nim b/tests/template/tissue909.nim
index 5b57a3558..6786ff48c 100644
--- a/tests/template/tissue909.nim
+++ b/tests/template/tissue909.nim
@@ -8,7 +8,7 @@ template baz() =
     var y = foo
     discard y()
 
-macro test(): stmt =
+macro test(): untyped =
   result = getAst(baz())
   echo(treeRepr(result))
 
diff --git a/tests/template/tissue993.nim b/tests/template/tissue993.nim
index dae9df683..552890bb4 100644
--- a/tests/template/tissue993.nim
+++ b/tests/template/tissue993.nim
@@ -1,15 +1,15 @@
 
 type PNode* = ref object of RootObj
 
-template litNode (name, ty): stmt  =
+template litNode(name, ty)  =
   type name* = ref object of PNode
     val*: ty
 litNode PIntNode, int
 
 import json
 
-template withKey*(j: JsonNode; key: string; varname: expr;
-                  body:stmt): stmt {.immediate.} =
+template withKey*(j: JsonNode; key: string; varname,
+                  body: untyped): typed =
   if j.hasKey(key):
     let varname{.inject.}= j[key]
     block:
@@ -18,4 +18,3 @@ template withKey*(j: JsonNode; key: string; varname: expr;
 var j = parsejson("{\"zzz\":1}")
 withkey(j, "foo", x):
   echo(x)
-
diff --git a/tests/template/tit.nim b/tests/template/tit.nim
index cf50d2f6f..76b1d151b 100644
--- a/tests/template/tit.nim
+++ b/tests/template/tit.nim
@@ -1,7 +1,7 @@
 
 # bug #1337
 
-template someIt(a, pred: expr): expr =
+template someIt(a, pred): untyped =
   var it {.inject.} = 0
   pred
 
diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim
index 568725fd4..3fb0dd4a5 100644
--- a/tests/template/tparams_gensymed.nim
+++ b/tests/template/tparams_gensymed.nim
@@ -43,7 +43,7 @@ template forStatic(index, slice, predicate: untyped) =
       block:
         const index = i
         predicate
-    template iterateStartingFrom(i: int): stmt =
+    template iterateStartingFrom(i: int) =
       when i <= b:
         iteration i
         iterateStartingFrom i + 1
diff --git a/tests/template/tprefer_immediate.nim b/tests/template/tprefer_immediate.nim
index 578f447b0..3a4cfc07b 100644
--- a/tests/template/tprefer_immediate.nim
+++ b/tests/template/tprefer_immediate.nim
@@ -4,14 +4,12 @@ discard """
 
 # Test that immediate templates are preferred over non-immediate templates
 
-template foo(a, b: expr) = echo "foo expr"
-
+template foo(a, b: untyped) = echo "foo expr"
 template foo(a, b: int) = echo "foo int"
 template foo(a, b: float) = echo "foo float"
 template foo(a, b: string) = echo "foo string"
-template foo(a, b: expr) {.immediate.} = echo "immediate"
+template foo(a, b: untyped) {.immediate.} = echo "immediate"
 template foo(a, b: bool) = echo "foo bool"
 template foo(a, b: char) = echo "foo char"
 
 foo(undeclaredIdentifier, undeclaredIdentifier2)
-
diff --git a/tests/template/tscope.nim b/tests/template/tscope.nim
index 2d5841af3..1eeebbdd4 100644
--- a/tests/template/tscope.nim
+++ b/tests/template/tscope.nim
@@ -3,7 +3,7 @@ discard """
 """
 
 var x = 1
-template quantity(): stmt {.immediate.} =
+template quantity() =
   # Causes internal error in compiler/sem.nim
   proc unit*(x = 1.0): float = 12
   # Throws the correct error: redefinition of 'x'
diff --git a/tests/template/tstmt_semchecked_twice.nim b/tests/template/tstmt_semchecked_twice.nim
index 05c16c3c9..c6463ae06 100644
--- a/tests/template/tstmt_semchecked_twice.nim
+++ b/tests/template/tstmt_semchecked_twice.nim
@@ -13,7 +13,7 @@ type
     Vector2[T] = T
     Pixels=int
 
-template use*(fb: int, st: stmt) : stmt =
+template use*(fb: int, st: untyped): untyped =
     echo "a ", $fb
     st
     echo "a ", $fb
diff --git a/tests/template/tsymchoicefield.nim b/tests/template/tsymchoicefield.nim
index ab05500bf..4483c2aa2 100644
--- a/tests/template/tsymchoicefield.nim
+++ b/tests/template/tsymchoicefield.nim
@@ -3,10 +3,9 @@ type Foo = object
 
 var f = Foo(len: 40)
 
-template getLen(f: Foo): expr = f.len
+template getLen(f: Foo): int = f.len
 
 echo f.getLen
 # This fails, because `len` gets the nkOpenSymChoice
 # treatment inside the template early pass and then
 # it can't be recognized as a field anymore
-
diff --git a/tests/template/ttempl3.nim b/tests/template/ttempl3.nim
index 56daf9fe6..91d416c48 100644
--- a/tests/template/ttempl3.nim
+++ b/tests/template/ttempl3.nim
@@ -1,6 +1,6 @@
 
-template withOpenFile(f: expr, filename: string, mode: TFileMode,
-                      actions: stmt): stmt {.immediate.} =
+template withOpenFile(f: untyped, filename: string, mode: TFileMode,
+                      actions: untyped): untyped =
   block:
     # test that 'f' is implicitly 'injecting':
     var f: TFile
@@ -20,20 +20,20 @@ var
   myVar: array[0..1, int]
 
 # Test zero argument template:
-template ha: expr = myVar[0]
+template ha: untyped = myVar[0]
 
 ha = 1
 echo(ha)
 
 
 # Test identifier generation:
-template prefix(name: expr): expr {.immediate.} = `"hu" name`
+template prefix(name): untyped = `"hu" name`
 
 var `hu "XYZ"` = "yay"
 
 echo prefix(XYZ)
 
-template typedef(name: expr, typ: typeDesc) {.immediate, dirty.} =
+template typedef(name: untyped, typ: typeDesc) {.immediate, dirty.} =
   type
     `T name`* = typ
     `P name`* = ref `T name`
@@ -51,7 +51,7 @@ type
 proc initFoo(arg: int): Foo =
   result.arg = arg
 
-template create(typ: typeDesc, arg: expr): expr = `init typ`(arg)
+template create(typ: typeDesc, arg: untyped): untyped = `init typ`(arg)
 
 var ff = Foo.create(12)
 
diff --git a/tests/template/ttempl4.nim b/tests/template/ttempl4.nim
index 26c82e471..d1d26385f 100644
--- a/tests/template/ttempl4.nim
+++ b/tests/template/ttempl4.nim
@@ -1,8 +1,7 @@
 
-template `:=`(name, val: expr): stmt {.immediate.} =
+template `:=`(name, val: untyped): typed =
   var name = val
 
 ha := 1 * 4
 hu := "ta-da" == "ta-da"
 echo ha, hu
-
diff --git a/tests/template/ttempl5.nim b/tests/template/ttempl5.nim
index a020a8e2c..fd3ea0cad 100644
--- a/tests/template/ttempl5.nim
+++ b/tests/template/ttempl5.nim
@@ -9,7 +9,7 @@ proc parse_to_close(value: string, index: int, open='(', close=')'): int =
     discard
 
 # Call parse_to_close
-template get_next_ident: stmt =
+template get_next_ident: typed =
     discard "{something}".parse_to_close(0, open = '{', close = '}')
 
 get_next_ident()
@@ -19,11 +19,10 @@ get_next_ident()
 
 #bug #880 (also example in the manual!)
 
-template typedef(name: expr, typ: typedesc) {.immediate.} =
+template typedef(name: untyped, typ: typedesc) =
   type
     `T name`* {.inject.} = typ
     `P name`* {.inject.} = ref `T name`
 
 typedef(myint, int)
 var x: PMyInt
-
diff --git a/tests/template/twhen_gensym.nim b/tests/template/twhen_gensym.nim
index d84ee6f03..f1a8d0eb7 100644
--- a/tests/template/twhen_gensym.nim
+++ b/tests/template/twhen_gensym.nim
@@ -3,7 +3,7 @@ discard """
 """
 
 # bug #2670
-template testTemplate(b: bool): stmt =
+template testTemplate(b: bool): typed =
     when b:
         var a = "hi"
     else:
diff --git a/tests/template/twrongmapit.nim b/tests/template/twrongmapit.nim
index df695fcd6..cbc63b9cd 100644
--- a/tests/template/twrongmapit.nim
+++ b/tests/template/twrongmapit.nim
@@ -8,7 +8,7 @@ discard """
 type Foo* {.pure, final.} = object
   elt: float
 
-template defineOpAssign(T: expr, op: expr) {.immediate.} =
+template defineOpAssign(T, op: untyped) {.immediate.} =
   proc op*(v: var T, w: T) {.inline.} =
     for i in 0..1:
       op(v.elt, w.elt)
@@ -18,7 +18,7 @@ const ATTEMPT = 0
 when ATTEMPT == 0:
   # FAILS: defining `/=` with template calling template
   # ERROR about sem.nim line 144
-  template defineOpAssigns(T: expr) {.immediate.} =
+  template defineOpAssigns(T: untyped) =
     mixin `/=`
     defineOpAssign(T, `/=`)
 
diff --git a/tests/template/twrongsymkind.nim b/tests/template/twrongsymkind.nim
index be3d8c652..5fa618914 100644
--- a/tests/template/twrongsymkind.nim
+++ b/tests/template/twrongsymkind.nim
@@ -9,7 +9,7 @@ type
   MyData = object
       x: int
 
-template newDataWindow(data: ref MyData): stmt =
+template newDataWindow(data: ref MyData): untyped =
     proc testProc(data: ref MyData) =
         echo "Hello, ", data.x
     testProc(data)
diff --git a/tests/template/utemplates.nim b/tests/template/utemplates.nim
index 8b9ae5d26..199268046 100644
--- a/tests/template/utemplates.nim
+++ b/tests/template/utemplates.nim
@@ -1,32 +1,31 @@
 import unittest
 
-template t(a: int): expr = "int"
-template t(a: string): expr = "string"
+template t(a: int): string = "int"
+template t(a: string): string = "string"
 
 test "templates can be overloaded":
   check t(10) == "int"
   check t("test") == "string"
 
 test "previous definitions can be further overloaded or hidden in local scopes":
-  template t(a: bool): expr = "bool"
+  template t(a: bool): string = "bool"
 
   check t(true) == "bool"
   check t(10) == "int"
 
-  template t(a: int): expr = "inner int"
+  template t(a: int): string = "inner int"
   check t(10) == "inner int"
   check t("test") == "string"
 
 test "templates can be redefined multiple times":
-  template customAssert(cond: bool, msg: string): stmt {.immediate, dirty.} =
+  template customAssert(cond: bool, msg: string): typed {.immediate, dirty.} =
     if not cond: fail(msg)
 
-  template assertion_failed(body: stmt) {.immediate, dirty.} =
-    template fail(msg: string): stmt = body
+  template assertion_failed(body: typed) {.immediate, dirty.} =
+    template fail(msg: string): typed = body
 
   assertion_failed: check msg == "first fail path"
   customAssert false, "first fail path"
 
   assertion_failed: check msg == "second fail path"
   customAssert false, "second fail path"
-
diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim
index 4ba07cd21..7b1dd0df0 100644
--- a/tests/testament/categories.nim
+++ b/tests/testament/categories.nim
@@ -139,7 +139,7 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
                   " -d:release --gc:markAndSweep", cat, actionRun)
   template test(filename: untyped) =
     testWithoutBoehm filename
-    when not defined(windows):
+    when not defined(windows) and not defined(android):
       # AR: cannot find any boehm.dll on the net, right now, so disabled
       # for windows:
       testSpec r, makeTest("tests/gc" / filename, options &
diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim
index b83eb668a..e4bbc3a00 100644
--- a/tests/testament/tester.nim
+++ b/tests/testament/tester.nim
@@ -64,12 +64,22 @@ let
 
 var targets = {low(TTarget)..high(TTarget)}
 
-proc normalizeMsg(s: string): string = s.strip.replace("\C\L", "\L")
+proc normalizeMsg(s: string): string =
+  result = newStringOfCap(s.len+1)
+  for x in splitLines(s):
+    if result.len > 0: result.add '\L'
+    result.add x.strip
+
+proc getFileDir(filename: string): string =
+  result = filename.splitFile().dir
+  if not result.isAbsolute():
+    result = getCurrentDir() / result
 
 proc callCompiler(cmdTemplate, filename, options: string,
                   target: TTarget): TSpec =
   let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
-                       "options", options, "file", filename.quoteShell])
+                       "options", options, "file", filename.quoteShell,
+                       "filedir", filename.getFileDir()])
   var p = startProcess(command=c[0], args=c[1.. ^1],
                        options={poStdErrToStdOut, poUsePath})
   let outp = p.outputStream
@@ -114,7 +124,8 @@ proc callCompiler(cmdTemplate, filename, options: string,
 proc callCCompiler(cmdTemplate, filename, options: string,
                   target: TTarget): TSpec =
   let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
-                       "options", options, "file", filename.quoteShell])
+                       "options", options, "file", filename.quoteShell,
+                       "filedir", filename.getFileDir()])
   var p = startProcess(command="gcc", args=c[5.. ^1],
                        options={poStdErrToStdOut, poUsePath})
   let outp = p.outputStream
diff --git a/tests/trmacros/targlist.nim b/tests/trmacros/targlist.nim
index f9d2cb6c6..46235dab1 100644
--- a/tests/trmacros/targlist.nim
+++ b/tests/trmacros/targlist.nim
@@ -3,7 +3,7 @@ discard """
 """
 
 proc f(x: varargs[string, `$`]) = discard
-template optF{f(x)}(x: varargs[expr]) =
+template optF{f(x)}(x: varargs[untyped]) =
   writeLine(stdout, x)
 
 f 1, 2, false, 3, "ha"
diff --git a/tests/trmacros/tcse.nim b/tests/trmacros/tcse.nim
index 023a8f298..315570d8f 100644
--- a/tests/trmacros/tcse.nim
+++ b/tests/trmacros/tcse.nim
@@ -2,8 +2,8 @@ discard """
   output: "4"
 """
 
-template cse{f(a, a, x)}(a: expr{(nkDotExpr|call|nkBracketExpr)&noSideEffect},
-                         f: expr, x: varargs[expr]): expr =
+template cse{f(a, a, x)}(a: typed{(nkDotExpr|call|nkBracketExpr)&noSideEffect},
+                         f: typed, x: varargs[typed]): untyped =
   let aa = a
   f(aa, aa, x)+4
 
diff --git a/tests/trmacros/tmatrix.nim b/tests/trmacros/tmatrix.nim
index f409434c5..a14ad2db0 100644
--- a/tests/trmacros/tmatrix.nim
+++ b/tests/trmacros/tmatrix.nim
@@ -15,7 +15,7 @@ proc `$`(a: TMat): string = result = $a.dummy
 proc mat21(): TMat =
   result.dummy = 21
 
-macro optOps{ (`+`|`-`|`*`) ** a }(a: TMat): expr =
+macro optOps{ (`+`|`-`|`*`) ** a }(a: TMat): untyped =
   echo treeRepr(a)
   result = newCall(bindSym"mat21")
 
diff --git a/tests/trmacros/tnoalias.nim b/tests/trmacros/tnoalias.nim
index 1d5671362..ec12d4712 100644
--- a/tests/trmacros/tnoalias.nim
+++ b/tests/trmacros/tnoalias.nim
@@ -2,7 +2,7 @@ discard """
   output: "23"
 """
 
-template optslice{a = b + c}(a: expr{noalias}, b, c: expr): stmt =
+template optslice{a = b + c}(a: untyped{noalias}, b, c: untyped): typed =
   a = b
   inc a, c
 
diff --git a/tests/trmacros/tnoalias2.nim b/tests/trmacros/tnoalias2.nim
index 5a816acb9..9362e764f 100644
--- a/tests/trmacros/tnoalias2.nim
+++ b/tests/trmacros/tnoalias2.nim
@@ -3,7 +3,7 @@ discard """
 """
 
 # bug #206
-template optimizeOut{testFunc(a, b)}(a: int, b: int{alias}) : expr = 0
+template optimizeOut{testFunc(a, b)}(a: int, b: int{alias}): untyped = 0
 
 proc testFunc(a, b: int): int = result = a + b
 var testVar = 1
diff --git a/tests/trmacros/tnoendlessrec.nim b/tests/trmacros/tnoendlessrec.nim
index 53891bcc0..508770ca7 100644
--- a/tests/trmacros/tnoendlessrec.nim
+++ b/tests/trmacros/tnoendlessrec.nim
@@ -4,7 +4,7 @@ discard """
 
 # test that an endless recursion is avoided:
 
-template optLen{len(x)}(x: expr): expr = len(x)
+template optLen{len(x)}(x: typed): int = len(x)
 
 var s = "lala"
 echo len(s)
diff --git a/tests/trmacros/tor.nim b/tests/trmacros/tor.nim
index 500851582..d698e928d 100644
--- a/tests/trmacros/tor.nim
+++ b/tests/trmacros/tor.nim
@@ -4,13 +4,13 @@ true
 3'''
 """
 
-template arithOps: expr = (`+` | `-` | `*`)
-template testOr{ (arithOps{f})(a, b) }(a, b, f: expr): expr = f(a+1, b)
+template arithOps: untyped = (`+` | `-` | `*`)
+template testOr{ (arithOps{f})(a, b) }(a, b, f: untyped): untyped = f(a+1, b)
 
 let xx = 10
 echo 10*xx
 
-template t{x = (~x){y} and (~x){z}}(x, y, z: bool): stmt =
+template t{x = (~x){y} and (~x){z}}(x, y, z: bool): typed =
   x = y
   if x: x = z
 
@@ -22,7 +22,7 @@ a = b and a
 echo a
 
 # bug #798
-template t012{(0|1|2){x}}(x: expr): expr = x+1
+template t012{(0|1|2){x}}(x: untyped): untyped = x+1
 let z = 1
 # outputs 3 thanks to fixpoint iteration:
 echo z
diff --git a/tests/trmacros/tpartial.nim b/tests/trmacros/tpartial.nim
index fdaa3414a..c636684d7 100644
--- a/tests/trmacros/tpartial.nim
+++ b/tests/trmacros/tpartial.nim
@@ -5,7 +5,7 @@ discard """
 proc p(x, y: int; cond: bool): int =
   result = if cond: x + y else: x - y
 
-template optP{p(x, y, true)}(x, y: expr): expr = x - y
-template optP{p(x, y, false)}(x, y: expr): expr = x + y
+template optP{p(x, y, true)}(x, y): untyped = x - y
+template optP{p(x, y, false)}(x, y): untyped = x + y
 
 echo p(2, 4, true)
diff --git a/tests/trmacros/tstar.nim b/tests/trmacros/tstar.nim
index 536289ff0..86f698232 100644
--- a/tests/trmacros/tstar.nim
+++ b/tests/trmacros/tstar.nim
@@ -10,7 +10,7 @@ proc `&&`(s: varargs[string]): string =
   for i in 1..len(s)-1: result.add s[i]
   inc calls
 
-template optConc{ `&&` * a }(a: string): expr = &&a
+template optConc{ `&&` * a }(a: string): string = &&a
 
 let space = " "
 echo "my" && (space & "awe" && "some " ) && "concat"
diff --git a/tests/trmacros/tstmtlist.nim b/tests/trmacros/tstmtlist.nim
index 5202f778b..751acb79a 100644
--- a/tests/trmacros/tstmtlist.nim
+++ b/tests/trmacros/tstmtlist.nim
@@ -8,7 +8,7 @@ discard """
 template optWrite{
   write(f, x)
   ((write|writeLine){w})(f, y)
-}(x, y: varargs[expr], f, w: expr) =
+}(x, y: varargs[untyped], f, w: untyped) =
   w(f, "|", x, y, "|")
 
 if true:
diff --git a/tests/tuples/tuple_with_nil.nim b/tests/tuples/tuple_with_nil.nim
index 9b5d583d3..7f5a359f5 100644
--- a/tests/tuples/tuple_with_nil.nim
+++ b/tests/tuples/tuple_with_nil.nim
@@ -758,7 +758,7 @@ proc addfmtfmt(fmtstr: string; args: NimNode; retvar: NimNode): NimNode {.compil
     if arg.cnt == 0:
       warning("Argument " & $(i+1) & " `" & args[i].repr & "` is not used in format string")
 
-macro addfmt(s: var string, fmtstr: string{lit}, args: varargs[expr]): expr =
+macro addfmt(s: var string, fmtstr: string{lit}, args: varargs[typed]): untyped =
   ## The same as `s.add(fmtstr.fmt(args...))` but faster.
   result = addfmtfmt($fmtstr, args, s)
 
diff --git a/tests/typerel/temptynode.nim b/tests/typerel/temptynode.nim
index b32b16121..32148ce13 100644
--- a/tests/typerel/temptynode.nim
+++ b/tests/typerel/temptynode.nim
@@ -10,7 +10,7 @@ import macros
 proc blah(x: proc (a, b: int): int) =
   echo x(5, 5)
 
-macro test(): stmt =
+macro test(): untyped =
   result = newNimNode(nnkEmpty)
 
 blah(test())
diff --git a/tests/typerel/tsymchoice_for_expr.nim b/tests/typerel/tsymchoice_for_expr.nim
index 4c1f52bef..394b22704 100644
--- a/tests/typerel/tsymchoice_for_expr.nim
+++ b/tests/typerel/tsymchoice_for_expr.nim
@@ -1,6 +1,6 @@
 # bug #1988
 
-template t(e: expr) = discard
+template t(e) = discard
 
 proc positive(x: int): int = +x
 proc negative(x: int): int = -x
diff --git a/tests/typerel/tvarargsexpr.nim b/tests/typerel/tvarargsexpr.nim
index c6a59fb20..092d50076 100644
--- a/tests/typerel/tvarargsexpr.nim
+++ b/tests/typerel/tvarargsexpr.nim
@@ -8,7 +8,7 @@ true'''
 
 import macros
 
-macro thirteen(args: varargs[expr]): expr =
+macro thirteen(args: varargs[untyped]): int =
   result = newIntLitNode(13)
 
 doAssert(13==thirteen([1,2])) # works
@@ -22,7 +22,8 @@ echo "success"
 # bug #2545
 
 import macros
-macro test(e: varargs[untyped]): expr = bindSym"true"
+macro test(e: varargs[untyped]): untyped =
+  bindSym"true"
 
 echo test(a)
 echo test(fake=90, arguments=80, also="false", possible=true)
diff --git a/tests/types/taliasassignment.nim b/tests/types/taliasassignment.nim
deleted file mode 100644
index bf4fe8520..000000000
--- a/tests/types/taliasassignment.nim
+++ /dev/null
@@ -1,50 +0,0 @@
-discard """
-  output: '''19
-(c: 0)
-(c: 13)
-@[(c: 11)]
-@[(c: 17)]'''
-"""
-# bug #5238
-
-type
-  Rgba8 = object
-    c: int
-  BlenderRgb*[ColorT] = object
-
-template getColorType*[C](x: typedesc[BlenderRgb[C]]): typedesc = C
-
-type
-  ColorT = getColorType(BlenderRgb[int])
-
-proc setColor(c: var ColorT) =
-  c = 19
-
-var n: ColorT
-n.setColor()
-echo n
-
-type
-  ColorType = getColorType(BlenderRgb[Rgba8])
-
-var x: ColorType
-echo x
-
-proc setColor(c: var ColorType) =
-  c = Rgba8(c: 13)
-
-proc setColor(c: var seq[ColorType]) =
-  c[0] = Rgba8(c: 11)
-
-proc setColorArray(c: var openArray[ColorType]) =
-  c[0] = Rgba8(c: 17)
-
-x.setColor()
-echo x
-
-var y = @[Rgba8(c:15)]
-y.setColor()
-echo y
-
-y.setColorArray()
-echo y
\ No newline at end of file
diff --git a/tests/types/taliasbugs.nim b/tests/types/taliasbugs.nim
new file mode 100644
index 000000000..57254760a
--- /dev/null
+++ b/tests/types/taliasbugs.nim
@@ -0,0 +1,158 @@
+discard """
+  msg: '''true
+true
+true
+true
+true
+true'''
+  output: '''true
+true
+true
+true
+true
+true
+R
+R
+R
+R
+19
+(c: 0)
+(c: 13)
+@[(c: 11)]
+@[(c: 17)]
+100'''
+"""
+
+# bug #5360
+import macros
+
+type
+  Order = enum
+    R
+  OrderAlias = Order
+
+template getOrderTypeA(): typedesc = Order
+template getOrderTypeB(): typedesc = OrderAlias
+
+type
+  OrderR    = getOrderTypeA()
+  OrderG    = getOrderTypeB()
+
+macro typeRep(a, b: typed): untyped =
+  if sameType(a, b):
+    echo "true"
+  else:
+    echo "false"
+
+template test(a, b: typedesc) =
+  when a is b:
+    echo "true"
+  else:
+    echo "false"
+
+test(OrderAlias, Order)
+test(OrderR, Order)
+test(OrderG, Order)
+
+test(OrderR, OrderG)
+test(OrderR, OrderAlias)
+test(OrderG, OrderAlias)
+
+typeRep(OrderAlias.R, Order.R)  # true
+typeRep(OrderR.R, Order.R)      # true
+typeRep(OrderG.R, Order.R)      # true
+
+typeRep(OrderR.R, OrderAlias.R) # true
+typeRep(OrderG.R, OrderAlias.R) # true
+typeRep(OrderR.R, OrderG.R)     # true
+
+echo OrderR.R      # R
+echo OrderG.R      # R
+echo OrderAlias.R  # R
+echo Order.R       # R
+
+# bug #5238
+
+type
+  Rgba8 = object
+    c: int
+  BlenderRgb*[ColorT] = object
+
+template getColorType*[C](x: typedesc[BlenderRgb[C]]): typedesc = C
+
+type
+  ColorT = getColorType(BlenderRgb[int])
+
+proc setColor(c: var ColorT) =
+  c = 19
+
+var n: ColorT
+n.setColor()
+echo n
+
+type
+  ColorType = getColorType(BlenderRgb[Rgba8])
+
+var x: ColorType
+echo x
+
+proc setColor(c: var ColorType) =
+  c = Rgba8(c: 13)
+
+proc setColor(c: var seq[ColorType]) =
+  c[0] = Rgba8(c: 11)
+
+proc setColorArray(c: var openArray[ColorType]) =
+  c[0] = Rgba8(c: 17)
+
+x.setColor()
+echo x
+
+var y = @[Rgba8(c:15)]
+y.setColor()
+echo y
+
+y.setColorArray()
+echo y
+
+#bug #6016
+type
+  Onion {.union.} = object
+    field1: int
+    field2: uint64
+
+  Stroom  = Onion
+
+  PStroom = ptr Stroom
+
+proc pstruct(u: PStroom) =
+  echo u.field2
+
+var oni = Onion(field1: 100)
+pstruct(oni.addr)
+
+
+# bug #4124
+
+import sequtils
+
+type
+    Foo = distinct string
+
+var
+  foo: Foo
+
+type
+    Alias = (type(foo))
+var
+  a: Alias
+
+a = foo
+
+when true:
+  var xs = @[1,2,3]
+
+  proc asFoo(i: string): Foo =
+      Foo(i)
+
+  var xx = xs.mapIt(asFoo($(it + 5)))
diff --git a/tests/types/taliasinequality.nim b/tests/types/taliasinequality.nim
deleted file mode 100644
index f3ecd536a..000000000
--- a/tests/types/taliasinequality.nim
+++ /dev/null
@@ -1,66 +0,0 @@
-discard """
-  msg: '''true
-true
-true
-true
-true
-true'''
-  output: '''true
-true
-true
-true
-true
-true
-R
-R
-R
-R'''
-"""
-
-# bug #5360
-import macros
-
-type
-  Order = enum
-    R
-  OrderAlias = Order
-
-template getOrderTypeA(): typedesc = Order
-template getOrderTypeB(): typedesc = OrderAlias
-
-type
-  OrderR    = getOrderTypeA()
-  OrderG    = getOrderTypeB()
-
-macro typeRep(a, b: typed): untyped =
-  if sameType(a, b):
-    echo "true"
-  else:
-    echo "false"
-
-template test(a, b: typedesc) =
-  when a is b:
-    echo "true"
-  else:
-    echo "false"
-
-test(OrderAlias, Order)
-test(OrderR, Order)
-test(OrderG, Order)
-
-test(OrderR, OrderG)
-test(OrderR, OrderAlias)
-test(OrderG, OrderAlias)
-
-typeRep(OrderAlias.R, Order.R)  # true
-typeRep(OrderR.R, Order.R)      # true
-typeRep(OrderG.R, Order.R)      # true
-
-typeRep(OrderR.R, OrderAlias.R) # true
-typeRep(OrderG.R, OrderAlias.R) # true
-typeRep(OrderR.R, OrderG.R)     # true
-
-echo OrderR.R      # R
-echo OrderG.R      # R
-echo OrderAlias.R  # R
-echo Order.R       # R
\ No newline at end of file
diff --git a/tests/types/tauto_canbe_void.nim b/tests/types/tauto_canbe_void.nim
index 60e83c510..fd42cb701 100644
--- a/tests/types/tauto_canbe_void.nim
+++ b/tests/types/tauto_canbe_void.nim
@@ -1,7 +1,7 @@
 
 import future
 
-template tempo(s: expr) =
+template tempo(s) =
   s("arg")
 
 tempo((s: string)->auto => echo(s))
diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim
index 14999ebee..2f9dbf245 100644
--- a/tests/types/tisopr.nim
+++ b/tests/types/tisopr.nim
@@ -17,10 +17,10 @@ proc IsVoid[T](): string =
 const x = int is int
 echo x, " ", float is float, " ", float is string, " ", IsVoid[void]()
 
-template yes(e: expr): stmt =
+template yes(e): void =
   static: assert e
 
-template no(e: expr): stmt =
+template no(e): void =
   static: assert(not e)
 
 when false:
diff --git a/tests/types/typeof_produces_alias.nim b/tests/types/typeof_produces_alias.nim
deleted file mode 100644
index 44cb00c94..000000000
--- a/tests/types/typeof_produces_alias.nim
+++ /dev/null
@@ -1,25 +0,0 @@
-
-# bug #4124
-
-import sequtils
-
-type
-    Foo = distinct string
-
-var
-  foo: Foo
-
-type
-    Alias = (type(foo))
-var
-  a: Alias
-
-a = foo
-
-when true:
-  var xs = @[1,2,3]
-
-  proc asFoo(i: string): Foo =
-      Foo(i)
-
-  var xx = xs.mapIt(asFoo($(it + 5)))
diff --git a/tests/untestable/readme.markdown b/tests/untestable/readme.markdown
new file mode 100644
index 000000000..fcb7f4f28
--- /dev/null
+++ b/tests/untestable/readme.markdown
@@ -0,0 +1,2 @@
+This directory contains tests which are not automatically executed
+for various reasons. Mainly due to dependencies on external services.
\ No newline at end of file
diff --git a/tests/usingstmt/tusingstatement.nim b/tests/usingstmt/tusingstatement.nim
index 0d76b2423..8585bcc9e 100644
--- a/tests/usingstmt/tusingstatement.nim
+++ b/tests/usingstmt/tusingstatement.nim
@@ -12,7 +12,7 @@ import
 # Nim's destructors offer a mechanism for automatic
 # disposal of resources.
 #
-macro autoClose(e: expr): stmt {.immediate.} =
+macro autoClose(args: varargs[untyped]): untyped =
   let e = callsite()
   if e.len != 3:
     error "Using statement: unexpected number of arguments. Got " &
@@ -85,5 +85,3 @@ proc use(r: var TResource) =
 
 autoClose(r = openResource("test")):
   use r
-
-
diff --git a/tests/vm/tanonproc.nim b/tests/vm/tanonproc.nim
index 474b768ca..c5cb57d75 100644
--- a/tests/vm/tanonproc.nim
+++ b/tests/vm/tanonproc.nim
@@ -42,7 +42,7 @@ proc getOrElse[T](o: Option[T], def: T): T =
 proc quoteStr(s: string): Option[string] =
   s.some.notEmpty.map(v => "`" & v & "`")
 
-macro str(s: string): stmt =
+macro str(s: string): typed =
   let x = s.strVal
   let y = quoteStr(x)
   let sn = newStrLitNode(y.getOrElse("NONE"))
diff --git a/tests/vm/tasmparser.nim b/tests/vm/tasmparser.nim
index fbacdbc87..d70c629b6 100644
--- a/tests/vm/tasmparser.nim
+++ b/tests/vm/tasmparser.nim
@@ -10,10 +10,10 @@ var
   cpp {.compileTime.} = ""
   token {.compileTime.} = ""
 
-proc log (msg: string) {.compileTime.} =
+proc log(msg: string) {.compileTime.} =
     echo msg
 
-proc asmx64 () {.compileTime} =
+proc asmx64() {.compileTime} =
 
   #log "code = $1" % code
 
@@ -36,7 +36,7 @@ proc asmx64 () {.compileTime} =
   const end_or_symbol_or_comment_or_passthrough = symbolStart + end_or_comment + passthrough_start
 
 
-  proc abortAsmParse (err:string) =
+  proc abortAsmParse(err:string) =
     discard
 
   let codeLen = code.len
@@ -49,17 +49,17 @@ proc asmx64 () {.compileTime} =
 
   var state:asmParseState = leading
 
-  proc checkEnd (err:string) =
-    let ch = code [start]
-    if int (ch) == 0:
-      abortAsmParse (err)
+  proc checkEnd(err:string) =
+    let ch = code[start]
+    if int(ch) == 0:
+      abortAsmParse(err)
 
-  proc get_passthrough () =
+  proc get_passthrough() =
     inc start
     let prev_start = start
     let prev_token = token
-    start += code.parseUntil (token, passthrough_end, start)
-    checkEnd ("Failed to find passthrough end delimiter from offset $1 for:$2\n$3" % [$prev_start, $(code [prev_start-prev_token.len..prev_start]), token[1..token.len-1]])
+    start += code.parseUntil(token, passthrough_end, start)
+    checkEnd("Failed to find passthrough end delimiter from offset $1 for:$2\n$3" % [$prev_start, $(code[prev_start-prev_token.len..prev_start]), token[1..token.len-1]])
     inc start
     cpp.add "`"
     cpp.add token
@@ -67,27 +67,27 @@ proc asmx64 () {.compileTime} =
 
   var inparse = true
 
-  proc checkCmdEnd () =
+  proc checkCmdEnd() =
     if codeLen == start:
       state = endCmd
       inparse = false
 
   while inparse:
-    checkCmdEnd ()
+    checkCmdEnd()
 
-    log ("state=$1 start=$2" % [$state, $start])
+    log("state=$1 start=$2" % [$state, $start])
 
     case state:
     of leading:
 
       echo "b100 ", start
-      start += code.skipWhile (leadingWhiteSpace, start)
+      start += code.skipWhile(leadingWhiteSpace, start)
       echo "b200 ", start
-      let ch = code [start]
+      let ch = code[start]
       if ch in endOfLine:
-        inc (line)
+        inc(line)
         #echo "c100 ", start, ' ', code
-        start += code.skipWhile (endOfline, start)
+        start += code.skipWhile(endOfline, start)
         #echo "c200 ", start, ' ', code
         continue
       elif ch in symbolStart:
@@ -95,20 +95,20 @@ proc asmx64 () {.compileTime} =
       elif ch in eolComment:
         state = skipToEndOfLine
       elif ch in passthrough_start:
-        get_passthrough ()
+        get_passthrough()
         echo "d100 ", start
-        start += code.parseUntil (token, end_or_symbol_or_comment_or_passthrough, start)
+        start += code.parseUntil(token, end_or_symbol_or_comment_or_passthrough, start)
         echo "d200 ", start
         cpp.add token
         state = mnemonic
-      elif int (ch) == 0:
+      elif int(ch) == 0:
         break
       else:
-        abortAsmParse ("after '$3' illegal character at offset $1: $2" % [$start, $(int (ch)), token])
+        abortAsmParse("after '$3' illegal character at offset $1: $2" % [$start, $(int(ch)), token])
 
     of mnemonic:
       echo "e100 ", start
-      start += code.parseWhile (token, symbol, start)
+      start += code.parseWhile(token, symbol, start)
       echo "e200 ", start
       cpp.add xp
       cpp.add token
@@ -118,29 +118,29 @@ proc asmx64 () {.compileTime} =
     of betweenArguments:
       let tmp = start
       let rcode = code
-      start += rcode.parseUntil (token, end_or_symbol_or_comment_or_passthrough, tmp)
+      start += rcode.parseUntil(token, end_or_symbol_or_comment_or_passthrough, tmp)
       cpp.add token
 
       if codeLen <= start:
         state = endCmd
         continue
 
-      let ch = code [start]
+      let ch = code[start]
       if ch in passthrough_start:
-        get_passthrough ()
+        get_passthrough()
         continue
-      if (ch in {'x', 'X'}) and ('0' == code [start-1]):
-        token = $(code [start])
+      if(ch in {'x', 'X'}) and('0' == code[start-1]):
+        token = $(code[start])
         cpp.add token
         inc start
         continue
       state = arguments
 
     of arguments:
-      if code [start] in end_or_comment:
+      if code[start] in end_or_comment:
         state = endCmd
         continue
-      start += code.parseWhile (token, symbol, start)
+      start += code.parseWhile(token, symbol, start)
       cpp.add xp
       cpp.add token
       state = betweenArguments
@@ -151,21 +151,21 @@ proc asmx64 () {.compileTime} =
 
     of skipToEndOfLine:
       echo "a100 ", start
-      start += code.skipUntil (endOfLine, start)
+      start += code.skipUntil(endOfLine, start)
       echo "a200 ", start
-      start += code.skipWhile (endOfline, start)
+      start += code.skipWhile(endOfline, start)
       echo "a300 ", start
       inc line
       state = leading
 
   cpp.add asmx64post
 
-  echo ($cpp)
+  echo($cpp)
 
-macro asmx64x (code_in:expr) : stmt =
+macro asmx64x(code_in:untyped) : typed =
   code = $code_in
-  echo ("code.len = $1, code = >>>$2<<<" % [$code.len, code])
-  asmx64 ()
+  echo("code.len = $1, code = >>>$2<<<" % [$code.len, code])
+  asmx64()
   discard result
 
 asmx64x """
diff --git a/tests/vm/tcomponent.nim b/tests/vm/tcomponent.nim
index efeba2a6d..e7962e7ab 100644
--- a/tests/vm/tcomponent.nim
+++ b/tests/vm/tcomponent.nim
@@ -70,12 +70,12 @@ proc parse_component(body: NimNode): Component =
           result.procs_index.add(procdef.identifier.name)
       else: discard
 
-macro component*(name: expr, body: stmt): stmt {.immediate.} =
+macro component*(name, body: untyped): typed =
   let component = parse_component(body)
   registry.addComponent($name, component)
   parseStmt("discard")
 
-macro component_builtins(body: stmt): stmt {.immediate.} =
+macro component_builtins(body: untyped): typed =
   let builtin = parse_component(body)
   registry.field_index = builtin.field_index
   registry.procs_index = builtin.procs_index
@@ -88,7 +88,7 @@ proc bind_methods*(component: var Component, identifier: Ident): seq[NimNode] =
     procdef.params.insert(this_field, 0)
     result.add(procdef.render())
 
-macro bind_components*(type_name, component_names: expr): stmt {.immediate.} =
+macro bind_components*(type_name, component_names: untyped): typed =
   result = newStmtList()
   let identifier = newIdent(type_name)
   let components = newBracket(component_names)
diff --git a/tests/vm/teval1.nim b/tests/vm/teval1.nim
index 1d3a68a0d..0eaa050da 100644
--- a/tests/vm/teval1.nim
+++ b/tests/vm/teval1.nim
@@ -5,7 +5,7 @@ proc testProc: string {.compileTime.} =
   result = result & ""
 
 when true:
-  macro test(n: stmt): stmt {.immediate.} =
+  macro test(n: untyped): untyped =
     result = newNimNode(nnkStmtList)
     echo "#", testProc(), "#"
   test:
@@ -20,5 +20,3 @@ echo "##", x, "##"
 static:
     var i, j: set[int8] = {}
     var k = i + j
-
-
diff --git a/tests/vm/texcl.nim b/tests/vm/texcl.nim
index 4ccfd6bfa..e23a423fe 100644
--- a/tests/vm/texcl.nim
+++ b/tests/vm/texcl.nim
@@ -15,13 +15,13 @@ proc initOpts(): set[nlOptions] =
   result.incl nloDebug
   result.incl nloNone
   result.excl nloDebug
-  
+
 const cOpts = initOpts()
 
-macro nlo(): stmt =
+macro nlo(): typed =
   nlOpts.incl(nloNone)
   nlOpts.excl(nloDebug)
   result = newEmptyNode()
 
 nlo()
-echo nloDebug in cOpts
\ No newline at end of file
+echo nloDebug in cOpts
diff --git a/tests/vm/tmitems.nim b/tests/vm/tmitems.nim
index 4ee225eed..a0e64d6aa 100644
--- a/tests/vm/tmitems.nim
+++ b/tests/vm/tmitems.nim
@@ -7,7 +7,7 @@ discard """
 # bug #3731
 var list {.compileTime.} = newSeq[int]()
 
-macro calc*(): stmt {.immediate.} =
+macro calc*(): typed =
   list.add(1)
   for c in list.mitems:
     c = 13
@@ -19,7 +19,7 @@ calc()
 
 # bug #3859
 import macros
-macro m: stmt =
+macro m: typed =
   var s = newseq[NimNode](3)
   # var s: array[3,NimNode]                 # not working either
   for i in 0..<s.len: s[i] = newLit(3)    # works
@@ -29,3 +29,17 @@ macro m: stmt =
     result.add newCall(bindsym"echo", i)
 
 m()
+
+# bug 4741 & 5013
+proc test() =
+  var s = [("baz", 42), ("bath", 42)]
+  for i in s.mitems:
+    i[1] = 3
+  doAssert(s == [("baz", 3), ("bath", 3)])
+
+static:
+  test()
+  var s = [("baz", 42), ("bath", 42)]
+  for i in s.mitems:
+    i[1] = 3
+  doAssert(s == [("baz", 3), ("bath", 3)])
diff --git a/tests/vm/tseq_badinit.nim b/tests/vm/tseq_badinit.nim
new file mode 100644
index 000000000..15889d60e
--- /dev/null
+++ b/tests/vm/tseq_badinit.nim
@@ -0,0 +1,58 @@
+
+type
+  AObj = object
+    i: int
+    d: float
+  ATup = tuple
+    i: int
+    d: float
+  MyEnum = enum
+    E01, E02, E03
+  Myrange = range[0..10]
+
+  MyProc = proc (x: int): bool
+  MyInt = distinct int
+  MyAlias = MyInt
+  MySet = set[char]
+  MyArray = array[4, char]
+  MySeq = seq[string]
+
+template test(typename, default: untyped) =
+  proc `abc typename`(): seq[typename] =
+    result = newSeq[typename]()
+    result.add(default)
+    result.setLen(3)
+    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:
+      result[i] = default
+  const constarr = `arr typename`()
+  doAssert(constarr == `arr typename`())
+
+proc even(x: int): bool = x mod 2 == 0
+proc `==`(x, y: MyInt): bool = ord(x) == ord(y)
+proc `$`(x: MyInt): string = $ord(x)
+proc `$`(x: proc): string =
+  if x.isNil: "(nil)" else: "funcptr"
+
+test(int, 0)
+test(uint, 0)
+test(float, 0.1)
+test(char, '0')
+test(bool, false)
+test(uint8, 2)
+test(string, "data")
+test(MyProc, even)
+test(MyEnum, E02)
+test(AObj, AObj())
+test(ATup, (i:11, d:9.99))
+test(Myrange, 4)
+test(MyInt, MyInt(4))
+test(MyAlias, MyAlias(4))
+test(MyArray, ['0','1','2','3'])
+test(MySeq, @["data"])
diff --git a/tests/vm/tstaticprintseq.nim b/tests/vm/tstaticprintseq.nim
index e4a6aa081..246a0211b 100644
--- a/tests/vm/tstaticprintseq.nim
+++ b/tests/vm/tstaticprintseq.nim
@@ -24,7 +24,7 @@ bb
 
 const s = @[1,2,3]
 
-macro foo: stmt =
+macro foo: typed =
   for e in s:
     echo e
 
@@ -34,7 +34,7 @@ static:
   for e in s:
     echo e
 
-macro bar(x: static[seq[int]]): stmt =
+macro bar(x: static[seq[int]]): untyped =
   for e in x:
     echo e
 
@@ -55,7 +55,7 @@ static:
   var m2: TData = data
   for x in m2.numbers: echo x
 
-macro ff(d: static[TData]): stmt =
+macro ff(d: static[TData]): typed =
   for x in d.letters:
     echo x
 
diff --git a/tests/vm/tstringnil.nim b/tests/vm/tstringnil.nim
index bb546b698..39e4040dc 100644
--- a/tests/vm/tstringnil.nim
+++ b/tests/vm/tstringnil.nim
@@ -41,7 +41,7 @@ proc buildSuiteContents(suiteName, suiteDesc, suiteBloc: NimNode): tuple[tests:
 
   return (tests: tests)
 
-macro suite(suiteName, suiteDesc: expr, suiteBloc: stmt): stmt {.immediate.} =
+macro suite(suiteName, suiteDesc, suiteBloc: untyped): typed =
   let contents = buildSuiteContents(suiteName, suiteDesc, suiteBloc)
 
 # Test above
diff --git a/tests/vm/ttouintconv.nim b/tests/vm/ttouintconv.nim
index cd25ffb00..5f8884e80 100644
--- a/tests/vm/ttouintconv.nim
+++ b/tests/vm/ttouintconv.nim
@@ -20,7 +20,7 @@ msg: '''
 
 #bug #2514
 
-macro foo(): stmt =
+macro foo(): typed =
   var x = 8'u8
   var y = 9'u16
   var z = 17'u32
@@ -57,21 +57,21 @@ macro foo(): stmt =
 
   var zz = 0x7FFFFFFF'u32
   echo zz
-  
-macro foo2(): stmt =
+
+macro foo2(): typed =
   var xx = 0x7FFFFFFFFFFFFFFF
   echo xx
-  
+
   var yy = 0
   echo yy
-  
+
   var zz = 0x80'u8
   echo zz
-  
+
   var ww = -9
   var vv = ww.uint
   var kk = vv.uint32
   echo kk
-  
+
 foo()
 foo2()
diff --git a/tests/vm/twrongconst.nim b/tests/vm/twrongconst.nim
index 424ed080e..a329cb578 100644
--- a/tests/vm/twrongconst.nim
+++ b/tests/vm/twrongconst.nim
@@ -4,6 +4,6 @@ discard """
 """
 
 var x: array[100, char]
-template foo : expr = x[42]
+template foo : char = x[42]
 
 const myConst = foo
diff --git a/tests/vm/tzero_extend.nim b/tests/vm/tzero_extend.nim
new file mode 100644
index 000000000..a79105531
--- /dev/null
+++ b/tests/vm/tzero_extend.nim
@@ -0,0 +1,44 @@
+
+const RANGE = -384.. -127
+
+proc get_values(): (seq[int8], seq[int16], seq[int32]) =
+  let i8 = -3'i8
+  let i16 = -3'i16
+  let i32 = -3'i32
+  doAssert i8.ze == 0xFD
+  doAssert i8.ze64 == 0xFD
+  doAssert i16.ze == 0xFFFD
+  doAssert i16.ze64 == 0xFFFD
+
+  result[0] = @[]; result[1] = @[]; result[2] = @[]
+
+  for offset in RANGE:
+    let i8 = -(1 shl 9) + offset
+    let i16 = -(1 shl 17) + offset
+    let i32 = -(1 shl 33) + offset
+
+    # higher bits are masked. these should be exactly equal to offset.
+    result[0].add i8.toU8
+    result[1].add i16.toU16
+    result[2].add i32.toU32
+
+
+# these values this computed by VM
+const COMPILETIME_VALUES = get_values()
+
+# these values this computed by compiler
+let RUNTIME_VALUES = get_values()
+
+template check_values(int_type: static[int]) =
+  var index = 0
+  let cvalues = COMPILETIME_VALUES[int_type]
+  let rvalues = RUNTIME_VALUES[int_type]
+  for offset in RANGE:
+    let moffset = cast[type(rvalues[0])](offset)
+    doAssert(moffset == rvalues[index] and moffset == cvalues[index],
+      "expected: " & $moffset & " got runtime: " & $rvalues[index] & " && compiletime: " & $cvalues[index] )
+    inc(index)
+
+check_values(0) # uint8
+check_values(1) # uint16
+check_values(2) # uint32
diff --git a/tools/nim.bash-completion b/tools/nim.bash-completion
new file mode 100644
index 000000000..4f62da986
--- /dev/null
+++ b/tools/nim.bash-completion
@@ -0,0 +1,47 @@
+# bash completion for nim                             -*- shell-script -*-
+
+_nim()
+{
+ local cur prev words cword split
+ _init_completion -s || return
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+
+ if [ $COMP_CWORD -eq 1 ] ; then
+   # first item - suggest commands
+   kw="compile c doc doc2 compileToC cc compileToCpp cpp compileToOC objc js e rst2html rst2tex jsondoc jsondoc2 buildIndex genDepend dump check"
+   COMPREPLY=( $( compgen -W "${kw}" -- $cur ) )
+   return 0
+ fi
+  case $prev in
+    --stackTrace|--lineTrace|--threads|-x|--checks|--objChecks|--fieldChecks|--rangeChecks|--boundChecks|--overflowChecks|-a|--assertions|--floatChecks|--nanChecks|--infChecks|--deadCodeElim)
+      # Options that require on/off
+      [[ "$cur" == "=" ]] && cur=""
+      COMPREPLY=( $(compgen -W 'on off' -- "$cur") )
+      return 0
+    ;;
+    --opt)
+      [[ "$cur" == "=" ]] && cur=""
+      COMPREPLY=( $(compgen -W 'none speed size' -- "$cur") )
+      return 0
+    ;;
+    --app)
+      [[ "$cur" == "=" ]] && cur=""
+      COMPREPLY=( $(compgen -W 'console gui lib staticlib' -- "$cur") )
+      return 0
+    ;;
+    *)
+      kw="-r -p= --path= -d= --define= -u= --undef= -f --forceBuild --opt= --app= --stackTrace= --lineTrace= --threads= -x= --checks= --objChecks= --fieldChecks= --rangeChecks= --boundChecks= --overflowChecks= -a= --assertions= --floatChecks= --nanChecks= --infChecks= --deadCodeElim="
+      COMPREPLY=( $( compgen -W "${kw}" -- $cur ) )
+      _filedir '@(nim)'
+      #$split
+      return 0
+    ;;
+  esac
+  return 0
+
+} &&
+complete -onospace -F _nim nim
+
+# ex: ts=2 sw=2 et filetypesh
diff --git a/tools/nim.zsh-completion b/tools/nim.zsh-completion
new file mode 100644
index 000000000..3d6e0d5f6
--- /dev/null
+++ b/tools/nim.zsh-completion
@@ -0,0 +1,76 @@
+#compdef nim
+
+_nim() {
+  _arguments -C \
+    ':command:((
+      {compile,c}\:compile\ project\ with\ default\ code\ generator\ C
+      doc\:generate\ the\ documentation\ for\ inputfile
+      doc2\:generate\ the\ documentation\ for\ inputfile
+      {compileToC,cc}\:compile\ project\ with\ C\ code\ generator
+      {compileToCpp,cpp}\:compile\ project\ to\ C++\ code
+      {compileToOC,objc}\:compile\ project\ to\ Objective\ C\ code
+      js\:compile\ project\ to\ Javascript
+      e\:run\ a\ Nimscript\ file
+      rst2html\:convert\ a\ reStructuredText\ file\ to\ HTML
+      rst2tex\:convert\ a\ reStructuredText\ file\ to\ TeX
+      jsondoc\:extract\ the\ documentation\ to\ a\ json\ file
+      jsondoc2\:extract\ documentation\ to\ a\ json\ file\ using\ doc2
+      buildIndex\:build\ an\ index\ for\ the\ whole\ documentation
+      genDepend\:generate\ a\ DOT\ file\ containing\ the\ module\ dependency\ graph
+      dump\:dump\ all\ defined\ conditionals\ and\ search\ paths
+      check\:checks\ the\ project\ for\ syntax\ and\ semantic
+    ))' \
+    '*-r[run the application]' \
+    '*--run[run the application]' \
+    '*-p=[add path to search paths]' \
+    '*--path=[add path to search paths]' \
+    '*-d=[define a conditional symbol]' \
+    '*--define=[define a conditional symbol]' \
+    '*-u=[undefine a conditional symbol]' \
+    '*--undef=[undefine a conditional symbol]' \
+    '*-f[force rebuilding of all modules]' \
+    '*--forceBuild[force rebuilding of all modules]' \
+    '*--stackTrace=on[turn stack tracing on]' \
+    '*--stackTrace=off[turn stack tracing off]' \
+    '*--lineTrace=on[turn line tracing on]' \
+    '*--lineTrace=off[turn line tracing off]' \
+    '*--threads=on[turn support for multi-threading on]' \
+    '*--threads=off[turn support for multi-threading off]' \
+    '*-x=on[turn all runtime checks on]' \
+    '*-x=off[turn all runtime checks off]' \
+    '*--checks=on[turn all runtime checks on]' \
+    '*--checks=off[turn all runtime checks off]' \
+    '*--objChecks=on[turn obj conversion checks on]' \
+    '*--objChecks=off[turn obj conversion checks off]' \
+    '*--fieldChecks=on[turn case variant field checks on]' \
+    '*--fieldChecks=off[turn case variant field checks off]' \
+    '*--rangeChecks=on[turn range checks on]' \
+    '*--rangeChecks=off[turn range checks off]' \
+    '*--boundChecks=on[turn bound checks on]' \
+    '*--boundChecks=off[turn bound checks off]' \
+    '*--overflowChecks=on[turn int over-/underflow checks on]' \
+    '*--overflowChecks=off[turn int over-/underflow checks off]' \
+    '*-a[turn assertions on]' \
+    '*-a[turn assertions off]' \
+    '*--assertions=on[turn assertions on]' \
+    '*--assertions=off[turn assertions off]' \
+    '*--floatChecks=on[turn all floating point (NaN/Inf) checks on]' \
+    '*--floatChecks=off[turn all floating point (NaN/Inf) checks off]' \
+    '*--nanChecks=on[turn NaN checks on]' \
+    '*--nanChecks=off[turn NaN checks off]' \
+    '*--infChecks=on[turn Inf checks on]' \
+    '*--infChecks=off[turn Inf checks off]' \
+    '*--deadCodeElim=on[whole program dead code elimination on]' \
+    '*--deadCodeElim=off[whole program dead code elimination off]' \
+    '*--opt=none[do not optimize]' \
+    '*--opt=speed[optimize for speed|size - use -d:release for a release build]' \
+    '*--opt=size[optimize for size]' \
+    '*--debugger:native|endb[use native debugger (gdb) | ENDB (experimental)]' \
+    '*--app=console[generate a console app]' \
+    '*--app=gui[generate a GUI app]' \
+    '*--app=lib[generate a dynamic library]' \
+    '*--app=staticlib[generate a static library]' \
+    ':filename:_files -g"*.nim"'
+}
+
+_nim "$@"
diff --git a/tools/niminst/buildsh.tmpl b/tools/niminst/buildsh.tmpl
index e90ad97c0..faa8a47d0 100644
--- a/tools/niminst/buildsh.tmpl
+++ b/tools/niminst/buildsh.tmpl
@@ -1,4 +1,4 @@
-#? stdtmpl(subsChar='?') | standard
+#? stdtmpl(subsChar='?') | standard
 #proc generateBuildShellScript(c: ConfigData): string =
 #  result = "#! /bin/sh\n# Generated from niminst\n" &
 #           "# Template is in tools/niminst/buildsh.tmpl\n" &
@@ -9,6 +9,18 @@ set -e
 while :
 do
   case "$1" in
+    --os)
+      optos=$2
+      shift 2
+      ;;
+    --cpu)
+      optcpu=$2
+      shift 2
+      ;;
+    --osname)
+      optosname=$2
+      shift 2
+      ;;
     --extraBuildArgs)
       extraBuildArgs=" $2"
       shift 2
@@ -35,6 +47,7 @@ PS4=""
 #  add(result, "# platform detection\n")
 ucpu=`uname -m`
 uos=`uname`
+uosname=`uname -o`
 #  add(result, "# bin dir detection\n")
 binDir=?{firstBinPath(c).toUnix}
 
@@ -46,9 +59,21 @@ if [ ! -d $binDir ]; then
   mkdir $binDir
 fi
 
+#  add(result, "# override OS, CPU and OS Name with command-line arguments\n")
+if [ -n "$optos" ]; then 
+  uos="$optos"
+fi
+if [ -n "$optcpu" ]; then
+  ucpu="$optcpu"
+fi
+if [ -n "$optcpu" ]; then
+  uosname="$optosname"
+fi
+
 #  add(result, "# convert to lower case:\n")
 ucpu=`echo $ucpu | tr "[:upper:]" "[:lower:]"`
 uos=`echo $uos | tr "[:upper:]" "[:lower:]"`
+uosname=`echo $uosname | tr "[:upper:]" "[:lower:]"`
 
 case $uos in
   *linux* )
@@ -97,6 +122,11 @@ case $uos in
   *mingw* )
     myos="windows"
     ;;
+  *android* )
+    myos="android"
+    LINK_FLAGS="$LINK_FLAGS -ldl -lm -lrt"
+    LINK_FLAGS="$LINK_FLAGS -landroid-glob"
+    ;;
   *)
     echo 2>&1 "Error: unknown operating system: $uos"
     exit 1
@@ -123,8 +153,16 @@ case $ucpu in
   *power*|*ppc* )
     mycpu="powerpc" ;;
   *mips* )
-    mycpu="mips" ;;
-  *arm*|*armv6l* )
+    mycpu="$("$CC" -dumpmachine | sed 's/-.*//')"
+    case $mycpu in
+      mips|mipsel|mips64|mips64el)
+        ;;
+      *)
+        echo 2>&1 "Error: unknown MIPS target: $mycpu"
+        exit 1
+    esac
+    ;;
+  *arm*|*armv6l*|*armv71* )
     mycpu="arm" ;;
   *aarch64* )
     mycpu="arm64" ;;
@@ -134,7 +172,17 @@ case $ucpu in
     ;;
 esac
 
+case $uosname in
+  *android* )
+    LINK_FLAGS="$LINK_FLAGS -landroid-glob"
+    myosname="android"
+    myos="android"
+    ;;
+esac
+
 #  add(result, "# call the compiler:\n")
+echo \# OS:  $myos
+echo \# CPU: $mycpu
 
 case $myos in
 #  for osA in 1..c.oses.len:
diff --git a/tools/niminst/makefile.tmpl b/tools/niminst/makefile.tmpl
index c4e0be55e..4a20680e0 100644
--- a/tools/niminst/makefile.tmpl
+++ b/tools/niminst/makefile.tmpl
@@ -17,6 +17,7 @@ endif
 
 ucpu := $(shell sh -c 'uname -m | tr "[:upper:]" "[:lower:]"')
 uos := $(shell sh -c 'uname | tr "[:upper:]" "[:lower:]"')
+uosname := $(shell sh -c 'uname -o | tr "[:upper:]" "[:lower:]"')
 
 ifeq ($(uos),linux)
   myos = linux
@@ -68,6 +69,11 @@ ifndef uos
   $(error unknown operating system: $(uos))
 endif
 
+ifeq ($(uosname),android)
+  myos = android
+  LINK_FLAGS += -landroid-glob
+endif
+
 ifeq ($(ucpu),i386)
   mycpu = i386
 endif
@@ -114,8 +120,11 @@ endif
 ifeq ($(ucpu),ppc)
   mycpu = ppc
 endif
-ifeq ($(ucpu),mips)
-  mycpu = mips
+ifneq (,$(filter $(ucpu), mips mips64))
+  mycpu = $(shell /bin/sh -c '"$(CC)" -dumpmachine | sed "s/-.*//"')
+  ifeq (,$(filter $(mycpu), mips mipsel mips64 mips64el))
+    $(error unknown MIPS target: $(mycpu))
+  endif
 endif
 ifeq ($(ucpu),arm)
   mycpu = arm
diff --git a/tools/start.bat b/tools/start.bat
index a4475fac7..8e345998c 100644
--- a/tools/start.bat
+++ b/tools/start.bat
@@ -1,5 +1,6 @@
 @echo off

 REM COLOR 0A

+chcp 65001

 SET NIMPATH=%~dp0\..

 SET PATH=%NIMPATH%\bin;%NIMPATH%\dist\mingw\bin;%PATH%

 cd %NIMPATH%

diff --git a/tools/winrelease.nim b/tools/winrelease.nim
new file mode 100644
index 000000000..5a687cfaa
--- /dev/null
+++ b/tools/winrelease.nim
@@ -0,0 +1,9 @@
+## This is a small helper program to build the Win release.
+## This used to be part of koch (and it still uses koch as a library)
+## but since 'koch.exe' cannot overwrite itself is now its own program.
+## The problem is that 'koch.exe' too is part of the zip bundle and
+## needs to have the right 32/64 bit version. (Bug #6147)
+
+import "../koch"
+
+winRelease()
diff --git a/web/nimblepkglist.nim b/web/nimblepkglist.nim
index 7070f281b..870944ab0 100644
--- a/web/nimblepkglist.nim
+++ b/web/nimblepkglist.nim
@@ -33,6 +33,7 @@ proc processContent(content: string) =
 
   for pkg in jsonArr:
     assert pkg.kind == JObject
+    if not pkg.hasKey"url": continue
     let pkgWeb =
       if pkg.hasKey("web"): pkg["web"].str
       else: pkg["url"].str
@@ -40,7 +41,7 @@ proc processContent(content: string) =
       desc = pkg["description"].str
       dot = if desc.high > 0 and desc[desc.high] in endings: "" else: "."
       listItem = li(a(href=pkgWeb, pkg["name"].str), " ", desc & dot)
-    if pkg["url"].str.startsWith("git://github.com/nimrod-code") or
+    if pkg["url"].str.startsWith("https://github.com/nim-lang") or
        pkg["url"].str.startsWith("git://github.com/nim-lang") or
        "official" in pkg["tags"].elems:
       officialCount.inc