summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md7
-rw-r--r--compiler/aliases.nim2
-rw-r--r--compiler/ast.nim38
-rw-r--r--compiler/canonicalizer.nim2
-rw-r--r--compiler/ccgcalls.nim8
-rw-r--r--compiler/ccgexprs.nim363
-rw-r--r--compiler/ccgstmts.nim26
-rw-r--r--compiler/ccgtrav.nim3
-rw-r--r--compiler/ccgtypes.nim95
-rw-r--r--compiler/ccgutils.nim6
-rw-r--r--compiler/cgen.nim105
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/docgen.nim14
-rw-r--r--compiler/extccomp.nim102
-rw-r--r--compiler/gorgeimpl.nim83
-rw-r--r--compiler/importer.nim62
-rw-r--r--compiler/jsgen.nim20
-rw-r--r--compiler/lambdalifting.nim9
-rw-r--r--compiler/lexer.nim4
-rw-r--r--compiler/lookups.nim10
-rw-r--r--compiler/lowerings.nim2
-rw-r--r--compiler/main.nim7
-rw-r--r--compiler/nim.nim2
-rw-r--r--compiler/nimblecmd.nim8
-rw-r--r--compiler/options.nim3
-rw-r--r--compiler/parser.nim56
-rw-r--r--compiler/passes.nim2
-rw-r--r--compiler/pbraces.nim11
-rw-r--r--compiler/renderer.nim17
-rw-r--r--compiler/rodread.nim11
-rw-r--r--compiler/rodwrite.nim13
-rw-r--r--compiler/sem.nim14
-rw-r--r--compiler/semasgn.nim6
-rw-r--r--compiler/semcall.nim2
-rw-r--r--compiler/semdata.nim6
-rw-r--r--compiler/semexprs.nim38
-rw-r--r--compiler/semfold.nim6
-rw-r--r--compiler/semgnrc.nim10
-rw-r--r--compiler/seminst.nim2
-rw-r--r--compiler/semparallel.nim2
-rw-r--r--compiler/sempass2.nim8
-rw-r--r--compiler/semstmts.nim8
-rw-r--r--compiler/semtempl.nim2
-rw-r--r--compiler/semtypes.nim7
-rw-r--r--compiler/semtypinst.nim2
-rw-r--r--compiler/sighashes.nim2
-rw-r--r--compiler/sigmatch.nim10
-rw-r--r--compiler/transf.nim2
-rw-r--r--compiler/types.nim151
-rw-r--r--compiler/vm.nim10
-rw-r--r--compiler/vmdeps.nim50
-rw-r--r--compiler/vmgen.nim6
-rw-r--r--compiler/wordrecg.nim4
-rw-r--r--compiler/writetracking.nim2
-rw-r--r--doc/keywords.txt2
-rw-r--r--doc/manual/generics.txt6
-rw-r--r--doc/manual/stmts.txt4
-rw-r--r--doc/manual/types.txt3
-rw-r--r--doc/tut1.rst5
-rw-r--r--koch.nim3
-rw-r--r--lib/core/macros.nim10
-rw-r--r--lib/core/typeinfo.nim1
-rw-r--r--lib/genode_cpp/threads.h12
-rw-r--r--lib/impure/db_sqlite.nim3
-rw-r--r--lib/js/jsffi.nim6
-rw-r--r--lib/nimbase.h10
-rw-r--r--lib/packages/docutils/highlite.nim4
-rw-r--r--lib/pure/basic2d.nim855
-rw-r--r--lib/pure/basic3d.nim1040
-rw-r--r--lib/pure/collections/sets.nim98
-rw-r--r--lib/pure/json.nim57
-rw-r--r--lib/pure/nativesockets.nim4
-rw-r--r--lib/pure/options.nim81
-rw-r--r--lib/pure/osproc.nim14
-rw-r--r--lib/pure/strutils.nim2
-rw-r--r--lib/pure/unittest.nim83
-rw-r--r--lib/system.nim30
-rw-r--r--lib/system/assign.nim33
-rw-r--r--lib/system/channels.nim2
-rw-r--r--lib/system/deepcopy.nim2
-rw-r--r--lib/system/gc.nim18
-rw-r--r--lib/system/gc2.nim510
-rw-r--r--lib/system/gc_common.nim20
-rw-r--r--lib/system/gc_ms.nim14
-rw-r--r--lib/system/hti.nim15
-rw-r--r--lib/system/mmdisp.nim6
-rw-r--r--lib/system/nimscript.nim9
-rw-r--r--lib/system/sysstr.nim45
-rw-r--r--lib/system/threads.nim9
-rw-r--r--tests/array/tarraylen.nim18
-rw-r--r--tests/casestmt/tcomputedgoto.nim2
-rw-r--r--tests/concepts/t5642.nim2
-rw-r--r--tests/constr/tconstr1.nim4
-rw-r--r--tests/constr/tconstr2.nim2
-rw-r--r--tests/js/tclosures.nim16
-rw-r--r--tests/macros/tdebugstmt.nim2
-rw-r--r--tests/macros/tvtable.nim4
-rw-r--r--tests/magics/tlowhigh.nim2
-rw-r--r--tests/method/tnildispatcher.nim21
-rw-r--r--tests/misc/mvarious.nim2
-rw-r--r--tests/misc/tmandelbrot.nim22
-rw-r--r--tests/misc/tnew.nim2
-rw-r--r--tests/misc/tradix.nim10
-rw-r--r--tests/misc/tstrdesc.nim4
-rw-r--r--tests/niminaction/Chapter3/ChatApp/readme.markdown26
-rw-r--r--tests/niminaction/Chapter3/ChatApp/src/client.nim54
-rw-r--r--tests/niminaction/Chapter3/ChatApp/src/client.nim.cfg1
-rw-r--r--tests/niminaction/Chapter3/ChatApp/src/protocol.nim55
-rw-r--r--tests/niminaction/Chapter3/ChatApp/src/server.nim84
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/concurrency.nim79
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/concurrency.nim.cfg1
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim64
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim.cfg1
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/naive.nim29
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim72
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim.cfg1
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/race_condition.nim13
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/race_condition.nim.cfg1
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim34
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim15
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim.cfg1
-rw-r--r--tests/niminaction/Chapter7/Tweeter/Tweeter.nimble14
-rw-r--r--tests/niminaction/Chapter7/Tweeter/public/style.css117
-rw-r--r--tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim6
-rw-r--r--tests/niminaction/Chapter7/Tweeter/src/database.nim93
-rw-r--r--tests/niminaction/Chapter7/Tweeter/src/tweeter.nim62
-rw-r--r--tests/niminaction/Chapter7/Tweeter/src/views/general.nim51
-rw-r--r--tests/niminaction/Chapter7/Tweeter/src/views/user.nim49
-rw-r--r--tests/niminaction/Chapter7/Tweeter/tests/database_test.nim28
-rw-r--r--tests/niminaction/Chapter7/Tweeter/tests/database_test.nims2
-rw-r--r--tests/niminaction/Chapter8/canvas/canvas.nim19
-rw-r--r--tests/niminaction/Chapter8/canvas/canvas_test.nim19
-rw-r--r--tests/niminaction/Chapter8/sdl/sdl.nim34
-rw-r--r--tests/niminaction/Chapter8/sdl/sdl_test.nim25
-rw-r--r--tests/niminaction/Chapter8/sfml/sfml.nim26
-rw-r--r--tests/niminaction/Chapter8/sfml/sfml_test.nim9
-rw-r--r--tests/niminaction/Chapter9/configurator/configurator.nim84
-rw-r--r--tests/stdlib/mjsonexternproc.nim10
-rw-r--r--tests/stdlib/tjsonexternproc.nim5
-rw-r--r--tests/stdlib/tmath2.nim2
-rw-r--r--tests/stdlib/trepr2.nim2
-rw-r--r--tests/stdlib/tstrset.nim6
-rw-r--r--tests/stdlib/tunittest.nim27
-rw-r--r--tests/testament/categories.nim44
-rw-r--r--tests/threads/tthreadanalysis.nim2
-rw-r--r--tests/threads/tthreadanalysis2.nim2
-rw-r--r--tests/varres/tvarres3.nim2
-rw-r--r--tests/vm/tarrayboundeval.nim2
-rw-r--r--tests/vm/tnimnode.nim18
-rw-r--r--tests/vm/tvmmisc.nim66
-rw-r--r--tools/finish.nim21
-rw-r--r--tools/nimresolve.nim158
152 files changed, 3011 insertions, 3096 deletions
diff --git a/changelog.md b/changelog.md
new file mode 100644
index 000000000..9f6a83a37
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,7 @@
+## v0.18.0 - dd/mm/yyyy
+
+### Changes affecting backwards compatibility
+
+- Removed basic2d/basic3d out of the stdlib and into Nimble packages.
+  These packages deprecated however, use the ``glm``, ``arraymancer``, ``neo``
+  or another package.
diff --git a/compiler/aliases.nim b/compiler/aliases.nim
index 0c836bb24..c0371e159 100644
--- a/compiler/aliases.nim
+++ b/compiler/aliases.nim
@@ -95,7 +95,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
   if a.kind == b.kind:
     case a.kind
     of nkSym:
-      const varKinds = {skVar, skTemp, skProc}
+      const varKinds = {skVar, skTemp, skProc, skFunc}
       # same symbol: aliasing:
       if a.sym.id == b.sym.id: result = arYes
       elif a.sym.kind in varKinds or b.sym.kind in varKinds:
diff --git a/compiler/ast.nim b/compiler/ast.nim
index f9b43ed13..38cfcf77f 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -221,6 +221,8 @@ type
     nkGotoState,          # used for the state machine (for iterators)
     nkState,              # give a label to a code section (for iterators)
     nkBreakState,         # special break statement for easier code generation
+    nkFuncDef             # a func
+
   TNodeKinds* = set[TNodeKind]
 
 type
@@ -352,7 +354,7 @@ type
     tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers
     tyFloat, tyFloat32, tyFloat64, tyFloat128,
     tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64,
-    tyUnused0, tyUnused1, tyUnused2,
+    tyOptAsRef, tyUnused1, tyUnused2,
     tyVarargs,
     tyUnused,
     tyProxy # used as errornous type (for idetools)
@@ -402,14 +404,8 @@ type
       # instantiation and prior to this it has the potential to
       # be any type.
 
-    tyFieldAccessor
-      # Expressions such as Type.field (valid in contexts such
-      # as the `is` operator and magics like `high` and `low`).
-      # Could be lifted to a single argument proc returning the
-      # field value.
-      # sons[0]: type of containing object or tuple
-      # sons[1]: field type
-      # .n: nkDotExpr storing the field name
+    tyOpt
+      # Builtin optional type
 
     tyVoid
       # now different from tyEmpty, hurray!
@@ -534,6 +530,7 @@ type
     skConst,              # a constant
     skResult,             # special 'result' variable
     skProc,               # a proc
+    skFunc,               # a func
     skMethod,             # a method
     skIterator,           # an iterator
     skConverter,          # a type converter
@@ -552,7 +549,7 @@ type
   TSymKinds* = set[TSymKind]
 
 const
-  routineKinds* = {skProc, skMethod, skIterator,
+  routineKinds* = {skProc, skFunc, skMethod, skIterator,
                    skConverter, skMacro, skTemplate}
   tfIncompleteStruct* = tfVarargs
   tfUncheckedArray* = tfVarargs
@@ -621,7 +618,7 @@ type
     mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
     mNewString, mNewStringOfCap, mParseBiggestFloat,
     mReset,
-    mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
+    mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs,
     mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
     mOrdinal,
     mInt, mInt8, mInt16, mInt32, mInt64,
@@ -631,7 +628,7 @@ type
     mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc,
     mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn, mDeepCopy,
     mIsMainModule, mCompileDate, mCompileTime, mProcCall,
-    mCpuEndian, mHostOS, mHostCPU, mAppType,
+    mCpuEndian, mHostOS, mHostCPU, mBuildOS, mBuildCPU, mAppType,
     mNaN, mInf, mNegInf,
     mCompileOption, mCompileOptionArg,
     mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind,
@@ -754,9 +751,9 @@ type
   TLocFlags* = set[TLocFlag]
   TLoc* = object
     k*: TLocKind              # kind of location
-    s*: TStorageLoc
+    storage*: TStorageLoc
     flags*: TLocFlags         # location's flags
-    t*: PType                 # type of location
+    lode*: PNode              # Node where the location came from; can be faked
     r*: Rope                  # rope value of location (code generators)
     dup*: Rope                # duplicated location for precise stack scans
 
@@ -934,7 +931,7 @@ type
 # the poor naming choices in the standard library.
 
 const
-  OverloadableSyms* = {skProc, skMethod, skIterator,
+  OverloadableSyms* = {skProc, skFunc, skMethod, skIterator,
     skConverter, skModule, skTemplate, skMacro}
 
   GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
@@ -957,7 +954,7 @@ const
                                     tyTuple, tySequence}
   NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
     tyProc, tyString, tyError}
-  ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType,
+  ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType,
     skIterator,
     skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
   PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
@@ -981,14 +978,14 @@ const
 
   nkLiterals* = {nkCharLit..nkTripleStrLit}
   nkLambdaKinds* = {nkLambda, nkDo}
-  declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef}
+  declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef}
   procDefs* = nkLambdaKinds + declarativeDefs
 
   nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice}
   nkStrKinds* = {nkStrLit..nkTripleStrLit}
 
   skLocalVars* = {skVar, skLet, skForVar, skParam, skResult}
-  skProcKinds* = {skProc, skTemplate, skMacro, skIterator,
+  skProcKinds* = {skProc, skFunc, skTemplate, skMacro, skIterator,
                   skMethod, skConverter}
 
 var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
@@ -1264,11 +1261,10 @@ proc newType*(kind: TTypeKind, owner: PSym): PType =
 
 proc mergeLoc(a: var TLoc, b: TLoc) =
   if a.k == low(a.k): a.k = b.k
-  if a.s == low(a.s): a.s = b.s
+  if a.storage == low(a.storage): a.storage = b.storage
   a.flags = a.flags + b.flags
-  if a.t == nil: a.t = b.t
+  if a.lode == nil: a.lode = b.lode
   if a.r == nil: a.r = b.r
-  #if a.a == 0: a.a = b.a
 
 proc newSons*(father: PNode, length: int) =
   if isNil(father.sons):
diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim
index d17d928c8..6972f5acf 100644
--- a/compiler/canonicalizer.nim
+++ b/compiler/canonicalizer.nim
@@ -130,7 +130,7 @@ proc hashType(c: var MD5Context, t: PType) =
     c.hashSym body.sym
     for i in countup(1, sonsLen(t) - 2):
       c.hashType t.sons[i]
-  of tyFromExpr, tyFieldAccessor:
+  of tyFromExpr:
     c.hashTree(t.n)
   of tyArray:
     c.hashTree(t.sons[0].n)
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 7493a50ca..a00e2bc77 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -56,7 +56,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
         if d.k == locNone: getTemp(p, typ.sons[0], d)
         assert(d.t != nil)        # generate an assignment to d:
         var list: TLoc
-        initLoc(list, locCall, d.t, OnUnknown)
+        initLoc(list, locCall, d.lode, OnUnknown)
         list.r = pl
         genAssignment(p, d, list, {}) # no need for deep copying
   else:
@@ -241,7 +241,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
       if d.k == locNone: getTemp(p, typ.sons[0], d)
       assert(d.t != nil)        # generate an assignment to d:
       var list: TLoc
-      initLoc(list, locCall, d.t, OnUnknown)
+      initLoc(list, locCall, d.lode, OnUnknown)
       list.r = callPattern % [op.r, pl, pl.addComma, rawProc]
       genAssignment(p, d, list, {}) # no need for deep copying
   else:
@@ -437,7 +437,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
         if d.k == locNone: getTemp(p, typ.sons[0], d)
         assert(d.t != nil)        # generate an assignment to d:
         var list: TLoc
-        initLoc(list, locCall, d.t, OnUnknown)
+        initLoc(list, locCall, d.lode, OnUnknown)
         list.r = pl
         genAssignment(p, d, list, {}) # no need for deep copying
     else:
@@ -519,7 +519,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
       if d.k == locNone: getTemp(p, typ.sons[0], d)
       assert(d.t != nil)        # generate an assignment to d:
       var list: TLoc
-      initLoc(list, locCall, nil, OnUnknown)
+      initLoc(list, locCall, ri, OnUnknown)
       list.r = pl
       genAssignment(p, d, list, {}) # no need for deep copying
   else:
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index f5c793d29..88944aea6 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -157,10 +157,23 @@ proc getStorageLoc(n: PNode): TStorageLoc =
     result = getStorageLoc(n.sons[0])
   else: result = OnUnknown
 
+proc canMove(n: PNode): bool =
+  # for now we're conservative here:
+  if n.kind == nkBracket:
+    # This needs to be kept consistent with 'const' seq code
+    # generation!
+    if not isDeepConstExpr(n) or n.len == 0:
+      if skipTypes(n.typ, abstractVarRange).kind == tySequence:
+        return true
+  result = n.kind in nkCallKinds
+  #if result:
+  #  echo n.info, " optimized ", n
+  #  result = false
+
 proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
-  if dest.s == OnStack or not usesNativeGC():
+  if dest.storage == OnStack or not usesNativeGC():
     linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
-  elif dest.s == OnHeap:
+  elif dest.storage == OnHeap:
     # location is on heap
     # now the writer barrier is inlined for performance:
     #
@@ -202,13 +215,13 @@ proc asgnComplexity(n: PNode): int =
 proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc =
   assert field != nil
   result.k = locField
-  result.s = a.s
-  result.t = t
+  result.storage = a.storage
+  result.lode = lodeTyp t
   result.r = rdLoc(a) & "." & field
 
 proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   let newflags =
-    if src.s == OnStatic:
+    if src.storage == OnStatic:
       flags + {needToCopy}
     elif tfShallow in dest.t.flags:
       flags - {needToCopy}
@@ -225,7 +238,7 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
                       t: PNode, typ: PType) =
   if t == nil: return
   let newflags =
-    if src.s == OnStatic:
+    if src.storage == OnStatic:
       flags + {needToCopy}
     elif tfShallow in dest.t.flags:
       flags - {needToCopy}
@@ -250,7 +263,7 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   # (for objects, etc.):
   if needToCopy notin flags or
       tfShallow in skipTypes(dest.t, abstractVarRange).flags:
-    if dest.s == OnStack or not usesNativeGC():
+    if dest.storage == OnStack or not usesNativeGC():
       useStringh(p.module)
       linefmt(p, cpsStmts,
            "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
@@ -274,18 +287,18 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   of tyRef:
     genRefAssign(p, dest, src, flags)
   of tySequence:
-    if needToCopy notin flags and src.s != OnStatic:
+    if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
       genRefAssign(p, dest, src, flags)
     else:
       linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n",
               addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t))
   of tyString:
-    if needToCopy notin flags and src.s != OnStatic:
+    if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
       genRefAssign(p, dest, src, flags)
     else:
-      if dest.s == OnStack or not usesNativeGC():
+      if dest.storage == OnStack or not usesNativeGC():
         linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc)
-      elif dest.s == OnHeap:
+      elif dest.storage == OnHeap:
         # we use a temporary to care for the dreaded self assignment:
         var tmp: TLoc
         getTemp(p, ty, tmp)
@@ -357,7 +370,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
   else: internalError("genAssignment: " & $ty.kind)
 
-  if optMemTracker in p.options and dest.s in {OnHeap, OnUnknown}:
+  if optMemTracker in p.options and dest.storage in {OnHeap, OnUnknown}:
     #writeStackTrace()
     #echo p.currLineInfo, " requesting"
     linefmt(p, cpsStmts, "#memTrackerWrite((void*)$1, $2, $3, $4);$n",
@@ -407,11 +420,11 @@ proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) =
   else:
     d = s # ``d`` is free, so fill it with ``s``
 
-proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) =
+proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) =
   var a: TLoc
   if d.k != locNone:
     # need to generate an assignment here
-    initLoc(a, locData, t, OnStatic)
+    initLoc(a, locData, n, OnStatic)
     a.r = r
     if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
     else: genAssignment(p, d, a, {needToCopy})
@@ -419,14 +432,14 @@ proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) =
     # we cannot call initLoc() here as that would overwrite
     # the flags field!
     d.k = locData
-    d.t = t
+    d.lode = n
     d.r = r
 
-proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope; s=OnUnknown) =
+proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) =
   var a: TLoc
   if d.k != locNone:
     # need to generate an assignment here
-    initLoc(a, locExpr, t, s)
+    initLoc(a, locExpr, n, s)
     a.r = r
     if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
     else: genAssignment(p, d, a, {needToCopy})
@@ -434,7 +447,7 @@ proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope; s=OnUnknown) =
     # we cannot call initLoc() here as that would overwrite
     # the flags field!
     d.k = locExpr
-    d.t = t
+    d.lode = n
     d.r = r
 
 proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
@@ -456,7 +469,7 @@ proc binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   assert(e.sons[2].typ != nil)
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
-  putIntoDest(p, d, e.typ, ropecg(p.module, frmt, [rdLoc(a), rdLoc(b)]))
+  putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a), rdLoc(b)]))
 
 proc binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   var a, b: TLoc
@@ -464,17 +477,17 @@ proc binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   assert(e.sons[2].typ != nil)
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
-  putIntoDest(p, d, e.typ, ropecg(p.module, frmt, [a.rdCharLoc, b.rdCharLoc]))
+  putIntoDest(p, d, e, ropecg(p.module, frmt, [a.rdCharLoc, b.rdCharLoc]))
 
 proc unaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   var a: TLoc
   initLocExpr(p, e.sons[1], a)
-  putIntoDest(p, d, e.typ, ropecg(p.module, frmt, [rdLoc(a)]))
+  putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a)]))
 
 proc unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   var a: TLoc
   initLocExpr(p, e.sons[1], a)
-  putIntoDest(p, d, e.typ, ropecg(p.module, frmt, [rdCharLoc(a)]))
+  putIntoDest(p, d, e, ropecg(p.module, frmt, [rdCharLoc(a)]))
 
 proc binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc;
                             frmt: string): Rope =
@@ -514,11 +527,11 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   let t = e.typ.skipTypes(abstractRange)
   if optOverflowCheck notin p.options:
     let res = opr[m] % [getTypeDesc(p.module, e.typ), rdLoc(a), rdLoc(b)]
-    putIntoDest(p, d, e.typ, res)
+    putIntoDest(p, d, e, res)
   else:
     let res = binaryArithOverflowRaw(p, t, a, b,
                                    if t.kind == tyInt64: prc64[m] else: prc[m])
-    putIntoDest(p, d, e.typ, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])
+    putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])
 
 proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   const
@@ -535,7 +548,7 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   if optOverflowCheck in p.options:
     linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n",
             rdLoc(a), intLiteral(firstOrd(t)))
-  putIntoDest(p, d, e.typ, opr[m] % [rdLoc(a), rope(getSize(t) * 8)])
+  putIntoDest(p, d, e, opr[m] % [rdLoc(a), rope(getSize(t) * 8)])
 
 proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   const
@@ -593,7 +606,7 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   # BUGFIX: cannot use result-type here, as it may be a boolean
   s = max(getSize(a.t), getSize(b.t)) * 8
   k = getSize(a.t) * 8
-  putIntoDest(p, d, e.typ,
+  putIntoDest(p, d, e,
               binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s),
                                       getSimpleTypeDesc(p.module, e.typ), rope(k)])
 
@@ -604,10 +617,10 @@ proc genEqProc(p: BProc, e: PNode, d: var TLoc) =
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
   if a.t.skipTypes(abstractInst).callConv == ccClosure:
-    putIntoDest(p, d, e.typ,
+    putIntoDest(p, d, e,
       "($1.ClP_0 == $2.ClP_0 && $1.ClE_0 == $2.ClE_0)" % [rdLoc(a), rdLoc(b)])
   else:
-    putIntoDest(p, d, e.typ, "($1 == $2)" % [rdLoc(a), rdLoc(b)])
+    putIntoDest(p, d, e, "($1 == $2)" % [rdLoc(a), rdLoc(b)])
 
 proc genIsNil(p: BProc, e: PNode, d: var TLoc) =
   let t = skipTypes(e.sons[1].typ, abstractRange)
@@ -644,7 +657,7 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   assert(e.sons[1].typ != nil)
   initLocExpr(p, e.sons[1], a)
   t = skipTypes(e.typ, abstractRange)
-  putIntoDest(p, d, e.typ,
+  putIntoDest(p, d, e,
               unArithTab[op] % [rdLoc(a), rope(getSize(t) * 8),
                 getSimpleTypeDesc(p.module, e.typ)])
 
@@ -662,7 +675,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
     #  message(e.info, warnUser, "CAME HERE " & renderTree(e))
     expr(p, e.sons[0], d)
     if e.sons[0].typ.skipTypes(abstractInst).kind == tyRef:
-      d.s = OnHeap
+      d.storage = OnHeap
   else:
     var a: TLoc
     var typ = skipTypes(e.sons[0].typ, abstractInst)
@@ -675,25 +688,25 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
       initLocExprSingleUse(p, e.sons[0], a)
     if d.k == locNone:
       # dest = *a;  <-- We do not know that 'dest' is on the heap!
-      # It is completely wrong to set 'd.s' here, unless it's not yet
+      # It is completely wrong to set 'd.storage' here, unless it's not yet
       # been assigned to.
       case typ.kind
       of tyRef:
-        d.s = OnHeap
+        d.storage = OnHeap
       of tyVar:
-        d.s = OnUnknown
+        d.storage = OnUnknown
         if tfVarIsPtr notin typ.flags and p.module.compileToCpp and
             e.kind == nkHiddenDeref:
-          putIntoDest(p, d, e.typ, rdLoc(a), a.s)
+          putIntoDest(p, d, e, rdLoc(a), a.storage)
           return
       of tyPtr:
-        d.s = OnUnknown         # BUGFIX!
+        d.storage = OnUnknown         # BUGFIX!
       else:
         internalError(e.info, "genDeref " & $typ.kind)
     elif p.module.compileToCpp:
       if typ.kind == tyVar and tfVarIsPtr notin typ.flags and
            e.kind == nkHiddenDeref:
-        putIntoDest(p, d, e.typ, rdLoc(a), a.s)
+        putIntoDest(p, d, e, rdLoc(a), a.storage)
         return
     if enforceDeref and mt == ctPtrToArray:
       # we lie about the type for better C interop: 'ptr array[3,T]' is
@@ -701,26 +714,26 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
       # See tmissingderef. So we get rid of the deref instead. The codegen
       # ends up using 'memcpy' for the array assignment,
       # so the '&' and '*' cancel out:
-      putIntoDest(p, d, a.t.sons[0], rdLoc(a), a.s)
+      putIntoDest(p, d, lodeTyp(a.t.sons[0]), rdLoc(a), a.storage)
     else:
-      putIntoDest(p, d, e.typ, "(*$1)" % [rdLoc(a)], a.s)
+      putIntoDest(p, d, e, "(*$1)" % [rdLoc(a)], a.storage)
 
 proc genAddr(p: BProc, e: PNode, d: var TLoc) =
   # careful  'addr(myptrToArray)' needs to get the ampersand:
   if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
     var a: TLoc
     initLocExpr(p, e.sons[0], a)
-    putIntoDest(p, d, e.typ, "&" & a.r, a.s)
+    putIntoDest(p, d, e, "&" & a.r, a.storage)
     #Message(e.info, warnUser, "HERE NEW &")
   elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ):
     expr(p, e.sons[0], d)
   else:
     var a: TLoc
     initLocExpr(p, e.sons[0], a)
-    putIntoDest(p, d, e.typ, addrLoc(a), a.s)
+    putIntoDest(p, d, e, addrLoc(a), a.storage)
 
 template inheritLocation(d: var TLoc, a: TLoc) =
-  if d.k == locNone: d.s = a.s
+  if d.k == locNone: d.storage = a.storage
 
 proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) =
   initLocExpr(p, e.sons[0], a)
@@ -742,7 +755,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
   of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal)
   else: internalError(e.info, "genTupleElem")
   addf(r, ".Field$1", [rope(i)])
-  putIntoDest(p, d, tupType.sons[i], r, a.s)
+  putIntoDest(p, d, e, r, a.storage)
 
 proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope;
                       resTyp: ptr PType = nil): PSym =
@@ -769,15 +782,14 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
     # we found a unique tuple type which lacks field information
     # so we use Field$i
     addf(r, ".Field$1", [rope(f.position)])
-    putIntoDest(p, d, f.typ, r, a.s)
+    putIntoDest(p, d, e, r, a.storage)
   else:
     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)
-  #d.s = a.s
+    putIntoDest(p, d, e, r, a.storage)
 
 proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc)
 
@@ -792,11 +804,11 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym;
     if op.magic == mNot: it = it.sons[1]
     let disc = it.sons[2].skipConv
     assert(disc.kind == nkSym)
-    initLoc(test, locNone, it.typ, OnStack)
+    initLoc(test, locNone, it, OnStack)
     initLocExpr(p, it.sons[1], u)
     var o = obj
     let d = lookupFieldAgain(p, origTy, disc.sym, o)
-    initLoc(v, locExpr, d.typ, OnUnknown)
+    initLoc(v, locExpr, disc, OnUnknown)
     v.r = o
     v.r.add(".")
     v.r.add(d.loc.r)
@@ -827,11 +839,11 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
       internalError(e.info, "genCheckedRecordField") # generate the checks:
     genFieldCheck(p, e, r, field, ty)
     add(r, rfmt(nil, ".$1", field.loc.r))
-    putIntoDest(p, d, field.typ, r, a.s)
+    putIntoDest(p, d, e.sons[0], r, a.storage)
   else:
     genRecordField(p, e.sons[0], d)
 
-proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
+proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   var a, b: TLoc
   initLocExpr(p, x, a)
   initLocExpr(p, y, b)
@@ -853,30 +865,30 @@ proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
       if idx < firstOrd(ty) or idx > lastOrd(ty):
         localError(x.info, errIndexOutOfBounds)
   d.inheritLocation(a)
-  putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)),
-              rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.s)
+  putIntoDest(p, d, n,
+              rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.storage)
 
-proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) =
+proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   var a, b: TLoc
   initLocExpr(p, x, a)
   initLocExpr(p, y, b)
   var ty = skipTypes(a.t, abstractVarRange)
-  if d.k == locNone: d.s = a.s
-  putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)),
-              rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s)
+  inheritLocation(d, a)
+  putIntoDest(p, d, n,
+              rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
 
-proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) =
+proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   var a, b: TLoc
   initLocExpr(p, x, a)
   initLocExpr(p, y, b) # emit range check:
   if optBoundsCheck in p.options:
     linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError();$n",
             rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``!
-  if d.k == locNone: d.s = a.s
-  putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)),
-              rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s)
+  inheritLocation(d, a)
+  putIntoDest(p, d, n,
+              rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
 
-proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) =
+proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   var a, b: TLoc
   initLocExpr(p, x, a)
   initLocExpr(p, y, b)
@@ -892,20 +904,20 @@ proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) =
       linefmt(p, cpsStmts,
            "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
            rdLoc(b), rdLoc(a), lenField(p))
-  if d.k == locNone: d.s = OnHeap
+  if d.k == locNone: d.storage = OnHeap
   if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
     a.r = rfmt(nil, "(*$1)", a.r)
-  putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)),
-              rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.s)
+  putIntoDest(p, d, n,
+              rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
 
 proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
   var ty = skipTypes(n.sons[0].typ, abstractVarRange + tyUserTypeClasses)
   if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange)
   case ty.kind
-  of tyArray: genArrayElem(p, n.sons[0], n.sons[1], d)
-  of tyOpenArray, tyVarargs: genOpenArrayElem(p, n.sons[0], n.sons[1], d)
-  of tySequence, tyString: genSeqElem(p, n.sons[0], n.sons[1], d)
-  of tyCString: genCStringElem(p, n.sons[0], n.sons[1], d)
+  of tyArray: genArrayElem(p, n, n.sons[0], n.sons[1], d)
+  of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, n.sons[0], n.sons[1], d)
+  of tySequence, tyString: genSeqElem(p, n, n.sons[0], n.sons[1], d)
+  of tyCString: genCStringElem(p, n, n.sons[0], n.sons[1], d)
   of tyTuple: genTupleElem(p, n, d)
   else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')')
 
@@ -1071,7 +1083,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
       getTypeDesc(p.module, bt)])
   #if bt != b.t:
   #  echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
-  initLoc(dest, locExpr, bt, OnHeap)
+  initLoc(dest, locExpr, e.sons[2], OnHeap)
   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)
@@ -1088,7 +1100,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
   var sizeExpr = sizeExpr
   let typ = a.t
   var b: TLoc
-  initLoc(b, locExpr, a.t, OnHeap)
+  initLoc(b, locExpr, a.lode, OnHeap)
   let refType = typ.skipTypes(abstractInst)
   assert refType.kind == tyRef
   let bt = refType.lastSon
@@ -1098,7 +1110,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
   let args = [getTypeDesc(p.module, typ),
               genTypeInfo(p.module, typ),
               sizeExpr]
-  if a.s == OnHeap and usesNativeGC():
+  if a.storage == OnHeap and usesNativeGC():
     # use newObjRC1 as an optimization
     if canFormAcycle(a.t):
       linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc)
@@ -1128,8 +1140,8 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
   let args = [getTypeDesc(p.module, seqtype),
               genTypeInfo(p.module, seqtype), length]
   var call: TLoc
-  initLoc(call, locExpr, dest.t, OnHeap)
-  if dest.s == OnHeap and usesNativeGC():
+  initLoc(call, locExpr, dest.lode, OnHeap)
+  if dest.storage == OnHeap and usesNativeGC():
     if canFormAcycle(dest.t):
       linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc)
     else:
@@ -1151,7 +1163,7 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
   let seqtype = skipTypes(e.typ, abstractVarRange)
   var a: TLoc
   initLocExpr(p, e.sons[1], a)
-  putIntoDest(p, d, e.typ, ropecg(p.module,
+  putIntoDest(p, d, e, ropecg(p.module,
               "($1)#nimNewSeqOfCap($2, $3)", [
               getTypeDesc(p.module, seqtype),
               genTypeInfo(p.module, seqtype), a.rdLoc]))
@@ -1163,7 +1175,7 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
     let t = n.typ
     discard getTypeDesc(p.module, t) # so that any fields are initialized
     let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
-    fillLoc(d, locData, t, p.module.tmpBase & rope(id), OnStatic)
+    fillLoc(d, locData, n, p.module.tmpBase & rope(id), OnStatic)
     if id == p.module.labels:
       # expression not found in the cache:
       inc(p.module.labels)
@@ -1205,8 +1217,8 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
     add(tmp2.r, ".")
     add(tmp2.r, field.loc.r)
     tmp2.k = locTemp
-    tmp2.t = field.loc.t
-    tmp2.s = if isRef: OnHeap else: OnStack
+    tmp2.lode = it.sons[1]
+    tmp2.storage = if isRef: OnHeap else: OnStack
     expr(p, it.sons[1], tmp2)
 
   if d.k == locNone:
@@ -1214,37 +1226,37 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
   else:
     genAssignment(p, d, tmp, {})
 
-proc genSeqConstr(p: BProc, t: PNode, d: var TLoc) =
+proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
   var arr: TLoc
   if d.k == locNone:
-    getTemp(p, t.typ, d)
+    getTemp(p, n.typ, d)
   # generate call to newSeq before adding the elements per hand:
-  genNewSeqAux(p, d, intLiteral(sonsLen(t)))
-  for i in countup(0, sonsLen(t) - 1):
-    initLoc(arr, locExpr, elemType(skipTypes(t.typ, typedescInst)), OnHeap)
+  genNewSeqAux(p, d, intLiteral(sonsLen(n)))
+  for i in countup(0, sonsLen(n) - 1):
+    initLoc(arr, locExpr, n[i], OnHeap)
     arr.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i))
-    arr.s = OnHeap            # we know that sequences are on the heap
-    expr(p, t.sons[i], arr)
-  gcUsage(t)
+    arr.storage = OnHeap            # we know that sequences are on the heap
+    expr(p, n[i], arr)
+  gcUsage(n)
 
-proc genArrToSeq(p: BProc, t: PNode, d: var TLoc) =
+proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
   var elem, a, arr: TLoc
-  if t.sons[1].kind == nkBracket:
-    t.sons[1].typ = t.typ
-    genSeqConstr(p, t.sons[1], d)
+  if n.sons[1].kind == nkBracket:
+    n.sons[1].typ = n.typ
+    genSeqConstr(p, n.sons[1], d)
     return
   if d.k == locNone:
-    getTemp(p, t.typ, d)
+    getTemp(p, n.typ, d)
   # generate call to newSeq before adding the elements per hand:
-  var L = int(lengthOrd(t.sons[1].typ))
+  var L = int(lengthOrd(n.sons[1].typ))
 
   genNewSeqAux(p, d, intLiteral(L))
-  initLocExpr(p, t.sons[1], a)
+  initLocExpr(p, n.sons[1], a)
   for i in countup(0, L - 1):
-    initLoc(elem, locExpr, elemType(skipTypes(t.typ, abstractInst)), OnHeap)
+    initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
     elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i))
-    elem.s = OnHeap # we know that sequences are on the heap
-    initLoc(arr, locExpr, elemType(skipTypes(t.sons[1].typ, abstractInst)), a.s)
+    elem.storage = OnHeap # we know that sequences are on the heap
+    initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage)
     arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i))
     genAssignment(p, elem, arr, {afDestIsNil, needToCopy})
 
@@ -1256,7 +1268,7 @@ proc genNewFinalize(p: BProc, e: PNode) =
   refType = skipTypes(e.sons[1].typ, abstractVarRange)
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], f)
-  initLoc(b, locExpr, a.t, OnHeap)
+  initLoc(b, locExpr, a.lode, OnHeap)
   ti = genTypeInfo(p.module, refType)
   addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
   b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
@@ -1308,7 +1320,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
     r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r))
   else:
     r = rfmt(p.module, "($1)", genOfHelper(p, dest, r))
-  putIntoDest(p, d, getSysType(tyBool), r, a.s)
+  putIntoDest(p, d, x, r, a.storage)
 
 proc genOf(p: BProc, n: PNode, d: var TLoc) =
   genOf(p, n.sons[1], n.sons[2].typ, d)
@@ -1319,52 +1331,53 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
   var t = skipTypes(e.sons[1].typ, abstractVarRange)
   case t.kind
   of tyInt..tyInt64, tyUInt..tyUInt64:
-    putIntoDest(p, d, e.typ,
-                ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.s)
+    putIntoDest(p, d, e,
+                ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.storage)
   of tyFloat..tyFloat128:
-    putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]), a.s)
+    putIntoDest(p, d, e, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]), a.storage)
   of tyBool:
-    putIntoDest(p, d, e.typ, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]), a.s)
+    putIntoDest(p, d, e, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]), a.storage)
   of tyChar:
-    putIntoDest(p, d, e.typ, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.s)
+    putIntoDest(p, d, e, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.storage)
   of tyEnum, tyOrdinal:
-    putIntoDest(p, d, e.typ,
+    putIntoDest(p, d, e,
                 ropecg(p.module, "#reprEnum((NI)$1, $2)", [
-                rdLoc(a), genTypeInfo(p.module, t)]), a.s)
+                rdLoc(a), genTypeInfo(p.module, t)]), a.storage)
   of tyString:
-    putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.s)
+    putIntoDest(p, d, e, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.storage)
   of tySet:
-    putIntoDest(p, d, e.typ, ropecg(p.module, "#reprSet($1, $2)", [
-                addrLoc(a), genTypeInfo(p.module, t)]), a.s)
+    putIntoDest(p, d, e, ropecg(p.module, "#reprSet($1, $2)", [
+                addrLoc(a), genTypeInfo(p.module, t)]), a.storage)
   of tyOpenArray, tyVarargs:
     var b: TLoc
     case a.t.kind
     of tyOpenArray, tyVarargs:
-      putIntoDest(p, b, e.typ, "$1, $1Len_0" % [rdLoc(a)], a.s)
+      putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
     of tyString, tySequence:
-      putIntoDest(p, b, e.typ,
-                  "$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.s)
+      putIntoDest(p, b, e,
+                  "$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.storage)
     of tyArray:
-      putIntoDest(p, b, e.typ,
-                  "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.s)
+      putIntoDest(p, b, e,
+                  "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.storage)
     else: internalError(e.sons[0].info, "genRepr()")
-    putIntoDest(p, d, e.typ,
+    putIntoDest(p, d, e,
         ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b),
-        genTypeInfo(p.module, elemType(t))]), a.s)
+        genTypeInfo(p.module, elemType(t))]), a.storage)
   of tyCString, tyArray, tyRef, tyPtr, tyPointer, tyNil, tySequence:
-    putIntoDest(p, d, e.typ,
+    putIntoDest(p, d, e,
                 ropecg(p.module, "#reprAny($1, $2)", [
-                rdLoc(a), genTypeInfo(p.module, t)]), a.s)
+                rdLoc(a), genTypeInfo(p.module, t)]), a.storage)
   of tyEmpty, tyVoid:
     localError(e.info, "'repr' doesn't support 'void' type")
   else:
-    putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)",
-                                   [addrLoc(a), genTypeInfo(p.module, t)]), a.s)
+    putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)",
+                              [addrLoc(a), genTypeInfo(p.module, t)]),
+                               a.storage)
   gcUsage(e)
 
 proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) =
   let t = e.sons[1].typ
-  putIntoDest(p, d, e.typ, genTypeInfo(p.module, t))
+  putIntoDest(p, d, e, genTypeInfo(p.module, t))
 
 proc genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
   var a: TLoc
@@ -1409,11 +1422,11 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       else:
         frmt = "$1 = ($2 ? $2->len : 0);$n"
     lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a))
-    putIntoDest(p, d, e.typ, tmp.r)
+    putIntoDest(p, d, e, tmp.r)
   of tyArray:
     # YYY: length(sideeffect) is optimized away incorrectly?
-    if op == mHigh: putIntoDest(p, d, e.typ, rope(lastOrd(typ)))
-    else: putIntoDest(p, d, e.typ, rope(lengthOrd(typ)))
+    if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(typ)))
+    else: putIntoDest(p, d, e, rope(lengthOrd(typ)))
   else: internalError(e.info, "genArrayLen()")
 
 proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
@@ -1471,7 +1484,7 @@ proc fewCmps(s: PNode): bool =
     result = sonsLen(s) <= 8  # 8 seems to be a good value
 
 proc binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) =
-  putIntoDest(p, d, e.typ, frmt % [rdLoc(a), rdSetElemLoc(b, a.t)])
+  putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(b, a.t)])
 
 proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
   case int(getSize(skipTypes(e.sons[1].typ, abstractVar)))
@@ -1500,7 +1513,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
              else:
                e.sons[2]
     initLocExpr(p, ea, a)
-    initLoc(b, locExpr, e.typ, OnUnknown)
+    initLoc(b, locExpr, e, OnUnknown)
     b.r = rope("(")
     var length = sonsLen(e.sons[1])
     for i in countup(0, length - 1):
@@ -1514,7 +1527,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
         addf(b.r, "$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
       if i < length - 1: add(b.r, " || ")
     add(b.r, ")")
-    putIntoDest(p, d, e.typ, b.r)
+    putIntoDest(p, d, e, b.r)
   else:
     assert(e.sons[1].typ != nil)
     assert(e.sons[2].typ != nil)
@@ -1599,14 +1612,14 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
   initLocExpr(p, e.sons[1], a)
   let etyp = skipTypes(e.typ, abstractRange)
   if etyp.kind in ValueTypes and lfIndirect notin a.flags:
-    putIntoDest(p, d, e.typ, "(*($1*) ($2))" %
-        [getTypeDesc(p.module, e.typ), addrLoc(a)], a.s)
+    putIntoDest(p, d, e, "(*($1*) ($2))" %
+        [getTypeDesc(p.module, e.typ), addrLoc(a)], a.storage)
   elif etyp.kind == tyProc and etyp.callConv == ccClosure:
-    putIntoDest(p, d, e.typ, "(($1) ($2))" %
-        [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.s)
+    putIntoDest(p, d, e, "(($1) ($2))" %
+        [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
   else:
-    putIntoDest(p, d, e.typ, "(($1) ($2))" %
-        [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.s)
+    putIntoDest(p, d, e, "(($1) ($2))" %
+        [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
 
 proc genCast(p: BProc, e: PNode, d: var TLoc) =
   const ValueTypes = {tyFloat..tyFloat128, tyTuple, tyObject, tyArray}
@@ -1622,11 +1635,11 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
     linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n",
       getTypeDesc(p.module, e.sons[1].typ), getTypeDesc(p.module, e.typ), lbl)
     tmp.k = locExpr
-    tmp.t = srct
-    tmp.s = OnStack
+    tmp.lode = lodeTyp srct
+    tmp.storage = OnStack
     tmp.flags = {}
     expr(p, e.sons[1], tmp)
-    putIntoDest(p, d, e.typ, "LOC$#.dest" % [lbl], tmp.s)
+    putIntoDest(p, d, e, "LOC$#.dest" % [lbl], tmp.storage)
   else:
     # I prefer the shorter cast version for pointer types -> generate less
     # C code; plus it's the right thing to do for closures:
@@ -1639,14 +1652,14 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) =
   if optRangeCheck notin p.options or dest.skipTypes({tyRange}).kind in
                                              {tyUInt..tyUInt64}:
     initLocExpr(p, n.sons[0], a)
-    putIntoDest(p, d, n.typ, "(($1) ($2))" %
-        [getTypeDesc(p.module, dest), rdCharLoc(a)], a.s)
+    putIntoDest(p, d, n, "(($1) ($2))" %
+        [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
   else:
     initLocExpr(p, n.sons[0], a)
-    putIntoDest(p, d, dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [
+    putIntoDest(p, d, lodeTyp dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [
         getTypeDesc(p.module, dest), rdCharLoc(a),
         genLiteral(p, n.sons[1], dest), genLiteral(p, n.sons[2], dest),
-        rope(magic)]), a.s)
+        rope(magic)]), a.storage)
 
 proc genConv(p: BProc, e: PNode, d: var TLoc) =
   let destType = e.typ.skipTypes({tyVar, tyGenericInst, tyAlias})
@@ -1658,13 +1671,15 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) =
 proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
   var a: TLoc
   initLocExpr(p, n.sons[0], a)
-  putIntoDest(p, d, skipTypes(n.typ, abstractVar), "$1->data" % [rdLoc(a)], a.s)
+  putIntoDest(p, d, n, "$1->data" % [rdLoc(a)],
+              a.storage)
 
 proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
   var a: TLoc
   initLocExpr(p, n.sons[0], a)
-  putIntoDest(p, d, skipTypes(n.typ, abstractVar),
-              ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), a.s)
+  putIntoDest(p, d, n,
+              ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]),
+              a.storage)
   gcUsage(n)
 
 proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
@@ -1675,11 +1690,11 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
     binaryExpr(p, e, d, "($1 == $2)")
   elif (a.kind in {nkStrLit..nkTripleStrLit}) and (a.strVal == ""):
     initLocExpr(p, e.sons[2], x)
-    putIntoDest(p, d, e.typ,
+    putIntoDest(p, d, e,
       rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p)))
   elif (b.kind in {nkStrLit..nkTripleStrLit}) and (b.strVal == ""):
     initLocExpr(p, e.sons[1], x)
-    putIntoDest(p, d, e.typ,
+    putIntoDest(p, d, e,
       rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p)))
   else:
     binaryExpr(p, e, d, "#eqStrings($1, $2)")
@@ -1692,9 +1707,9 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
     assert(e.sons[2].typ != nil)
     initLocExpr(p, e.sons[1], a)
     initLocExpr(p, e.sons[2], b)
-    putIntoDest(p, d, e.typ, rfmt(nil, "(($4)($2) $1 ($4)($3))",
-                                  rope(opr[m]), rdLoc(a), rdLoc(b),
-                                  getSimpleTypeDesc(p.module, e[1].typ)))
+    putIntoDest(p, d, e, rfmt(nil, "(($4)($2) $1 ($4)($3))",
+                              rope(opr[m]), rdLoc(a), rdLoc(b),
+                              getSimpleTypeDesc(p.module, e[1].typ)))
     if optNaNCheck in p.options:
       linefmt(p, cpsStmts, "#nanCheck($1);$n", rdLoc(d))
     if optInfCheck in p.options:
@@ -1736,7 +1751,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       let ranged = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tyVar})
       let res = binaryArithOverflowRaw(p, ranged, a, b,
         if underlying.kind == tyInt64: fun64[op] else: fun[op])
-      putIntoDest(p, a, ranged, "($#)($#)" % [
+      putIntoDest(p, a, e.sons[1], "($#)($#)" % [
         getTypeDesc(p.module, ranged), res])
 
   of mConStrStr: genStrConcat(p, e, d)
@@ -1762,7 +1777,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mNewSeqOfCap: genNewSeqOfCap(p, e, d)
   of mSizeOf:
     let t = e.sons[1].typ.skipTypes({tyTypeDesc})
-    putIntoDest(p, d, e.typ, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)])
+    putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)])
   of mChr: genSomeCast(p, e, d)
   of mOrd: genOrd(p, e, d)
   of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray:
@@ -1783,7 +1798,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     else:
       frmt = "$1 = $2->len;$n"
     lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a))
-    putIntoDest(p, d, e.typ, tmp.r)
+    putIntoDest(p, d, e, 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)
@@ -1825,7 +1840,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
   var
     a, b, idx: TLoc
   if nfAllConst in e.flags:
-    putIntoDest(p, d, e.typ, genSetNode(p, e))
+    putIntoDest(p, d, e, genSetNode(p, e))
   else:
     if d.k == locNone: getTemp(p, e.typ, d)
     if getSize(e.typ) > 8:
@@ -1872,7 +1887,7 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
     for i in countup(0, sonsLen(n) - 1):
       var it = n.sons[i]
       if it.kind == nkExprColonExpr: it = it.sons[1]
-      initLoc(rec, locExpr, it.typ, d.s)
+      initLoc(rec, locExpr, it, d.storage)
       rec.r = "$1.Field$2" % [rdLoc(d), rope(i)]
       expr(p, it, rec)
 
@@ -1888,7 +1903,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) =
     var tmp = "CNSTCLOSURE" & rope(p.module.labels)
     addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n",
         [getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)])
-    putIntoDest(p, d, n.typ, tmp, OnStatic)
+    putIntoDest(p, d, n, tmp, OnStatic)
   else:
     var tmp, a, b: TLoc
     initLocExpr(p, n.sons[0], a)
@@ -1911,7 +1926,7 @@ proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) =
   if not handleConstExpr(p, n, d):
     if d.k == locNone: getTemp(p, n.typ, d)
     for i in countup(0, sonsLen(n) - 1):
-      initLoc(arr, locExpr, elemType(skipTypes(n.typ, abstractInst)), d.s)
+      initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), d.storage)
       arr.r = "$1[$2]" % [rdLoc(d), intLiteral(i)]
       expr(p, n.sons[i], arr)
 
@@ -1949,11 +1964,11 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
       linefmt(p, cpsStmts, "#chckObj($1.m_type, $2);$n",
               r, genTypeInfo(p.module, dest))
   if n.sons[0].typ.kind != tyObject:
-    putIntoDest(p, d, n.typ,
-                "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.s)
+    putIntoDest(p, d, n,
+                "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
   else:
-    putIntoDest(p, d, n.typ, "(*($1*) ($2))" %
-                             [getTypeDesc(p.module, dest), addrLoc(a)], a.s)
+    putIntoDest(p, d, n, "(*($1*) ($2))" %
+                        [getTypeDesc(p.module, dest), addrLoc(a)], a.storage)
 
 proc downConv(p: BProc, n: PNode, d: var TLoc) =
   if p.module.compileToCpp:
@@ -1985,9 +2000,9 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) =
         linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r)
       else:
         r = "&" & r
-        putIntoDest(p, d, n.typ, r, a.s)
+        putIntoDest(p, d, n, r, a.storage)
     else:
-      putIntoDest(p, d, n.typ, r, a.s)
+      putIntoDest(p, d, n, r, a.storage)
 
 proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
   let t = n.typ
@@ -2002,13 +2017,13 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
          [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)])
 
   if d.k == locNone:
-    fillLoc(d, locData, t, tmp, OnStatic)
+    fillLoc(d, locData, n, tmp, OnStatic)
   else:
-    putDataIntoDest(p, d, t, tmp)
+    putDataIntoDest(p, d, n, tmp)
     # This fixes bug #4551, but we really need better dataflow
     # analysis to make this 100% safe.
     if t.kind notin {tySequence, tyString}:
-      d.s = OnStatic
+      d.storage = OnStatic
 
 proc expr(p: BProc, n: PNode, d: var TLoc) =
   p.currLineInfo = n.info
@@ -2019,31 +2034,31 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     of skMethod:
       if {sfDispatcher, sfForward} * sym.flags != {}:
         # we cannot produce code for the dispatcher yet:
-        fillProcLoc(p.module, sym)
+        fillProcLoc(p.module, n)
         genProcPrototype(p.module, sym)
       else:
         genProc(p.module, sym)
       putLocIntoDest(p, d, sym.loc)
-    of skProc, skConverter, skIterator:
+    of skProc, skConverter, skIterator, skFunc:
       #if sym.kind == skIterator:
       #  echo renderTree(sym.getBody, {renderIds})
       if sfCompileTime in sym.flags:
         localError(n.info, "request to generate code for .compileTime proc: " &
            sym.name.s)
       genProc(p.module, sym)
-      if sym.loc.r == nil or sym.loc.t == nil:
+      if sym.loc.r == nil or sym.loc.lode == nil:
         internalError(n.info, "expr: proc not init " & sym.name.s)
       putLocIntoDest(p, d, sym.loc)
     of skConst:
       if isSimpleConst(sym.typ):
-        putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ), OnStatic)
+        putIntoDest(p, d, n, genLiteral(p, sym.ast, sym.typ), OnStatic)
       else:
         genComplexConst(p, sym, d)
     of skEnumField:
-      putIntoDest(p, d, n.typ, rope(sym.position))
+      putIntoDest(p, d, n, rope(sym.position))
     of skVar, skForVar, skResult, skLet:
       if {sfGlobal, sfThread} * sym.flags != {}:
-        genVarPrototype(p.module, sym)
+        genVarPrototype(p.module, n)
       if sym.loc.r == nil or sym.loc.t == nil:
         #echo "FAILED FOR PRCO ", p.prc.name.s
         #echo renderTree(p.prc.ast, {renderIds})
@@ -2051,7 +2066,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
       if sfThread in sym.flags:
         accessThreadLocalVar(p, sym)
         if emulatedThreadVars():
-          putIntoDest(p, d, sym.loc.t, "NimTV_->" & sym.loc.r)
+          putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r)
         else:
           putLocIntoDest(p, d, sym.loc)
       else:
@@ -2072,12 +2087,12 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol")
   of nkNilLit:
     if not isEmptyType(n.typ):
-      putIntoDest(p, d, n.typ, genLiteral(p, n))
+      putIntoDest(p, d, n, genLiteral(p, n))
   of nkStrLit..nkTripleStrLit:
-    putDataIntoDest(p, d, n.typ, genLiteral(p, n))
+    putDataIntoDest(p, d, n, genLiteral(p, n))
   of nkIntLit..nkUInt64Lit,
      nkFloatLit..nkFloat128Lit, nkCharLit:
-    putIntoDest(p, d, n.typ, genLiteral(p, n))
+    putIntoDest(p, d, n, genLiteral(p, n))
   of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand,
      nkCallStrLit:
     genLineDir(p, n)
@@ -2097,7 +2112,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
         genCall(p, n, d)
   of nkCurly:
     if isDeepConstExpr(n) and n.len != 0:
-      putIntoDest(p, d, n.typ, genSetNode(p, n))
+      putIntoDest(p, d, n, genSetNode(p, n))
     else:
       genSetConstr(p, n, d)
   of nkBracket:
@@ -2138,7 +2153,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
   of nkLambdaKinds:
     var sym = n.sons[namePos].sym
     genProc(p.module, sym)
-    if sym.loc.r == nil or sym.loc.t == nil:
+    if sym.loc.r == nil or sym.loc.lode == nil:
       internalError(n.info, "expr: proc not init " & sym.name.s)
     putLocIntoDest(p, d, sym.loc)
   of nkClosure: genClosure(p, n, d)
@@ -2190,7 +2205,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     discard
   of nkPragma: genPragma(p, n)
   of nkPragmaBlock: expr(p, n.lastSon, d)
-  of nkProcDef, nkMethodDef, nkConverterDef:
+  of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
     if n.sons[genericParamsPos].kind == nkEmpty:
       var prc = n.sons[namePos].sym
       # due to a bug/limitation in the lambda lifting, unused inner procs
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 8796dd729..eb32e7dd0 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -48,16 +48,17 @@ proc genVarTuple(p: BProc, n: PNode) =
   initLocExpr(p, n.sons[L-1], tup)
   var t = tup.t.skipTypes(abstractInst)
   for i in countup(0, L-3):
-    var v = n.sons[i].sym
+    let vn = n.sons[i]
+    let v = vn.sym
     if sfCompileTime in v.flags: continue
     if sfGlobal in v.flags:
-      assignGlobalVar(p, v)
+      assignGlobalVar(p, vn)
       genObjectInit(p, cpsInit, v.typ, v.loc, true)
       registerGcRoot(p, v)
     else:
-      assignLocalVar(p, v)
+      assignLocalVar(p, vn)
       initLocalVar(p, v, immediateAsgn=isAssignedImmediately(n[L-1]))
-    initLoc(field, locExpr, t.sons[i], tup.s)
+    initLoc(field, locExpr, vn, tup.storage)
     if t.kind == tyTuple:
       field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
     else:
@@ -171,7 +172,7 @@ proc genBreakState(p: BProc, n: PNode) =
     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)
+proc genVarPrototypeAux(m: BModule, n: PNode)
 
 proc genGotoVar(p: BProc; value: PNode) =
   if value.kind notin {nkCharLit..nkUInt64Lit}:
@@ -180,7 +181,8 @@ proc genGotoVar(p: BProc; value: PNode) =
     lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
 
 proc genSingleVar(p: BProc, a: PNode) =
-  let v = a.sons[0].sym
+  let vn = a.sons[0]
+  let v = vn.sym
   if sfCompileTime in v.flags: return
   if sfGoto in v.flags:
     # translate 'var state {.goto.} = X' into 'goto LX':
@@ -195,7 +197,7 @@ proc genSingleVar(p: BProc, a: PNode) =
     if sfPure in v.flags:
       # v.owner.kind != skModule:
       targetProc = p.module.preInitProc
-    assignGlobalVar(targetProc, v)
+    assignGlobalVar(targetProc, vn)
     # XXX: be careful here.
     # Global variables should not be zeromem-ed within loops
     # (see bug #20).
@@ -206,7 +208,7 @@ proc genSingleVar(p: BProc, a: PNode) =
     # Alternative construction using default constructor (which may zeromem):
     # if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc)
     if sfExportc in v.flags and p.module.g.generatedHeader != nil:
-      genVarPrototypeAux(p.module.g.generatedHeader, v)
+      genVarPrototypeAux(p.module.g.generatedHeader, vn)
     registerGcRoot(p, v)
   else:
     let value = a.sons[2]
@@ -217,7 +219,7 @@ proc genSingleVar(p: BProc, a: PNode) =
       # parameterless constructor followed by an assignment operator. So we
       # generate better code here:
       genLineDir(p, a)
-      let decl = localVarDecl(p, v)
+      let decl = localVarDecl(p, vn)
       var tmp: TLoc
       if value.kind in nkCallKinds and value[0].kind == nkSym and
            sfConstructor in value[0].sym.flags:
@@ -233,7 +235,7 @@ proc genSingleVar(p: BProc, a: PNode) =
         initLocExprSingleUse(p, value, tmp)
         lineF(p, cpsStmts, "$# = $#;$n", [decl, tmp.rdLoc])
       return
-    assignLocalVar(p, v)
+    assignLocalVar(p, vn)
     initLocalVar(p, v, imm)
 
   if a.sons[2].kind != nkEmpty:
@@ -513,7 +515,7 @@ proc genParForStmt(p: BProc, t: PNode) =
   preserveBreakIdx:
     let forLoopVar = t.sons[0].sym
     var rangeA, rangeB: TLoc
-    assignLocalVar(p, forLoopVar)
+    assignLocalVar(p, t.sons[0])
     #initLoc(forLoopVar.loc, locLocalVar, forLoopVar.typ, onStack)
     #discard mangleName(forLoopVar)
     let call = t.sons[1]
@@ -958,7 +960,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
       res.add(t.sons[i].strVal)
     of nkSym:
       var sym = t.sons[i].sym
-      if sym.kind in {skProc, skIterator, skMethod}:
+      if sym.kind in {skProc, skFunc, skIterator, skMethod}:
         var a: TLoc
         initLocExpr(p, t.sons[i], a)
         res.add($rdLoc(a))
diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim
index fa228ff04..4215a84b1 100644
--- a/compiler/ccgtrav.nim
+++ b/compiler/ccgtrav.nim
@@ -121,7 +121,8 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash;
   var c: TTraversalClosure
   var p = newProc(nil, m)
   result = "Marker_" & getTypeName(m, origTyp, sig)
-  let typ = origTyp.skipTypes(abstractInst)
+  var typ = origTyp.skipTypes(abstractInst)
+  if typ.kind == tyOpt: typ = optLowering(typ)
 
   case reason
   of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n"
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 35d73aac0..254b13429 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -183,7 +183,7 @@ proc mapType(typ: PType): TCTypeKind =
       of 8: result = ctInt64
       else: internalError("mapType")
   of tyRange: result = mapType(typ.sons[0])
-  of tyPtr, tyVar, tyRef:
+  of tyPtr, tyVar, tyRef, tyOptAsRef:
     var base = skipTypes(typ.lastSon, typedescInst)
     case base.kind
     of tyOpenArray, tyArray, tyVarargs: result = ctPtrToArray
@@ -194,6 +194,13 @@ proc mapType(typ: PType): TCTypeKind =
     else: result = ctPtr
   of tyPointer: result = ctPtr
   of tySequence: result = ctNimSeq
+  of tyOpt:
+    case optKind(typ)
+    of oBool: result = ctStruct
+    of oNil, oPtr: result = ctPtr
+    of oEnum:
+      # The 'nil' value is always negative, so we always use a signed integer
+      result = if getSize(typ.sons[0]) == 8: ctInt64 else: ctInt32
   of tyProc: result = if typ.callConv != ccClosure: ctProc else: ctStruct
   of tyString: result = ctNimStr
   of tyCString: result = ctCString
@@ -282,12 +289,13 @@ proc ccgIntroducedPtr(s: PSym): bool =
     result = (getSize(pt) > platform.floatSize*2) or (optByRef in s.options)
   else: result = false
 
-proc fillResult(param: PSym) =
-  fillLoc(param.loc, locParam, param.typ, ~"Result",
+proc fillResult(param: PNode) =
+  fillLoc(param.sym.loc, locParam, param, ~"Result",
           OnStack)
-  if mapReturnType(param.typ) != ctArray and isInvalidReturnType(param.typ):
-    incl(param.loc.flags, lfIndirect)
-    param.loc.s = OnUnknown
+  let t = param.sym.typ
+  if mapReturnType(t) != ctArray and isInvalidReturnType(t):
+    incl(param.sym.loc.flags, lfIndirect)
+    param.sym.loc.storage = OnUnknown
 
 proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope =
   if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone:
@@ -349,7 +357,7 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
   if result != nil: return
   result = getTypePre(m, typ, sig)
   if result != nil: return
-  let concrete = typ.skipTypes(abstractInst)
+  let concrete = typ.skipTypes(abstractInst + {tyOpt})
   case concrete.kind
   of tySequence, tyTuple, tyObject:
     result = getTypeName(m, typ, sig)
@@ -375,6 +383,12 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
   of tySequence:
     result = getTypeForward(m, t, hashType(t)) & "*"
     pushType(m, t)
+  of tyOpt:
+    if optKind(etB) == oPtr:
+      result = getTypeForward(m, t, hashType(t)) & "*"
+      pushType(m, t)
+    else:
+      result = getTypeDescAux(m, t, check)
   else:
     result = getTypeDescAux(m, t, check)
 
@@ -398,13 +412,13 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
     var param = t.n.sons[i].sym
     if isCompileTimeOnly(param.typ): continue
     if params != nil: add(params, ~", ")
-    fillLoc(param.loc, locParam, param.typ, mangleParamName(m, param),
+    fillLoc(param.loc, locParam, t.n.sons[i], mangleParamName(m, param),
             param.paramStorageLoc)
     if ccgIntroducedPtr(param):
       add(params, getTypeDescWeak(m, param.typ, check))
       add(params, ~"*")
       incl(param.loc.flags, lfIndirect)
-      param.loc.s = OnUnknown
+      param.loc.storage = OnUnknown
     elif weakDep:
       add(params, getTypeDescWeak(m, param.typ, check))
     else:
@@ -417,7 +431,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
     var j = 0
     while arr.kind in {tyOpenArray, tyVarargs}:
       # this fixes the 'sort' bug:
-      if param.typ.kind == tyVar: param.loc.s = OnUnknown
+      if param.typ.kind == tyVar: param.loc.storage = OnUnknown
       # need to pass hidden parameter:
       addf(params, ", NI $1Len_$2", [param.loc.r, j.rope])
       inc(j)
@@ -496,16 +510,16 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
     let sname = mangleRecFieldName(m, field, rectype)
     let ae = if accessExpr != nil: "$1.$2" % [accessExpr, sname]
              else: sname
-    fillLoc(field.loc, locField, field.typ, ae, OnUnknown)
+    fillLoc(field.loc, locField, n, ae, OnUnknown)
     # for importcpp'ed objects, we only need to set field.loc, but don't
     # have to recurse via 'getTypeDescAux'. And not doing so prevents problems
     # with heavily templatized C++ code:
     if not isImportedCppType(rectype):
-      let fieldType = field.loc.t.skipTypes(abstractInst)
+      let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
       if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags:
         addf(result, "$1 $2[SEQ_DECL_SIZE];$n",
             [getTypeDescAux(m, fieldType.elemType, check), sname])
-      elif fieldType.kind == tySequence:
+      elif fieldType.kind in {tySequence, tyOpt}:
         # we need to use a weak dependency here for trecursive_table.
         addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname])
       elif field.bitsize != 0:
@@ -624,7 +638,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
     excl(check, t.id)
     return
   case t.kind
-  of tyRef, tyPtr, tyVar:
+  of tyRef, tyOptAsRef, tyPtr, tyVar:
     var star = if t.kind == tyVar and tfVarIsPtr notin origTyp.flags and
                     compileToCpp(m): "&" else: "*"
     var et = origTyp.skipTypes(abstractInst).lastSon
@@ -651,6 +665,21 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
       result = name & "*" & star
       m.typeCache[sig] = result
       pushType(m, et)
+    of tyOpt:
+      if etB.sons[0].kind in {tyObject, tyTuple}:
+        let name = getTypeForward(m, et, hashType et)
+        result = name & "*" & star
+        m.typeCache[sig] = result
+        pushType(m, et)
+      elif optKind(etB) == oBool:
+        let name = getTypeForward(m, et, hashType et)
+        result = name & "*"
+        m.typeCache[sig] = result
+        pushType(m, et)
+      else:
+        # else we have a strong dependency  :-(
+        result = getTypeDescAux(m, et, check) & star
+        m.typeCache[sig] = result
     else:
       # else we have a strong dependency  :-(
       result = getTypeDescAux(m, et, check) & star
@@ -726,6 +755,38 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
       else:
         result = rope("TGenericSeq")
     add(result, "*")
+  of tyOpt:
+    result = cacheGetType(m.typeCache, sig)
+    if result == nil:
+      case optKind(t)
+      of oBool:
+        result = cacheGetType(m.forwTypeCache, sig)
+        if result == nil:
+          result = getTypeName(m, origTyp, sig)
+          addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
+              [structOrUnion(t), result])
+          m.forwTypeCache[sig] = result
+        appcg(m, m.s[cfsSeqTypes], "struct $2 {$n" &
+           "  NIM_BOOL Field0;$n" &
+           "  $1 Field1;$n" &
+           "};$n", [getTypeDescAux(m, t.sons[0], check), result])
+      of oPtr:
+        let et = t.sons[0]
+        if et.kind in {tyTuple, tyObject}:
+          let name = getTypeForward(m, et, hashType et)
+          result = name & "*"
+          pushType(m, et)
+        else:
+          result = getTypeDescAux(m, t.sons[0], check) & "*"
+      of oNil:
+        result = getTypeDescAux(m, t.sons[0], check)
+      of oEnum:
+        result = getTypeName(m, origTyp, sig)
+        if getSize(t.sons[0]) == 8:
+          addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result])
+        else:
+          addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
+      m.typeCache[sig] = result
   of tyArray:
     var n: BiggestInt = lengthOrd(t)
     if n <= 0: n = 1   # make an array of at least one element
@@ -861,7 +922,7 @@ proc genProcHeader(m: BModule, prc: PSym): Rope =
   elif prc.typ.callConv == ccInline:
     result.add "static "
   var check = initIntSet()
-  fillLoc(prc.loc, locProc, prc.typ, mangleName(m, prc), OnUnknown)
+  fillLoc(prc.loc, locProc, prc.ast[namePos], mangleName(m, prc), OnUnknown)
   genProcParams(m, prc.typ, rettype, params, check)
   # careful here! don't access ``prc.ast`` as that could reload large parts of
   # the object graph!
@@ -1113,6 +1174,8 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
 proc genTypeInfo(m: BModule, t: PType): Rope =
   let origType = t
   var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
+  if t.kind == tyOpt:
+    return genTypeInfo(m, optLowering(t))
 
   let sig = hashType(origType)
   result = m.typeInfoMarker.getOrDefault(sig)
@@ -1158,7 +1221,7 @@ proc genTypeInfo(m: BModule, t: PType): Rope =
     else:
       let x = fakeClosureType(t.owner)
       genTupleInfo(m, x, x, result)
-  of tySequence, tyRef:
+  of tySequence, tyRef, tyOptAsRef:
     genTypeInfoAux(m, t, t, result)
     if gSelectedGC >= gcMarkAndSweep:
       let markerProc = genTraverseProc(m, origType, sig, tiNew)
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index 6a7aa8951..4cfeeb3c3 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -100,7 +100,7 @@ proc getUniqueType*(key: PType): PType =
       if result == nil:
         gCanonicalTypes[k] = key
         result = key
-    of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor:
+    of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr:
       if key.isResolvedUserTypeClass:
         return getUniqueType(lastSon(key))
       if key.sym != nil:
@@ -126,7 +126,7 @@ proc getUniqueType*(key: PType): PType =
         result = slowSearch(key, k)
     of tyGenericInvocation, tyGenericBody,
        tyOpenArray, tyArray, tySet, tyRange, tyTuple,
-       tySequence, tyForward, tyVarargs, tyProxy:
+       tySequence, tyForward, tyVarargs, tyProxy, tyOpt:
       # we have to do a slow linear search because types may need
       # to be compared by their structure:
       result = slowSearch(key, k)
@@ -157,7 +157,7 @@ proc getUniqueType*(key: PType): PType =
       else:
         # ugh, we need the canon here:
         result = slowSearch(key, k)
-    of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("getUniqueType")
+    of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("getUniqueType")
 
 proc makeSingleLineCString*(s: string): string =
   result = "\""
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index b618837c7..6970d09c3 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -47,21 +47,31 @@ proc findPendingModule(m: BModule, s: PSym): BModule =
   var ms = getModule(s)
   result = m.g.modules[ms.position]
 
-proc initLoc(result: var TLoc, k: TLocKind, typ: PType, s: TStorageLoc) =
+proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) =
   result.k = k
-  result.s = s
-  result.t = typ
+  result.storage = s
+  result.lode = lode
   result.r = nil
   result.flags = {}
 
-proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: Rope, s: TStorageLoc) =
+proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) =
   # fills the loc if it is not already initialized
   if a.k == locNone:
     a.k = k
-    a.t = typ
-    a.s = s
+    a.lode = lode
+    a.storage = s
     if a.r == nil: a.r = r
 
+proc t(a: TLoc): PType {.inline.} =
+  if a.lode.kind == nkSym:
+    result = a.lode.sym.typ
+  else:
+    result = a.lode.typ
+
+proc lodeTyp(t: PType): PNode =
+  result = newNode(nkEmpty)
+  result.typ = t
+
 proc isSimpleConst(typ: PType): bool =
   let t = skipTypes(typ, abstractVar)
   result = t.kind notin
@@ -286,7 +296,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
   if not isComplexValueType(typ):
     if containsGcRef:
       var nilLoc: TLoc
-      initLoc(nilLoc, locTemp, loc.t, OnStack)
+      initLoc(nilLoc, locTemp, loc.lode, OnStack)
       nilLoc.r = rope("NIM_NIL")
       genRefAssign(p, loc, nilLoc, {afSrcIsNil})
     else:
@@ -294,7 +304,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
   else:
     if optNilCheck in p.options:
       linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc))
-    if loc.s != OnStack:
+    if loc.storage != OnStack:
       linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
               addrLoc(loc), genTypeInfo(p.module, loc.t))
       # XXX: generated reset procs should not touch the m_type
@@ -340,8 +350,8 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
   result.r = "T" & rope(p.labels) & "_"
   linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
   result.k = locTemp
-  result.t = t
-  result.s = OnStack
+  result.lode = lodeTyp t
+  result.storage = OnStack
   result.flags = {}
   constructLoc(p, result, not needsInit)
 
@@ -350,8 +360,8 @@ proc getIntTemp(p: BProc, result: var TLoc) =
   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.storage = OnStack
+  result.lode = lodeTyp getSysType(tyInt)
   result.flags = {}
 
 proc initGCFrame(p: BProc): Rope =
@@ -375,9 +385,10 @@ proc localDebugInfo(p: BProc, s: PSym) =
   inc(p.maxFrameLen)
   inc p.blocks[p.blocks.len-1].frameLen
 
-proc localVarDecl(p: BProc; s: PSym): Rope =
+proc localVarDecl(p: BProc; n: PNode): Rope =
+  let s = n.sym
   if s.loc.k == locNone:
-    fillLoc(s.loc, locLocalVar, s.typ, mangleLocalName(p, s), OnStack)
+    fillLoc(s.loc, locLocalVar, n, mangleLocalName(p, s), OnStack)
     if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
   result = getTypeDesc(p.module, s.typ)
   if s.constraint.isNil:
@@ -390,23 +401,24 @@ proc localVarDecl(p: BProc; s: PSym): Rope =
   else:
     result = s.cgDeclFrmt % [result, s.loc.r]
 
-proc assignLocalVar(p: BProc, s: PSym) =
+proc assignLocalVar(p: BProc, n: PNode) =
   #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 nl = if optLineDir in gOptions: "" else: tnl
-  let decl = localVarDecl(p, s) & ";" & nl
+  let decl = localVarDecl(p, n) & ";" & nl
   line(p, cpsLocals, decl)
-  localDebugInfo(p, s)
+  localDebugInfo(p, n.sym)
 
 include ccgthreadvars
 
 proc varInDynamicLib(m: BModule, sym: PSym)
 proc mangleDynLibProc(sym: PSym): Rope
 
-proc assignGlobalVar(p: BProc, s: PSym) =
+proc assignGlobalVar(p: BProc, n: PNode) =
+  let s = n.sym
   if s.loc.k == locNone:
-    fillLoc(s.loc, locGlobalVar, s.typ, mangleName(p.module, s), OnHeap)
+    fillLoc(s.loc, locGlobalVar, n, mangleName(p.module, s), OnHeap)
 
   if lfDynamicLib in s.loc.flags:
     var q = findPendingModule(p.module, s)
@@ -446,9 +458,10 @@ proc assignParam(p: BProc, s: PSym) =
   scopeMangledParam(p, s)
   localDebugInfo(p, s)
 
-proc fillProcLoc(m: BModule; sym: PSym) =
+proc fillProcLoc(m: BModule; n: PNode) =
+  let sym = n.sym
   if sym.loc.k == locNone:
-    fillLoc(sym.loc, locProc, sym.typ, mangleName(m, sym), OnStack)
+    fillLoc(sym.loc, locProc, n, mangleName(m, sym), OnStack)
 
 proc getLabel(p: BProc): TLabel =
   inc(p.labels)
@@ -457,7 +470,7 @@ proc getLabel(p: BProc): TLabel =
 proc fixLabel(p: BProc, labl: TLabel) =
   lineF(p, cpsStmts, "$1: ;$n", [labl])
 
-proc genVarPrototype(m: BModule, sym: PSym)
+proc genVarPrototype(m: BModule, n: PNode)
 proc requestConstImpl(p: BProc, sym: PSym)
 proc genStmts(p: BProc, t: PNode)
 proc expr(p: BProc, n: PNode, d: var TLoc)
@@ -469,11 +482,11 @@ proc genLiteral(p: BProc, n: PNode): Rope
 proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope
 
 proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
-  initLoc(result, locNone, e.typ, OnUnknown)
+  initLoc(result, locNone, e, OnUnknown)
   expr(p, e, result)
 
 proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) =
-  initLoc(result, locNone, e.typ, OnUnknown)
+  initLoc(result, locNone, e, OnUnknown)
   result.flags.incl lfSingleUse
   expr(p, e, result)
 
@@ -590,8 +603,8 @@ proc cgsym(m: BModule, name: string): Rope =
   var sym = magicsys.getCompilerProc(name)
   if sym != nil:
     case sym.kind
-    of skProc, skMethod, skConverter, skIterator: genProc(m, sym)
-    of skVar, skResult, skLet: genVarPrototype(m, sym)
+    of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym)
+    of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym)
     of skType: discard getTypeDesc(m, sym.typ)
     else: internalError("cgsym: " & name & ": " & $sym.kind)
   else:
@@ -645,7 +658,7 @@ proc closureSetup(p: BProc, prc: PSym) =
     internalError(prc.info, "closure generation failed")
   var env = ls.sym
   #echo "created environment: ", env.id, " for ", prc.name.s
-  assignLocalVar(p, env)
+  assignLocalVar(p, ls)
   # generate cast assignment:
   linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n",
           rdLoc(env.loc), getTypeDesc(p.module, env.typ))
@@ -676,29 +689,30 @@ proc genProcAux(m: BModule, prc: PSym) =
   if sfPure notin prc.flags and prc.typ.sons[0] != nil:
     if resultPos >= prc.ast.len:
       internalError(prc.info, "proc has no result symbol")
-    var res = prc.ast.sons[resultPos].sym # get result symbol
+    let resNode = prc.ast.sons[resultPos]
+    let res = resNode.sym # get result symbol
     if not isInvalidReturnType(prc.typ.sons[0]):
       if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
       if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(prc.getBody); val != nil):
-        var decl = localVarDecl(p, res)
+        var decl = localVarDecl(p, resNode)
         var a: TLoc
         initLocExprSingleUse(p, val, a)
         linefmt(p, cpsStmts, "$1 = $2;$n", decl, rdLoc(a))
       else:
         # declare the result symbol:
-        assignLocalVar(p, res)
+        assignLocalVar(p, resNode)
         assert(res.loc.r != nil)
         initLocalVar(p, res, immediateAsgn=false)
       returnStmt = rfmt(nil, "\treturn $1;$n", rdLoc(res.loc))
     else:
-      fillResult(res)
+      fillResult(resNode)
       assignParam(p, res)
       if skipTypes(res.typ, abstractInst).kind == tyArray:
         #incl(res.loc.flags, lfIndirect)
-        res.loc.s = OnUnknown
+        res.loc.storage = OnUnknown
 
   for i in countup(1, sonsLen(prc.typ.n) - 1):
-    var param = prc.typ.n.sons[i].sym
+    let param = prc.typ.n.sons[i].sym
     if param.typ.isCompileTimeOnly: continue
     assignParam(p, param)
   closureSetup(p, prc)
@@ -764,13 +778,13 @@ proc genProcPrototype(m: BModule, sym: PSym) =
 
 proc genProcNoForward(m: BModule, prc: PSym) =
   if lfImportCompilerProc in prc.loc.flags:
-    fillProcLoc(m, prc)
+    fillProcLoc(m, prc.ast[namePos])
     useHeader(m, prc)
     # dependency to a compilerproc:
     discard cgsym(m, prc.name.s)
     return
   if lfNoDecl in prc.loc.flags:
-    fillProcLoc(m, prc)
+    fillProcLoc(m, prc.ast[namePos])
     useHeader(m, prc)
     genProcPrototype(m, prc)
   elif prc.typ.callConv == ccInline:
@@ -779,7 +793,7 @@ proc genProcNoForward(m: BModule, prc: PSym) =
     # a check for ``m.declaredThings``.
     if not containsOrIncl(m.declaredThings, prc.id):
       #if prc.loc.k == locNone:
-      fillProcLoc(m, prc)
+      fillProcLoc(m, prc.ast[namePos])
       #elif {sfExportc, sfImportc} * prc.flags == {}:
       #  # reset name to restore consistency in case of hashing collisions:
       #  echo "resetting ", prc.id, " by ", m.module.name.s
@@ -790,7 +804,7 @@ proc genProcNoForward(m: BModule, prc: PSym) =
       genProcAux(m, prc)
   elif lfDynamicLib in prc.loc.flags:
     var q = findPendingModule(m, prc)
-    fillProcLoc(q, prc)
+    fillProcLoc(q, prc.ast[namePos])
     useHeader(m, prc)
     genProcPrototype(m, prc)
     if q != nil and not containsOrIncl(q.declaredThings, prc.id):
@@ -799,13 +813,13 @@ proc genProcNoForward(m: BModule, prc: PSym) =
       symInDynamicLibPartial(m, prc)
   elif sfImportc notin prc.flags:
     var q = findPendingModule(m, prc)
-    fillProcLoc(q, prc)
+    fillProcLoc(q, prc.ast[namePos])
     useHeader(m, prc)
     genProcPrototype(m, prc)
     if q != nil and not containsOrIncl(q.declaredThings, prc.id):
       genProcAux(q, prc)
   else:
-    fillProcLoc(m, prc)
+    fillProcLoc(m, prc.ast[namePos])
     useHeader(m, prc)
     if sfInfixCall notin prc.flags: genProcPrototype(m, prc)
 
@@ -813,7 +827,7 @@ proc requestConstImpl(p: BProc, sym: PSym) =
   var m = p.module
   useHeader(m, sym)
   if sym.loc.k == locNone:
-    fillLoc(sym.loc, locData, sym.typ, mangleName(p.module, sym), OnStatic)
+    fillLoc(sym.loc, locData, sym.ast, mangleName(p.module, sym), OnStatic)
   if lfNoDecl in sym.loc.flags: return
   # declare implementation:
   var q = findPendingModule(m, sym)
@@ -836,7 +850,7 @@ proc genProc(m: BModule, prc: PSym) =
   if sfBorrow in prc.flags or not isActivated(prc): return
   if sfForward in prc.flags:
     addForwardedProc(m, prc)
-    fillProcLoc(m, prc)
+    fillProcLoc(m, prc.ast[namePos])
   else:
     genProcNoForward(m, prc)
     if {sfExportc, sfCompilerProc} * prc.flags == {sfExportc} and
@@ -846,10 +860,11 @@ proc genProc(m: BModule, prc: PSym) =
         if not containsOrIncl(m.g.generatedHeader.declaredThings, prc.id):
           genProcAux(m.g.generatedHeader, prc)
 
-proc genVarPrototypeAux(m: BModule, sym: PSym) =
+proc genVarPrototypeAux(m: BModule, n: PNode) =
   #assert(sfGlobal in sym.flags)
+  let sym = n.sym
   useHeader(m, sym)
-  fillLoc(sym.loc, locGlobalVar, sym.typ, mangleName(m, sym), OnHeap)
+  fillLoc(sym.loc, locGlobalVar, n, mangleName(m, sym), OnHeap)
   if (lfNoDecl in sym.loc.flags) or containsOrIncl(m.declaredThings, sym.id):
     return
   if sym.owner.id != m.module.id:
@@ -865,8 +880,8 @@ proc genVarPrototypeAux(m: BModule, sym: PSym) =
       if sfVolatile in sym.flags: add(m.s[cfsVars], " volatile")
       addf(m.s[cfsVars], " $1;$n", [sym.loc.r])
 
-proc genVarPrototype(m: BModule, sym: PSym) =
-  genVarPrototypeAux(m, sym)
+proc genVarPrototype(m: BModule, n: PNode) =
+  genVarPrototypeAux(m, n)
 
 proc addIntTypes(result: var Rope) {.inline.} =
   addf(result, "#define NIM_NEW_MANGLING_RULES" & tnl &
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index dc97e3648..9863e90bb 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -107,3 +107,4 @@ proc initDefines*() =
   defineSymbol("nimDistros")
   defineSymbol("nimHasCppDefine")
   defineSymbol("nimGenericInOutFlags")
+  when false: defineSymbol("nimHasOpt")
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 3f4f7b164..8c50a4f1d 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -325,7 +325,7 @@ proc complexName(k: TSymKind, n: PNode, baseName: string): string =
   ## section of ``doc/docgen.txt``.
   result = baseName
   case k:
-  of skProc: result.add(defaultParamSeparator)
+  of skProc, skFunc: result.add(defaultParamSeparator)
   of skMacro: result.add(".m" & defaultParamSeparator)
   of skMethod: result.add(".e" & defaultParamSeparator)
   of skIterator: result.add(".i" & defaultParamSeparator)
@@ -341,7 +341,7 @@ proc isCallable(n: PNode): bool =
   ## Returns true if `n` contains a callable node.
   case n.kind
   of nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef,
-    nkConverterDef: result = true
+    nkConverterDef, nkFuncDef: result = true
   else:
     result = false
 
@@ -533,6 +533,9 @@ proc generateDoc*(d: PDoc, n: PNode) =
   of nkProcDef:
     when useEffectSystem: documentRaises(n)
     genItem(d, n, n.sons[namePos], skProc)
+  of nkFuncDef:
+    when useEffectSystem: documentRaises(n)
+    genItem(d, n, n.sons[namePos], skFunc)
   of nkMethodDef:
     when useEffectSystem: documentRaises(n)
     genItem(d, n, n.sons[namePos], skMethod)
@@ -574,6 +577,9 @@ proc generateJson*(d: PDoc, n: PNode) =
   of nkProcDef:
     when useEffectSystem: documentRaises(n)
     d.add genJsonItem(d, n, n.sons[namePos], skProc)
+  of nkFuncDef:
+    when useEffectSystem: documentRaises(n)
+    d.add genJsonItem(d, n, n.sons[namePos], skFunc)
   of nkMethodDef:
     when useEffectSystem: documentRaises(n)
     d.add genJsonItem(d, n, n.sons[namePos], skMethod)
@@ -604,8 +610,8 @@ proc generateJson*(d: PDoc, n: PNode) =
 
 proc genSection(d: PDoc, kind: TSymKind) =
   const sectionNames: array[skModule..skTemplate, string] = [
-    "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods",
-    "Iterators", "Converters", "Macros", "Templates"
+    "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Funcs",
+    "Methods", "Iterators", "Converters", "Macros", "Templates"
   ]
   if d.section[kind] == nil: return
   var title = sectionNames[kind].rope
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index c47e4fb9a..4614eafe6 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -652,9 +652,10 @@ proc getLinkCmd(projectfile, objfiles: string): string =
   else:
     var linkerExe = getConfigVar(cCompiler, ".linkerexe")
     if len(linkerExe) == 0: linkerExe = cCompiler.getLinkerExe
+    # bug #6452: We must not use ``quoteShell`` here for ``linkerExe``
     if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
-    if noAbsolutePaths(): result = quoteShell(linkerExe)
-    else: result = quoteShell(joinPath(ccompilerpath, linkerExe))
+    if noAbsolutePaths(): result = linkerExe
+    else: result = joinPath(ccompilerpath, linkerExe)
     let buildgui = if optGenGuiApp in gGlobalOptions: CC[cCompiler].buildGui
                    else: ""
     var exefile, builddll: string
@@ -701,6 +702,40 @@ template tryExceptOSErrorMessage(errorPrefix: string = "", body: untyped): typed
       rawMessage(errExecutionOfProgramFailed, ose.msg & " " & $ose.errorCode)
     raise
 
+proc execLinkCmd(linkCmd: string) =
+  tryExceptOSErrorMessage("invocation of external linker program failed."):
+    execExternalProgram(linkCmd,
+      if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking)
+
+proc execCmdsInParallel(cmds: seq[string]; prettyCb: proc (idx: int)) =
+  let runCb = proc (idx: int, p: Process) =
+    let exitCode = p.peekExitCode
+    if exitCode != 0:
+      rawMessage(errGenerated, "execution of an external compiler program '" &
+        cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" &
+        p.outputStream.readAll.strip)
+  if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
+  var res = 0
+  if gNumberOfProcessors <= 1:
+    for i in countup(0, high(cmds)):
+      tryExceptOSErrorMessage("invocation of external compiler program failed."):
+        res = execWithEcho(cmds[i])
+      if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i])
+  else:
+    tryExceptOSErrorMessage("invocation of external compiler program failed."):
+      if optListCmd in gGlobalOptions or gVerbosity > 1:
+        res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams},
+                            gNumberOfProcessors, afterRunEvent=runCb)
+      elif gVerbosity == 1:
+        res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
+                            gNumberOfProcessors, prettyCb, afterRunEvent=runCb)
+      else:
+        res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
+                            gNumberOfProcessors, afterRunEvent=runCb)
+  if res != 0:
+    if gNumberOfProcessors <= 1:
+      rawMessage(errExecutionOfProgramFailed, cmds.join())
+
 proc callCCompiler*(projectfile: string) =
   var
     linkCmd: string
@@ -713,35 +748,9 @@ proc callCCompiler*(projectfile: string) =
   var prettyCmds: TStringSeq = @[]
   let prettyCb = proc (idx: int) =
     echo prettyCmds[idx]
-  let runCb = proc (idx: int, p: Process) =
-    let exitCode = p.peekExitCode
-    if exitCode != 0:
-      rawMessage(errGenerated, "execution of an external compiler program '" &
-        cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" &
-        p.outputStream.readAll.strip)
   compileCFile(toCompile, script, cmds, prettyCmds)
   if optCompileOnly notin gGlobalOptions:
-    if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
-    var res = 0
-    if gNumberOfProcessors <= 1:
-      for i in countup(0, high(cmds)):
-        tryExceptOSErrorMessage("invocation of external compiler program failed."):
-          res = execWithEcho(cmds[i])
-        if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i])
-    else:
-      tryExceptOSErrorMessage("invocation of external compiler program failed."):
-        if optListCmd in gGlobalOptions or gVerbosity > 1:
-          res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams},
-                              gNumberOfProcessors, afterRunEvent=runCb)
-        elif gVerbosity == 1:
-          res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
-                              gNumberOfProcessors, prettyCb, afterRunEvent=runCb)
-        else:
-          res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams},
-                              gNumberOfProcessors, afterRunEvent=runCb)
-    if res != 0:
-      if gNumberOfProcessors <= 1:
-        rawMessage(errExecutionOfProgramFailed, cmds.join())
+    execCmdsInParallel(cmds, prettyCb)
   if optNoLinking notin gGlobalOptions:
     # call the linker:
     var objfiles = ""
@@ -756,9 +765,7 @@ proc callCCompiler*(projectfile: string) =
 
     linkCmd = getLinkCmd(projectfile, objfiles)
     if optCompileOnly notin gGlobalOptions:
-      tryExceptOSErrorMessage("invocation of external linker program failed."):
-        execExternalProgram(linkCmd,
-          if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking)
+      execLinkCmd(linkCmd)
   else:
     linkCmd = ""
   if optGenScript in gGlobalOptions:
@@ -766,7 +773,8 @@ proc callCCompiler*(projectfile: string) =
     add(script, tnl)
     generateScript(projectfile, script)
 
-from json import escapeJson
+#from json import escapeJson
+import json
 
 proc writeJsonBuildInstructions*(projectfile: string) =
   template lit(x: untyped) = f.write x
@@ -834,6 +842,34 @@ proc writeJsonBuildInstructions*(projectfile: string) =
     lit "\L}\L"
     close(f)
 
+proc runJsonBuildInstructions*(projectfile: string) =
+  let file = projectfile.splitFile.name
+  let jsonFile = toGeneratedFile(file, "json")
+  try:
+    let data = json.parseFile(jsonFile)
+    let toCompile = data["compile"]
+    doAssert toCompile.kind == JArray
+    var cmds: TStringSeq = @[]
+    var prettyCmds: TStringSeq = @[]
+    for c in toCompile:
+      doAssert c.kind == JArray
+      doAssert c.len >= 2
+
+      add(cmds, c[1].getStr)
+      let (_, name, _) = splitFile(c[0].getStr)
+      add(prettyCmds, "CC: " & name)
+
+    let prettyCb = proc (idx: int) =
+      echo prettyCmds[idx]
+    execCmdsInParallel(cmds, prettyCb)
+
+    let linkCmd = data["linkcmd"]
+    doAssert linkCmd.kind == JString
+    execLinkCmd(linkCmd.getStr)
+  except:
+    echo getCurrentException().getStackTrace()
+    quit "error evaluating JSON file: " & jsonFile
+
 proc genMappingFiles(list: CFileList): Rope =
   for it in list:
     addf(result, "--file:r\"$1\"$N", [rope(it.cname)])
diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim
new file mode 100644
index 000000000..4e1ce6d50
--- /dev/null
+++ b/compiler/gorgeimpl.nim
@@ -0,0 +1,83 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2017 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Module that implements ``gorge`` for the compiler as well as
+## the scriptable import mechanism.
+
+import msgs, securehash, os, osproc, streams, strutils, options
+
+proc readOutput(p: Process): (string, int) =
+  result[0] = ""
+  var output = p.outputStream
+  while not output.atEnd:
+    result[0].add(output.readLine)
+    result[0].add("\n")
+  if result[0].len > 0:
+    result[0].setLen(result[0].len - "\n".len)
+  result[1] = p.waitForExit
+
+proc opGorge*(cmd, input, cache: string, info: TLineInfo): (string, int) =
+  let workingDir = parentDir(info.toFullPath)
+  if cache.len > 0:# and optForceFullMake notin gGlobalOptions:
+    let h = secureHash(cmd & "\t" & input & "\t" & cache)
+    let filename = options.toGeneratedFile("gorge_" & $h, "txt")
+    var f: File
+    if open(f, filename):
+      result = (f.readAll, 0)
+      f.close
+      return
+    var readSuccessful = false
+    try:
+      var p = startProcess(cmd, workingDir,
+                           options={poEvalCommand, poStderrToStdout})
+      if input.len != 0:
+        p.inputStream.write(input)
+        p.inputStream.close()
+      result = p.readOutput
+      readSuccessful = true
+      # only cache successful runs:
+      if result[1] == 0:
+        writeFile(filename, result[0])
+    except IOError, OSError:
+      if not readSuccessful: result = ("", -1)
+  else:
+    try:
+      var p = startProcess(cmd, workingDir,
+                           options={poEvalCommand, poStderrToStdout})
+      if input.len != 0:
+        p.inputStream.write(input)
+        p.inputStream.close()
+      result = p.readOutput
+    except IOError, OSError:
+      result = ("", -1)
+
+proc scriptableImport*(pkg, subdir: string; info: TLineInfo): string =
+  var cmd = getConfigVar("resolver.exe")
+  if cmd.len == 0: cmd = "nimresolve"
+  else: cmd = quoteShell(cmd)
+  cmd.add " --source:"
+  cmd.add quoteShell(info.toFullPath())
+  cmd.add " --stdlib:"
+  cmd.add quoteShell(options.libpath)
+  cmd.add "  --project:"
+  cmd.add quoteShell(gProjectFull)
+  if subdir.len != 0:
+    cmd.add " --subdir:"
+    cmd.add quoteShell(subdir)
+  if options.gNoNimblePath:
+    cmd.add " --nonimblepath"
+  cmd.add ' '
+  cmd.add quoteShell(pkg)
+  let (res, exitCode) = opGorge(cmd, "", cmd, info)
+  if exitCode == 0:
+    result = res.strip()
+  elif res.len > 0:
+    localError(info, res)
+  else:
+    localError(info, "cannot resolve: " & (pkg / subdir))
diff --git a/compiler/importer.nim b/compiler/importer.nim
index c4861df7f..07f42a147 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -11,11 +11,22 @@
 
 import
   intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups,
-  semdata, passes, renderer
+  semdata, passes, renderer, gorgeimpl
 
 proc evalImport*(c: PContext, n: PNode): PNode
 proc evalFrom*(c: PContext, n: PNode): PNode
 
+proc lookupPackage(pkg, subdir: PNode): string =
+  let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: ""
+  case pkg.kind
+  of nkStrLit, nkRStrLit, nkTripleStrLit:
+    result = scriptableImport(pkg.strVal, sub, pkg.info)
+  of nkIdent:
+    result = scriptableImport(pkg.ident.s, sub, pkg.info)
+  else:
+    localError(pkg.info, "package name must be an identifier or string literal")
+    result = ""
+
 proc getModuleName*(n: PNode): string =
   # This returns a short relative module name without the nim extension
   # e.g. like "system", "importer" or "somepath/module"
@@ -31,16 +42,33 @@ proc getModuleName*(n: PNode): string =
     result = n.ident.s
   of nkSym:
     result = n.sym.name.s
-  of nkInfix, nkPrefix:
-    if n.sons[0].kind == nkIdent and n.sons[0].ident.id == getIdent("as").id:
+  of nkInfix:
+    let n0 = n[0]
+    let n1 = n[1]
+    if n0.kind == nkIdent and n0.ident.id == getIdent("as").id:
       # XXX hack ahead:
       n.kind = nkImportAs
       n.sons[0] = n.sons[1]
       n.sons[1] = n.sons[2]
       n.sons.setLen(2)
       return getModuleName(n.sons[0])
-    # hacky way to implement 'x / y /../ z':
-    result = renderTree(n, {renderNoComments}).replace(" ")
+    if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$":
+      if n0.kind == nkIdent and n0.ident.s == "/":
+        result = lookupPackage(n1[1], n[2])
+      else:
+        localError(n.info, "only '/' supported with $package notation")
+        result = ""
+    else:
+      # hacky way to implement 'x / y /../ z':
+      result = getModuleName(n1)
+      result.add renderTree(n0, {renderNoComments})
+      result.add getModuleName(n[2])
+  of nkPrefix:
+    if n.sons[0].kind == nkIdent and n.sons[0].ident.s == "$":
+      result = lookupPackage(n[1], nil)
+    else:
+      # hacky way to implement 'x / y /../ z':
+      result = renderTree(n, {renderNoComments}).replace(" ")
   of nkDotExpr:
     result = renderTree(n, {renderNoComments}).replace(".", "/")
   of nkImportAs:
@@ -60,6 +88,11 @@ proc checkModuleName*(n: PNode; doLocalError=true): int32 =
   else:
     result = fullPath.fileInfoIdx
 
+proc importPureEnumField*(c: PContext; s: PSym) =
+  var check = strTableGet(c.importTable.symbols, s.name)
+  if check == nil:
+    strTableAdd(c.pureEnumFields, s)
+
 proc rawImportSymbol(c: PContext, s: PSym) =
   # This does not handle stubs, because otherwise loading on demand would be
   # pointless in practice. So importing stubs is fine here!
@@ -75,7 +108,7 @@ proc rawImportSymbol(c: PContext, s: PSym) =
   strTableAdd(c.importTable.symbols, s)
   if s.kind == skType:
     var etyp = s.typ
-    if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags:
+    if etyp.kind in {tyBool, tyEnum}:
       for j in countup(0, sonsLen(etyp.n) - 1):
         var e = etyp.n.sons[j].sym
         if e.kind != skEnumField:
@@ -91,7 +124,10 @@ proc rawImportSymbol(c: PContext, s: PSym) =
             break
           check = nextIdentIter(it, c.importTable.symbols)
         if e != nil:
-          rawImportSymbol(c, e)
+          if sfPure notin s.flags:
+            rawImportSymbol(c, e)
+          else:
+            importPureEnumField(c, e)
   else:
     # rodgen assures that converters and patterns are no stubs
     if s.kind == skConverter: addConverter(c, s)
@@ -201,12 +237,14 @@ proc evalImport(c: PContext, n: PNode): PNode =
   for i in countup(0, sonsLen(n) - 1):
     let it = n.sons[i]
     if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
-      let sep = renderTree(it.sons[0], {renderNoComments})
-      let dir = renderTree(it.sons[1], {renderNoComments})
+      let sep = it[0]
+      let dir = it[1]
+      let a = newNodeI(nkInfix, it.info)
+      a.add sep
+      a.add dir
+      a.add sep # dummy entry, replaced in the loop
       for x in it[2]:
-        let f = renderTree(x, {renderNoComments})
-        let a = newStrNode(nkStrLit, (dir & sep & f).replace(" "))
-        a.info = it.info
+        a.sons[2] = x
         impMod(c, a)
     else:
       impMod(c, it)
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 73e6a9948..7b1c43817 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -187,12 +187,12 @@ proc mapType(typ: PType): TJSTypeKind =
   of tyBool: result = etyBool
   of tyFloat..tyFloat128: result = etyFloat
   of tySet: result = etyObject # map a set to a table
-  of tyString, tySequence: result = etySeq
+  of tyString, tySequence, tyOpt: result = etySeq
   of tyObject, tyArray, tyTuple, tyOpenArray, tyVarargs:
     result = etyObject
   of tyNil: result = etyNull
   of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvocation,
-     tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor,
+     tyNone, tyFromExpr, tyForward, tyEmpty,
      tyExpr, tyStmt, tyTypeDesc, tyTypeClasses, tyVoid, tyAlias:
     result = etyNone
   of tyInferred:
@@ -202,7 +202,7 @@ proc mapType(typ: PType): TJSTypeKind =
     else: result = etyNone
   of tyProc: result = etyProc
   of tyCString: result = etyString
-  of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("mapType")
+  of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("mapType")
 
 proc mapType(p: PProc; typ: PType): TJSTypeKind =
   if p.target == targetPHP: result = etyObject
@@ -292,7 +292,7 @@ proc useMagic(p: PProc, name: string) =
   if name.len == 0: return
   var s = magicsys.getCompilerProc(name)
   if s != nil:
-    internalAssert s.kind in {skProc, skMethod, skConverter}
+    internalAssert s.kind in {skProc, skFunc, skMethod, skConverter}
     if not p.g.generatedSyms.containsOrIncl(s.id):
       let code = genProc(p, s)
       add(p.g.constants, code)
@@ -927,7 +927,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
 
   # we don't care if it's an etyBaseIndex (global) of a string, it's
   # still a string that needs to be copied properly:
-  if x.typ.skipTypes(abstractInst).kind in {tySequence, tyString}:
+  if x.typ.skipTypes(abstractInst).kind in {tySequence, tyOpt, tyString}:
     xtyp = etySeq
   case xtyp
   of etySeq:
@@ -971,7 +971,7 @@ proc genFastAsgn(p: PProc, n: PNode) =
   # See bug #5933. So we try to be more compatible with the C backend semantics
   # here for 'shallowCopy'. This is an educated guess and might require further
   # changes later:
-  let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyString}
+  let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyOpt, tyString}
   genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=noCopy)
 
 proc genSwap(p: PProc, n: PNode) =
@@ -1111,7 +1111,7 @@ template isIndirect(x: PSym): bool =
   ({sfAddrTaken, sfGlobal} * v.flags != {} and
     #(mapType(v.typ) != etyObject) and
     {sfImportc, sfVolatile, sfExportc} * v.flags == {} and
-    v.kind notin {skProc, skConverter, skMethod, skIterator,
+    v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator,
                   skConst, skTemp, skLet} and p.target == targetJS)
 
 proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
@@ -1237,7 +1237,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
     else:
       r.res = "$" & s.loc.r
       p.declareGlobal(s.id, r.res)
-  of skProc, skConverter, skMethod:
+  of skProc, skFunc, skConverter, skMethod:
     discard mangleName(s, p.target)
     if p.target == targetPHP and r.kind != resCallee:
       r.res = makeJsString($s.loc.r)
@@ -1550,7 +1550,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
       result = putToSeq("[null, 0]", indirect)
     else:
       result = putToSeq("null", indirect)
-  of tySequence, tyString, tyCString, tyPointer, tyProc:
+  of tySequence, tyOpt, tyString, tyCString, tyPointer, tyProc:
     result = putToSeq("null", indirect)
   of tyStatic:
     if t.n != nil:
@@ -2338,7 +2338,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
      nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
      nkFromStmt, nkTemplateDef, nkMacroDef: discard
   of nkPragma: genPragma(p, n)
-  of nkProcDef, nkMethodDef, nkConverterDef:
+  of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
     var s = n.sons[namePos].sym
     if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}:
       genSym(p, n.sons[namePos], r)
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 986d8c716..e64e0a898 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -194,7 +194,7 @@ proc illegalCapture(s: PSym): bool {.inline.} =
       s.kind == skResult
 
 proc isInnerProc(s: PSym): bool =
-  if s.kind in {skProc, skMethod, skConverter, skIterator} and s.magic == mNone:
+  if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone:
     result = s.skipGenericOwner.kind in routineKinds
 
 proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
@@ -371,7 +371,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
   case n.kind
   of nkSym:
     let s = n.sym
-    if s.kind in {skProc, skMethod, skConverter, skIterator} and s.typ != nil and s.typ.callConv == ccClosure:
+    if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and
+        s.typ != nil and s.typ.callConv == ccClosure:
       # this handles the case that the inner proc was declared as
       # .closure but does not actually capture anything:
       addClosureParam(c, s, n.info)
@@ -443,7 +444,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
     discard
   of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef:
     discard
-  of nkLambdaKinds, nkIteratorDef:
+  of nkLambdaKinds, nkIteratorDef, nkFuncDef:
     if n.typ != nil:
       detectCapturedVars(n[namePos], owner, c)
   else:
@@ -730,7 +731,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
         # now we know better, so patch it:
         n.sons[0] = x.sons[0]
         n.sons[1] = x.sons[1]
-  of nkLambdaKinds, nkIteratorDef:
+  of nkLambdaKinds, nkIteratorDef, nkFuncDef:
     if n.typ != nil and n[namePos].kind == nkSym:
       let m = newSymNode(n[namePos].sym)
       m.typ = n.typ
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index 09bcb4ce0..45d090b16 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -48,7 +48,7 @@ type
     tkShl, tkShr, tkStatic,
     tkTemplate,
     tkTry, tkTuple, tkType, tkUsing,
-    tkVar, tkWhen, tkWhile, tkWith, tkWithout, tkXor,
+    tkVar, tkWhen, tkWhile, tkXor,
     tkYield, # end of keywords
     tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit,
     tkUIntLit, tkUInt8Lit, tkUInt16Lit, tkUInt32Lit, tkUInt64Lit,
@@ -89,7 +89,7 @@ const
     "shl", "shr", "static",
     "template",
     "try", "tuple", "type", "using",
-    "var", "when", "while", "with", "without", "xor",
+    "var", "when", "while", "xor",
     "yield",
     "tkIntLit", "tkInt8Lit", "tkInt16Lit", "tkInt32Lit", "tkInt64Lit",
     "tkUIntLit", "tkUInt8Lit", "tkUInt16Lit", "tkUInt32Lit", "tkUInt64Lit",
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 5c326d10a..eddfeea56 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -144,8 +144,10 @@ type
 
 proc getSymRepr*(s: PSym): string =
   case s.kind
-  of skProc, skMethod, skConverter, skIterator: result = getProcHeader(s)
-  else: result = s.name.s
+  of skProc, skFunc, skMethod, skConverter, skIterator:
+    result = getProcHeader(s)
+  else:
+    result = s.name.s
 
 proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
   # check if all symbols have been used and defined:
@@ -286,7 +288,7 @@ proc lookUp*(c: PContext, n: PNode): PSym =
 
 type
   TLookupFlag* = enum
-    checkAmbiguity, checkUndeclared, checkModule
+    checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields
 
 proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
   const allExceptModule = {low(TSymKind)..high(TSymKind)}-{skModule,skPackage}
@@ -297,6 +299,8 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
       result = searchInScopes(c, ident).skipAlias(n)
     else:
       result = searchInScopes(c, ident, allExceptModule).skipAlias(n)
+    if result == nil and checkPureEnumFields in flags:
+      result = strTableGet(c.pureEnumFields, ident)
     if result == nil and checkUndeclared in flags:
       fixSpelling(n, ident, searchInScopes)
       errorUndeclaredIdentifier(c, n.info, ident.s)
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index ce76b63a4..033472c07 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -633,7 +633,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
   if fn.kind == nkClosure:
     localError(n.info, "closure in spawn environment is not allowed")
   if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
-                                               skMethod, skConverter}):
+                                               skFunc, skMethod, skConverter}):
     # for indirect calls we pass the function pointer in the scratchObj
     var argType = n[0].typ.skipTypes(abstractInst)
     var field = newSym(skField, getIdent"fn", owner, n.info)
diff --git a/compiler/main.nim b/compiler/main.nim
index f662ded1b..450542c4c 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -78,6 +78,10 @@ proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) =
     extccomp.callCCompiler(proj)
     extccomp.writeJsonBuildInstructions(proj)
 
+proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) =
+  let proj = changeFileExt(gProjectFull, "")
+  extccomp.runJsonBuildInstructions(proj)
+
 proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) =
   #incl(gGlobalOptions, optSafeCode)
   setTarget(osJS, cpuJS)
@@ -266,6 +270,9 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
   of "nop", "help":
     # prevent the "success" message:
     gCmd = cmdDump
+  of "jsonscript":
+    gCmd = cmdJsonScript
+    commandJsonScript(graph, cache)
   else:
     rawMessage(errInvalidCommandX, command)
 
diff --git a/compiler/nim.nim b/compiler/nim.nim
index 56885e9f1..89225a5e0 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -119,4 +119,6 @@ condsyms.initDefines()
 
 when not defined(selftest):
   handleCmdLine(newIdentCache(), newConfigRef())
+  when declared(GC_setMaxPause):
+    echo GC_getStatistics()
   msgQuit(int8(msgs.gErrorCounter > 0))
diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim
index ab63f9e12..39c3a17e7 100644
--- a/compiler/nimblecmd.nim
+++ b/compiler/nimblecmd.nim
@@ -16,11 +16,11 @@ proc addPath*(path: string, info: TLineInfo) =
     options.searchPaths.insert(path, 0)
 
 type
-  Version = distinct string
+  Version* = distinct string
 
-proc `$`(ver: Version): string {.borrow.}
+proc `$`*(ver: Version): string {.borrow.}
 
-proc newVersion(ver: string): Version =
+proc newVersion*(ver: string): Version =
   doAssert(ver.len == 0 or ver[0] in {'#', '\0'} + Digits,
            "Wrong version: " & ver)
   return Version(ver)
@@ -28,7 +28,7 @@ proc newVersion(ver: string): Version =
 proc isSpecial(ver: Version): bool =
   return ($ver).len > 0 and ($ver)[0] == '#'
 
-proc `<`(ver: Version, ver2: Version): bool =
+proc `<`*(ver: Version, ver2: Version): bool =
   ## This is synced from Nimble's version module.
 
   # Handling for special versions such as "#head" or "#branch".
diff --git a/compiler/options.nim b/compiler/options.nim
index 40d56aea5..9f44514c5 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -91,7 +91,8 @@ type
     cmdRst2html,              # convert a reStructuredText file to HTML
     cmdRst2tex,               # convert a reStructuredText file to TeX
     cmdInteractive,           # start interactive session
-    cmdRun                    # run the project via TCC backend
+    cmdRun,                   # run the project via TCC backend
+    cmdJsonScript             # compile a .json build file
   TStringSeq* = seq[string]
   TGCMode* = enum             # the selected GC
     gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcRefc,
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 253716247..e14a8fbdf 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -255,13 +255,6 @@ proc isUnary(p: TParser): bool =
      p.tok.strongSpaceB == 0 and
      p.tok.strongSpaceA > 0:
       result = true
-      # versions prior to 0.13.0 used to do this:
-      when false:
-        if p.strongSpaces:
-          result = true
-        else:
-          parMessage(p, warnDeprecated,
-            "will be parsed as unary operator; inconsistent spacing")
 
 proc checkBinary(p: TParser) {.inline.} =
   ## Check if the current parser token is a binary operator.
@@ -700,12 +693,7 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
   result = r
 
   template somePar() =
-    if p.tok.strongSpaceA > 0:
-      if p.strongSpaces:
-        break
-      else:
-        parMessage(p, warnDeprecated,
-          "a [b] will be parsed as command syntax; spacing")
+    if p.tok.strongSpaceA > 0: break
   # progress guaranteed
   while p.tok.indent < 0 or
        (p.tok.tokType == tkDot and p.tok.indent >= baseIndent):
@@ -991,7 +979,7 @@ proc parseDoBlock(p: var TParser; info: TLineInfo): PNode =
   if params.kind != nkEmpty:
     result = newProcNode(nkDo, info, result, params = params, pragmas = pragmas)
 
-proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
+proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
   #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
   # either a proc type or a anonymous proc
   let info = parLineInfo(p)
@@ -1002,7 +990,7 @@ proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
   if p.tok.tokType == tkEquals and isExpr:
     getTok(p)
     skipComment(p, result)
-    result = newProcNode(nkLambda, info, parseStmt(p),
+    result = newProcNode(kind, info, parseStmt(p),
                          params = params,
                          pragmas = pragmas)
   else:
@@ -1014,7 +1002,7 @@ proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
 proc isExprStart(p: TParser): bool =
   case p.tok.tokType
   of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf,
-     tkProc, tkIterator, tkBind, tkAddr,
+     tkProc, tkFunc, tkIterator, tkBind, tkAddr,
      tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
      tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut:
     result = true
@@ -1038,9 +1026,15 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
   optInd(p, result)
   if not isOperator(p.tok) and isExprStart(p):
     addSon(result, primary(p, mode))
-  if kind == nkDistinctTy and p.tok.tokType in {tkWith, tkWithout}:
-    let nodeKind = if p.tok.tokType == tkWith: nkWith
-                   else: nkWithout
+  if kind == nkDistinctTy and p.tok.tokType == tkSymbol:
+    # XXX document this feature!
+    var nodeKind: TNodeKind
+    if p.tok.ident.s == "with":
+      nodeKind = nkWith
+    elif p.tok.ident.s == "without":
+      nodeKind = nkWithout
+    else:
+      return result
     getTok(p)
     let list = newNodeP(nodeKind, p)
     result.addSon list
@@ -1088,21 +1082,12 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
 
   case p.tok.tokType:
   of tkTuple: result = parseTuple(p, mode == pmTypeDef)
-  of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef})
+  of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkLambda)
+  of tkFunc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkFuncDef)
   of tkIterator:
-    when false:
-      if mode in {pmTypeDesc, pmTypeDef}:
-        result = parseProcExpr(p, false)
-        result.kind = nkIteratorTy
-      else:
-        # no anon iterators for now:
-        parMessage(p, errExprExpected, p.tok)
-        getTok(p)  # we must consume a token here to prevend endless loops!
-        result = ast.emptyNode
-    else:
-      result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef})
-      if result.kind == nkLambda: result.kind = nkIteratorDef
-      else: result.kind = nkIteratorTy
+    result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkLambda)
+    if result.kind == nkLambda: result.kind = nkIteratorDef
+    else: result.kind = nkIteratorTy
   of tkEnum:
     if mode == pmTypeDef:
       result = parseEnum(p)
@@ -2000,6 +1985,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
   of tkDefer: result = parseStaticOrDefer(p, nkDefer)
   of tkAsm: result = parseAsm(p)
   of tkProc: result = parseRoutine(p, nkProcDef)
+  of tkFunc: result = parseRoutine(p, nkFuncDef)
   of tkMethod: result = parseRoutine(p, nkMethodDef)
   of tkIterator: result = parseRoutine(p, nkIteratorDef)
   of tkMacro: result = parseRoutine(p, nkMacroDef)
@@ -2056,8 +2042,8 @@ proc parseStmt(p: var TParser): PNode =
   else:
     # the case statement is only needed for better error messages:
     case p.tok.tokType
-    of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkIterator,
-       tkMacro, tkType, tkConst, tkWhen, tkVar:
+    of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc,
+       tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar:
       parMessage(p, errComplexStmtRequiresInd)
       result = ast.emptyNode
     else:
diff --git a/compiler/passes.nim b/compiler/passes.nim
index bf6ce1a0a..6efd50385 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -64,7 +64,7 @@ proc astNeeded*(s: PSym): bool =
   # needs to be stored. The passes manager frees s.sons[codePos] when
   # appropriate to free the procedure body's memory. This is important
   # to keep memory usage down.
-  if (s.kind in {skMethod, skProc}) and
+  if (s.kind in {skMethod, skProc, skFunc}) and
       ({sfCompilerProc, sfCompileTime} * s.flags == {}) and
       (s.typ.callConv != ccInline) and
       (s.ast.sons[genericParamsPos].kind == nkEmpty):
diff --git a/compiler/pbraces.nim b/compiler/pbraces.nim
index fa4ccc139..eba6f0b62 100644
--- a/compiler/pbraces.nim
+++ b/compiler/pbraces.nim
@@ -906,9 +906,14 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
   optInd(p, result)
   if not isOperator(p.tok) and isExprStart(p):
     addSon(result, primary(p, mode))
-  if kind == nkDistinctTy and p.tok.tokType in {tkWith, tkWithout}:
-    let nodeKind = if p.tok.tokType == tkWith: nkWith
-                   else: nkWithout
+  if kind == nkDistinctTy and p.tok.tokType == tkSymbol:
+    var nodeKind: TNodeKind
+    if p.tok.ident.s == "with":
+      nodeKind = nkWith
+    elif p.tok.ident.s == "without":
+      nodeKind = nkWithout
+    else:
+      return result
     getTok(p)
     let list = newNodeP(nodeKind, p)
     result.addSon list
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 220693f68..bbe81fe37 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -1079,9 +1079,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
       gsub(g, n.sons[0])
       if n.len > 1:
         if n[1].kind == nkWith:
-          putWithSpace(g, tkWith, " with")
+          putWithSpace(g, tkSymbol, " with")
         else:
-          putWithSpace(g, tkWithout, " without")
+          putWithSpace(g, tkSymbol, " without")
         gcomma(g, n[1])
     else:
       put(g, tkDistinct, "distinct")
@@ -1166,6 +1166,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   of nkProcDef:
     if renderNoProcDefs notin g.flags: putWithSpace(g, tkProc, "proc")
     gproc(g, n)
+  of nkFuncDef:
+    if renderNoProcDefs notin g.flags: putWithSpace(g, tkFunc, "func")
+    gproc(g, n)
   of nkConverterDef:
     if renderNoProcDefs notin g.flags: putWithSpace(g, tkConverter, "converter")
     gproc(g, n)
@@ -1324,7 +1327,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
         if p.typ == nil or tfImplicitTypeParam notin p.typ.flags:
           return true
       return false
-    
+
     if n.hasExplicitParams:
       put(g, tkBracketLe, "[")
       gsemicolon(g, n)
@@ -1363,7 +1366,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
 proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string =
   var g: TSrcGen
   initSrcGen(g, renderFlags)
-  gsub(g, n)
+  # do not indent the initial statement list so that
+  # writeFile("file.nim", repr n)
+  # produces working Nim code:
+  if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
+    gstmts(g, n, emptyContext, doIndent = false)
+  else:
+    gsub(g, n)
   result = g.buf
 
 proc renderModule(n: PNode, filename: string,
diff --git a/compiler/rodread.nim b/compiler/rodread.nim
index f7e5a0f84..31b54d760 100644
--- a/compiler/rodread.nim
+++ b/compiler/rodread.nim
@@ -257,9 +257,9 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
       loc.k = low(loc.k)
     if r.s[r.pos] == '*':
       inc(r.pos)
-      loc.s = TStorageLoc(decodeVInt(r.s, r.pos))
+      loc.storage = TStorageLoc(decodeVInt(r.s, r.pos))
     else:
-      loc.s = low(loc.s)
+      loc.storage = low(loc.storage)
     if r.s[r.pos] == '$':
       inc(r.pos)
       loc.flags = cast[TLocFlags](int32(decodeVInt(r.s, r.pos)))
@@ -267,9 +267,10 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
       loc.flags = {}
     if r.s[r.pos] == '^':
       inc(r.pos)
-      loc.t = rrGetType(r, decodeVInt(r.s, r.pos), info)
+      loc.lode = decodeNode(r, info)
+      # rrGetType(r, decodeVInt(r.s, r.pos), info)
     else:
-      loc.t = nil
+      loc.lode = nil
     if r.s[r.pos] == '!':
       inc(r.pos)
       loc.r = rope(decodeStr(r.s, r.pos))
@@ -588,7 +589,7 @@ proc cmdChangeTriggersRecompilation(old, new: TCommands): bool =
       return false
   of cmdNone, cmdDoc, cmdInterpret, cmdPretty, cmdGenDepend, cmdDump,
       cmdCheck, cmdParse, cmdScan, cmdIdeTools, cmdDef,
-      cmdRst2html, cmdRst2tex, cmdInteractive, cmdRun:
+      cmdRst2html, cmdRst2tex, cmdInteractive, cmdRun, cmdJsonScript:
     discard
   # else: trigger recompilation:
   result = true
diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim
index d61d817dd..fb50c6473 100644
--- a/compiler/rodwrite.nim
+++ b/compiler/rodwrite.nim
@@ -175,16 +175,17 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
   var oldLen = result.len
   result.add('<')
   if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
-  if loc.s != low(loc.s):
+  if loc.storage != low(loc.storage):
     add(result, '*')
-    encodeVInt(ord(loc.s), result)
+    encodeVInt(ord(loc.storage), result)
   if loc.flags != {}:
     add(result, '$')
     encodeVInt(cast[int32](loc.flags), result)
-  if loc.t != nil:
+  if loc.lode != nil:
     add(result, '^')
-    encodeVInt(cast[int32](loc.t.id), result)
-    pushType(w, loc.t)
+    encodeNode(w, unknownLineInfo(), loc.lode, result)
+    #encodeVInt(cast[int32](loc.t.id), result)
+    #pushType(w, loc.t)
   if loc.r != nil:
     add(result, '!')
     encodeStr($loc.r, result)
@@ -579,7 +580,7 @@ proc process(c: PPassContext, n: PNode): PNode =
     for i in countup(0, sonsLen(n) - 1): discard process(c, n.sons[i])
     #var s = n.sons[namePos].sym
     #addInterfaceSym(w, s)
-  of nkProcDef, nkIteratorDef, nkConverterDef,
+  of nkProcDef, nkFuncDef, nkIteratorDef, nkConverterDef,
       nkTemplateDef, nkMacroDef:
     let s = n.sons[namePos].sym
     if s == nil: internalError(n.info, "rodwrite.process")
diff --git a/compiler/sem.nim b/compiler/sem.nim
index cd0df0de0..ebfdafea7 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -449,7 +449,7 @@ include semtypes, semtempl, semgnrc, semstmts, semexprs
 proc addCodeForGenerics(c: PContext, n: PNode) =
   for i in countup(c.lastGenericIdx, c.generics.len - 1):
     var prc = c.generics[i].inst.sym
-    if prc.kind in {skProc, skMethod, skConverter} and prc.magic == mNone:
+    if prc.kind in {skProc, skFunc, skMethod, skConverter} and prc.magic == mNone:
       if prc.ast == nil or prc.ast.sons[bodyPos] == nil:
         internalError(prc.info, "no code for " & prc.name.s)
       else:
@@ -495,13 +495,15 @@ proc isImportSystemStmt(n: PNode): bool =
   case n.kind
   of nkImportStmt:
     for x in n:
-      let f = checkModuleName(x, false)
+      if x.kind == nkIdent:
+        let f = checkModuleName(x, false)
+        if f == magicsys.systemModule.info.fileIndex:
+          return true
+  of nkImportExceptStmt, nkFromStmt:
+    if n[0].kind == nkIdent:
+      let f = checkModuleName(n[0], false)
       if f == magicsys.systemModule.info.fileIndex:
         return true
-  of nkImportExceptStmt, nkFromStmt:
-    let f = checkModuleName(n[0], false)
-    if f == magicsys.systemModule.info.fileIndex:
-      return true
   else: discard
 
 proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
index f2144037c..dbb2a140b 100644
--- a/compiler/semasgn.nim
+++ b/compiler/semasgn.nim
@@ -185,7 +185,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case t.kind
   of tyNone, tyEmpty, tyVoid: discard
   of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString,
-      tyPtr, tyString, tyRef:
+      tyPtr, tyString, tyRef, tyOpt:
     defaultOp(c, t, body, x, y)
   of tyArray, tySequence:
     if tfHasAsgn in t.flags:
@@ -227,9 +227,9 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
      tyTypeDesc, tyGenericInvocation, tyForward:
     internalError(c.info, "assignment requested for type: " & typeToString(t))
   of tyOrdinal, tyRange, tyInferred,
-     tyGenericInst, tyFieldAccessor, tyStatic, tyVar, tyAlias:
+     tyGenericInst, tyStatic, tyVar, tyAlias:
     liftBodyAux(c, lastSon(t), body, x, y)
-  of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("liftBodyAux")
+  of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("liftBodyAux")
 
 proc newProcType(info: TLineInfo; owner: PSym): PType =
   result = newType(tyProc, owner)
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 5984e25e0..9492e63f4 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -468,7 +468,7 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
     for i in countup(0, len(a)-1):
       var candidate = a.sons[i].sym
       if candidate.kind in {skProc, skMethod, skConverter,
-                            skIterator}:
+                            skFunc, skIterator}:
         # it suffices that the candidate has the proper number of generic
         # type parameters:
         if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index d422646a8..a3f0f715b 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -65,7 +65,7 @@ type
     efWantStmt, efAllowStmt, efDetermineType, efExplain,
     efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
     efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo,
-  
+
   TExprFlags* = set[TExprFlag]
 
   TTypeAttachedOp* = enum
@@ -112,6 +112,7 @@ type
     semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable,
                                 info: TLineInfo): PSym
     includedFiles*: IntSet    # used to detect recursive include files
+    pureEnumFields*: TStrTable   # pure enum fields that can be used unambiguously
     userPragmas*: TStrTable
     evalContext*: PEvalContext
     unknownIdents*: IntSet     # ids of all unknown identifiers to prevent
@@ -210,6 +211,7 @@ proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext
   result.converters = @[]
   result.patterns = @[]
   result.includedFiles = initIntSet()
+  initStrTable(result.pureEnumFields)
   initStrTable(result.userPragmas)
   result.generics = @[]
   result.unknownIdents = initIntSet()
@@ -371,7 +373,7 @@ proc makeRangeType*(c: PContext; first, last: BiggestInt;
   addSonSkipIntLit(result, intType) # basetype of range
 
 proc markIndirect*(c: PContext, s: PSym) {.inline.} =
-  if s.kind in {skProc, skConverter, skMethod, skIterator}:
+  if s.kind in {skProc, skFunc, skConverter, skMethod, skIterator}:
     incl(s.flags, sfAddrTaken)
     # XXX add to 'c' for global analysis
 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 74b074f61..161847a4f 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -246,8 +246,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
     localError(n.info, errXExpectsTypeOrValue, opToStr[m])
   else:
     n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
-    var typ = skipTypes(n.sons[1].typ, abstractVarRange +
-                                       {tyTypeDesc, tyFieldAccessor})
+    var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc})
     case typ.kind
     of tySequence, tyString, tyCString, tyOpenArray, tyVarargs:
       n.typ = getSysType(tyInt)
@@ -255,7 +254,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
       n.typ = typ.sons[0] # indextype
     of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32:
       # do not skip the range!
-      n.typ = n.sons[1].typ.skipTypes(abstractVar + {tyFieldAccessor})
+      n.typ = n.sons[1].typ.skipTypes(abstractVar)
     of tyGenericParam:
       # prepare this for resolving in semtypinst:
       # we must use copyTree here in order to avoid creating a cycle
@@ -279,7 +278,7 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
     n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
     n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
 
-  let t1 = n[1].typ.skipTypes({tyTypeDesc, tyFieldAccessor})
+  let t1 = n[1].typ.skipTypes({tyTypeDesc})
 
   if n[2].kind in {nkStrLit..nkTripleStrLit}:
     case n[2].strVal.normalize
@@ -573,7 +572,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
         optImplicitStatic notin gOptions: return
 
     if callee.magic notin ctfeWhitelist: return
-    if callee.kind notin {skProc, skConverter} or callee.isGenericRoutine:
+    if callee.kind notin {skProc, skFunc, skConverter} or callee.isGenericRoutine:
       return
 
     if n.typ != nil and typeAllowed(n.typ, skConst) != nil: return
@@ -615,10 +614,10 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
     # for typeof support.
     # for ``type(countup(1,3))``, see ``tests/ttoseq``.
     result = semOverloadedCall(c, n, nOrig,
-      {skProc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags)
+      {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags)
   else:
     result = semOverloadedCall(c, n, nOrig,
-      {skProc, skMethod, skConverter, skMacro, skTemplate}, flags)
+      {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags)
 
   if result != nil:
     if result.sons[0].kind != nkSym:
@@ -1086,9 +1085,6 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
         if field != nil:
           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
@@ -1229,7 +1225,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
             else: nil
     if s != nil:
       case s.kind
-      of skProc, skMethod, skConverter, skIterator:
+      of skProc, skFunc, skMethod, skConverter, skIterator:
         # type parameters: partial generic specialization
         n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
         result = explicitGenericInstantiation(c, n, s)
@@ -1397,7 +1393,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
 proc semReturn(c: PContext, n: PNode): PNode =
   result = n
   checkSonsLen(n, 1)
-  if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro} or (
+  if c.p.owner.kind in {skConverter, skMethod, skProc, skFunc, skMacro} or (
      c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure):
     if n.sons[0].kind != nkEmpty:
       # transform ``return expr`` to ``result = expr; return``
@@ -1620,9 +1616,10 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
   # Preserve the magic symbol in order to be handled in evals.nim
   internalAssert n.sons[0].sym.magic == mExpandToAst
   #n.typ = getSysSym("NimNode").typ # expandedSym.getReturnType
-  n.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode"
-          else: sysTypeFromName"PNimrodNode"
-  result = n
+  if n.kind == nkStmtList and n.len == 1: result = n[0]
+  else: result = n
+  result.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode"
+               else: sysTypeFromName"PNimrodNode"
 
 proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym,
                     flags: TExprFlags = {}): PNode =
@@ -2114,12 +2111,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   if nfSem in n.flags: return
   case n.kind
   of nkIdent, nkAccQuoted:
-    let checks = if efNoEvaluateGeneric in flags: {checkUndeclared}
-                 else: {checkUndeclared, checkModule, checkAmbiguity}
+    let checks = if efNoEvaluateGeneric in flags:
+        {checkUndeclared, checkPureEnumFields}
+      else:
+        {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields}
     var s = qualifiedLookUp(c, n, checks)
     if c.matchedConcept == nil: semCaptureSym(s, c.p.owner)
     result = semSym(c, n, s, flags)
-    if s.kind in {skProc, skMethod, skConverter, skIterator}:
+    if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator}:
       #performProcvarCheck(c, n, s)
       result = symChoice(c, n, s, scClosed)
       if result.kind == nkSym:
@@ -2214,7 +2213,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
           errorUseQualifier(c, n.info, s)
         elif s.magic == mNone: result = semDirectOp(c, n, flags)
         else: result = semMagic(c, n, s, flags)
-      of skProc, skMethod, skConverter, skIterator:
+      of skProc, skFunc, skMethod, skConverter, skIterator:
         if s.magic == mNone: result = semDirectOp(c, n, flags)
         else: result = semMagic(c, n, s, flags)
       else:
@@ -2328,6 +2327,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkPragma: pragma(c, c.p.owner, n, stmtPragmas)
   of nkIteratorDef: result = semIterator(c, n)
   of nkProcDef: result = semProc(c, n)
+  of nkFuncDef: result = semFunc(c, n)
   of nkMethodDef: result = semMethod(c, n)
   of nkConverterDef: result = semConverterDef(c, n)
   of nkMacroDef: result = semMacroDef(c, n)
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 612df1ba3..089e66abd 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -486,6 +486,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
       of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n)
       of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n)
       of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n)
+      of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[platform.hostOS].name), n)
+      of mBuildCPU: result = newStrNodeT(platform.CPU[platform.hostCPU].name.toLowerAscii, n)
       of mAppType: result = getAppType(n)
       of mNaN: result = newFloatNodeT(NaN, n)
       of mInf: result = newFloatNodeT(Inf, n)
@@ -498,7 +500,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
           result = newStrNodeT(lookupSymbol(s.name), n)
       else:
         result = copyTree(s.ast)
-    of {skProc, skMethod}:
+    of {skProc, skFunc, skMethod}:
       result = n
     of skType:
       # XXX gensym'ed symbols can come here and cannot be resolved. This is
@@ -522,7 +524,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
   of nkCallKinds:
     if n.sons[0].kind != nkSym: return
     var s = n.sons[0].sym
-    if s.kind != skProc: return
+    if s.kind != skProc and s.kind != skFunc: return
     try:
       case s.magic
       of mNone:
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index 7e55b266a..3cdb68df6 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -61,7 +61,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
   of skUnknown:
     # Introduced in this pass! Leave it as an identifier.
     result = n
-  of skProc, skMethod, skIterator, skConverter, skModule:
+  of skProc, skFunc, skMethod, skIterator, skConverter, skModule:
     result = symChoice(c, n, s, scOpen)
   of skTemplate:
     if macroToExpandSym(s):
@@ -106,6 +106,10 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
   let ident = considerQuotedIdent(n)
   var s = searchInScopes(c, ident).skipAlias(n)
   if s == nil:
+    s = strTableGet(c.pureEnumFields, ident)
+    if s != nil and contains(c.ambiguousSymbols, s.id):
+      s = nil
+  if s == nil:
     if ident.id notin ctx.toMixin and withinMixin notin flags:
       errorUndeclaredIdentifier(c, n.info, ident.s)
   else:
@@ -239,7 +243,7 @@ proc semGenericStmt(c: PContext, n: PNode,
       of skUnknown, skParam:
         # Leave it as an identifier.
         discard
-      of skProc, skMethod, skIterator, skConverter, skModule:
+      of skProc, skFunc, skMethod, skIterator, skConverter, skModule:
         result.sons[0] = sc
         # do not check of 's.magic==mRoof' here because it might be some
         # other '^' but after overload resolution the proper one:
@@ -433,7 +437,7 @@ proc semGenericStmt(c: PContext, n: PNode,
       for j in countup(0, L-3):
         addTempDecl(c, getIdentNode(a.sons[j]), skParam)
   of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
-     nkIteratorDef, nkLambdaKinds:
+     nkFuncDef, nkIteratorDef, nkLambdaKinds:
     checkSonsLen(n, bodyPos + 1)
     if n.sons[namePos].kind != nkEmpty:
       addTempDecl(c, getIdentNode(n.sons[0]), skProc)
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index a28d322b1..6abb34e90 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -36,7 +36,7 @@ proc rawPushProcCon(c: PContext, owner: PSym) =
   c.p = x
 
 proc rawHandleSelf(c: PContext; owner: PSym) =
-  const callableSymbols = {skProc, skMethod, skConverter, skIterator, skMacro}
+  const callableSymbols = {skProc, skFunc, skMethod, skConverter, skIterator, skMacro}
   if c.selfName != nil and owner.kind in callableSymbols and owner.typ != nil:
     let params = owner.typ.n
     if params.len > 1:
diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim
index 90c1a315a..2581f5728 100644
--- a/compiler/semparallel.nim
+++ b/compiler/semparallel.nim
@@ -387,7 +387,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
         addFactNeg(c.guards, canon(n.sons[0]))
     dec c.inLoop
   of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
-      nkMacroDef, nkTemplateDef, nkConstSection, nkPragma:
+      nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef:
     discard
   else:
     analyseSons(c, n)
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index e24c5fd29..e1a3939fc 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -835,7 +835,7 @@ proc track(tracked: PEffects, n: PNode) =
     setLen(tracked.locked, oldLocked)
     tracked.currLockLevel = oldLockLevel
   of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
-      nkMacroDef, nkTemplateDef, nkLambda, nkDo:
+      nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
     discard
   of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv:
     if n.len == 2: track(tracked, n.sons[1])
@@ -937,7 +937,7 @@ proc trackProc*(s: PSym, body: PNode) =
   track(t, body)
   if not isEmptyType(s.typ.sons[0]) and
       {tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and
-      s.kind in {skProc, skConverter, skMethod}:
+      s.kind in {skProc, skFunc, skConverter, skMethod}:
     var res = s.ast.sons[resultPos].sym # get result symbol
     if res.id notin t.init:
       message(body.info, warnProveInit, "result")
@@ -979,10 +979,10 @@ proc trackProc*(s: PSym, body: PNode) =
     message(s.info, warnLockLevel,
       "declared lock level is $1, but real lock level is $2" %
         [$s.typ.lockLevel, $t.maxLockLevel])
-  when useWriteTracking: trackWrites(s, body)
+  if s.kind == skFunc: trackWrites(s, body)
 
 proc trackTopLevelStmt*(module: PSym; n: PNode) =
-  if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,
+  if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
                 nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
     return
   var effects = newNode(nkEffectList, n.info)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index dbdb543f5..a4dd8f354 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -437,8 +437,6 @@ proc isDiscardUnderscore(v: PSym): bool =
 proc semUsing(c: PContext; n: PNode): PNode =
   result = ast.emptyNode
   if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "using")
-  if not experimentalMode(c):
-    localError(n.info, "use the {.experimental.} pragma to enable 'using'")
   for i in countup(0, sonsLen(n)-1):
     var a = n.sons[i]
     if gCmd == cmdIdeTools: suggestStmt(c, a)
@@ -493,6 +491,7 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
       addSon(obj.n, newSymNode(field))
       n.sons[0] = makeDeref x
       n.sons[1] = newSymNode(field)
+      n.typ = field.typ
     else:
       localError(n.info, "implicit object field construction " &
         "requires a .partial object, but got " & typeToString(obj))
@@ -1233,7 +1232,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
   s.typ = n.typ
   for i in 1..<params.len:
     if params[i].typ.kind in {tyTypeDesc, tyGenericParam,
-                              tyFromExpr, tyFieldAccessor}+tyTypeClasses:
+                              tyFromExpr}+tyTypeClasses:
       localError(params[i].info, "cannot infer type of parameter: " &
                  params[i].sym.name.s)
     #params[i].sym.owner = s
@@ -1603,6 +1602,9 @@ proc semIterator(c: PContext, n: PNode): PNode =
 proc semProc(c: PContext, n: PNode): PNode =
   result = semProcAux(c, n, skProc, procPragmas)
 
+proc semFunc(c: PContext, n: PNode): PNode =
+  result = semProcAux(c, n, skFunc, procPragmas)
+
 proc semMethod(c: PContext, n: PNode): PNode =
   if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method")
   result = semProcAux(c, n, skMethod, methodPragmas)
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 8ad8a6288..be8567c9c 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -449,6 +449,8 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
         a.sons[2] = semTemplBody(c, a.sons[2])
   of nkProcDef, nkLambdaKinds:
     result = semRoutineInTemplBody(c, n, skProc)
+  of nkFuncDef:
+    result = semRoutineInTemplBody(c, n, skFunc)
   of nkMethodDef:
     result = semRoutineInTemplBody(c, n, skMethod)
   of nkIteratorDef:
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index a7c9244cc..fbb5d0b6b 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -88,7 +88,9 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
       if not isPure: strTableAdd(c.module.tab, e)
     addSon(result.n, newSymNode(e))
     styleCheckDef(e)
-    if sfGenSym notin e.flags and not isPure: addDecl(c, e)
+    if sfGenSym notin e.flags:
+      if not isPure: addDecl(c, e)
+      else: importPureEnumField(c, e)
     if isPure and strTableIncl(symbols, e):
       wrongRedefinition(e.info, e.name.s)
     inc(counter)
@@ -1391,6 +1393,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     of mSet: result = semSet(c, n, prev)
     of mOrdinal: result = semOrdinal(c, n, prev)
     of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
+    of mOpt: result = semContainer(c, n, tyOpt, "opt", prev)
     of mVarargs: result = semVarargs(c, n, prev)
     of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
     of mExpr:
@@ -1590,6 +1593,8 @@ proc processMagicType(c: PContext, m: PSym) =
     setMagicType(m, tySet, 0)
   of mSeq:
     setMagicType(m, tySequence, 0)
+  of mOpt:
+    setMagicType(m, tyOpt, 0)
   of mOrdinal:
     setMagicType(m, tyOrdinal, 0)
     rawAddSon(m.typ, newTypeS(tyNone, c))
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index b4a61deb7..a3953d87e 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -170,7 +170,7 @@ proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
       if isTypeParam(n[i]): needsFixing = true
     if needsFixing:
       n.sons[0] = newSymNode(n.sons[0].sym.owner)
-      return cl.c.semOverloadedCall(cl.c, n, n, {skProc}, {})
+      return cl.c.semOverloadedCall(cl.c, n, n, {skProc, skFunc}, {})
 
   for i in 0 .. <n.safeLen:
     n.sons[i] = reResolveCallsWithTypedescParams(cl, n[i])
diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim
index 6eecf98d8..6043eb4a7 100644
--- a/compiler/sighashes.nim
+++ b/compiler/sighashes.nim
@@ -201,7 +201,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
   of tyRef, tyPtr, tyGenericBody, tyVar:
     c.hashType t.lastSon, flags
     if tfVarIsPtr in t.flags: c &= ".varisptr"
-  of tyFromExpr, tyFieldAccessor:
+  of tyFromExpr:
     c.hashTree(t.n)
   of tyTuple:
     if t.n != nil and CoType notin flags:
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 41596f05c..f2bc24399 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -936,7 +936,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
                                      tfExplicit notin aOrig.flags
 
     aOrig = if useTypeLoweringRuleInTypeClass:
-          aOrig.skipTypes({tyTypeDesc, tyFieldAccessor})
+          aOrig.skipTypes({tyTypeDesc})
         else:
           aOrig
 
@@ -1373,7 +1373,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
     # XXX: This is very hacky. It should be moved back into liftTypeParam
     if x.kind in {tyGenericInst, tyArray} and
        c.calleeSym != nil and
-       c.calleeSym.kind == skProc:
+       c.calleeSym.kind in {skProc, skFunc}:
       let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
       return typeRel(c, inst, a)
 
@@ -1832,7 +1832,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
       bothMetaCounter < 100:
     lastBindingsLength = m.bindings.counter
     inc(bothMetaCounter)
-    if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
+    if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
       result = c.semInferredLambda(c, m.bindings, arg)
     elif arg.kind != nkSym:
       return nil
@@ -1865,7 +1865,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
     else:
       result = implicitConv(nkHiddenStdConv, f, arg, m, c)
   of isInferred, isInferredConvertible:
-    if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
+    if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
       result = c.semInferredLambda(c, m.bindings, arg)
     elif arg.kind != nkSym:
       return nil
@@ -1952,7 +1952,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
     z.calleeSym = m.calleeSym
     var best = -1
     for i in countup(0, sonsLen(arg) - 1):
-      if arg.sons[i].sym.kind in {skProc, skMethod, skConverter, skIterator}:
+      if arg.sons[i].sym.kind in {skProc, skFunc, skMethod, skConverter, skIterator}:
         copyCandidate(z, m)
         z.callee = arg.sons[i].typ
         if tfUnresolved in z.callee.flags: continue
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 41959b018..f1ee49a54 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -121,7 +121,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
     if s.kind == skIterator:
       if c.tooEarly: return n
       else: return liftIterSym(n, getCurrOwner(c))
-    elif s.kind in {skProc, skConverter, skMethod} and not c.tooEarly:
+    elif s.kind in {skProc, skFunc, skConverter, skMethod} and not c.tooEarly:
       # top level .closure procs are still somewhat supported for 'Nake':
       return makeClosure(s, nil, n.info)
   #elif n.sym.kind in {skVar, skLet} and n.sym.typ.callConv == ccClosure:
diff --git a/compiler/types.nim b/compiler/types.nim
index dc7cd52db..3dbf6fd18 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -12,9 +12,6 @@
 import
   intsets, ast, astalgo, trees, msgs, strutils, platform, renderer
 
-proc firstOrd*(t: PType): BiggestInt
-proc lastOrd*(t: PType): BiggestInt
-proc lengthOrd*(t: PType): BiggestInt
 type
   TPreferedDesc* = enum
     preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg
@@ -48,8 +45,6 @@ type
 
 proc equalParams*(a, b: PNode): TParamsEquality
   # returns whether the parameter lists of the procs a, b are exactly the same
-proc isOrdinalType*(t: PType): bool
-proc enumHasHoles*(t: PType): bool
 
 const
   # TODO: Remove tyTypeDesc from each abstractX and (where necessary)
@@ -70,17 +65,6 @@ const
   typedescPtrs* = abstractPtrs + {tyTypeDesc}
   typedescInst* = abstractInst + {tyTypeDesc}
 
-proc containsObject*(t: PType): bool
-proc containsGarbageCollectedRef*(typ: PType): bool
-proc containsHiddenPointer*(typ: PType): bool
-proc canFormAcycle*(typ: PType): bool
-proc isCompatibleToCString*(a: PType): bool
-proc getOrdValue*(n: PNode): BiggestInt
-proc computeSize*(typ: PType): BiggestInt
-proc getSize*(typ: PType): BiggestInt
-proc isPureObject*(typ: PType): bool
-proc invalidGenericInst*(f: PType): bool
-  # for debugging
 type
   TTypeFieldResult* = enum
     frNone,                   # type has no object type field
@@ -92,16 +76,16 @@ proc analyseObjectWithTypeField*(t: PType): TTypeFieldResult
   # made or intializing of the type field suffices or if there is no type field
   # at all in this type.
 
-proc invalidGenericInst(f: PType): bool =
+proc invalidGenericInst*(f: PType): bool =
   result = f.kind == tyGenericInst and lastSon(f) == nil
 
-proc isPureObject(typ: PType): bool =
+proc isPureObject*(typ: PType): bool =
   var t = typ
   while t.kind == tyObject and t.sons[0] != nil:
     t = t.sons[0].skipTypes(skipPtrs)
   result = t.sym != nil and sfPure in t.sym.flags
 
-proc getOrdValue(n: PNode): BiggestInt =
+proc getOrdValue*(n: PNode): BiggestInt =
   case n.kind
   of nkCharLit..nkUInt64Lit: result = n.intVal
   of nkNilLit: result = 0
@@ -116,14 +100,6 @@ proc isIntLit*(t: PType): bool {.inline.} =
 proc isFloatLit*(t: PType): bool {.inline.} =
   result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit
 
-proc isCompatibleToCString(a: PType): bool =
-  if a.kind == tyArray:
-    if (firstOrd(a.sons[0]) == 0) and
-        (skipTypes(a.sons[0], {tyRange, tyGenericInst, tyAlias}).kind in
-            {tyInt..tyInt64, tyUInt..tyUInt64}) and
-        (a.sons[1].kind == tyChar):
-      result = true
-
 proc getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string =
   result = sym.owner.name.s & '.' & sym.name.s & '('
   var n = sym.typ.n
@@ -151,7 +127,7 @@ proc elemType*(t: PType): PType =
   else: result = t.lastSon
   assert(result != nil)
 
-proc isOrdinalType(t: PType): bool =
+proc isOrdinalType*(t: PType): bool =
   assert(t != nil)
   const
     # caution: uint, uint64 are no ordinal types!
@@ -159,7 +135,7 @@ proc isOrdinalType(t: PType): bool =
     parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tyDistinct}
   t.kind in baseKinds or (t.kind in parentKinds and isOrdinalType(t.sons[0]))
 
-proc enumHasHoles(t: PType): bool =
+proc enumHasHoles*(t: PType): bool =
   var b = t
   while b.kind in {tyRange, tyGenericInst, tyAlias}: b = b.sons[0]
   result = b.kind == tyEnum and tfEnumHasHoles in b.flags
@@ -252,7 +228,7 @@ proc searchTypeFor(t: PType, predicate: TTypePredicate): bool =
 proc isObjectPredicate(t: PType): bool =
   result = t.kind == tyObject
 
-proc containsObject(t: PType): bool =
+proc containsObject*(t: PType): bool =
   result = searchTypeFor(t, isObjectPredicate)
 
 proc isObjectWithTypeFieldPredicate(t: PType): bool =
@@ -297,7 +273,7 @@ proc isGCRef(t: PType): bool =
   result = t.kind in GcTypeKinds or
     (t.kind == tyProc and t.callConv == ccClosure)
 
-proc containsGarbageCollectedRef(typ: PType): bool =
+proc containsGarbageCollectedRef*(typ: PType): bool =
   # returns true if typ contains a reference, sequence or string (all the
   # things that are garbage-collected)
   result = searchTypeFor(typ, isGCRef)
@@ -312,7 +288,7 @@ proc containsTyRef*(typ: PType): bool =
 proc isHiddenPointer(t: PType): bool =
   result = t.kind in {tyString, tySequence}
 
-proc containsHiddenPointer(typ: PType): bool =
+proc containsHiddenPointer*(typ: PType): bool =
   # returns true if typ contains a string, table or sequence (all the things
   # that need to be copied deeply)
   result = searchTypeFor(typ, isHiddenPointer)
@@ -355,7 +331,7 @@ proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool =
   of tyProc: result = typ.callConv == ccClosure
   else: discard
 
-proc canFormAcycle(typ: PType): bool =
+proc canFormAcycle*(typ: PType): bool =
   var marker = initIntSet()
   result = canFormAcycleAux(marker, typ, typ.id)
 
@@ -521,7 +497,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
   of tyExpr:
     internalAssert t.len == 0
     result = "untyped"
-  of tyFromExpr, tyFieldAccessor:
+  of tyFromExpr:
     result = renderTree(t.n)
   of tyArray:
     if t.sons[0].kind == tyRange:
@@ -532,6 +508,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
           typeToString(t.sons[1]) & ']'
   of tySequence:
     result = "seq[" & typeToString(t.sons[0]) & ']'
+  of tyOpt:
+    result = "opt[" & typeToString(t.sons[0]) & ']'
   of tyOrdinal:
     result = "ordinal[" & typeToString(t.sons[0]) & ']'
   of tySet:
@@ -605,7 +583,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
     result = typeToStr[t.kind]
   result.addTypeFlags(t)
 
-proc firstOrd(t: PType): BiggestInt =
+proc firstOrd*(t: PType): BiggestInt =
   case t.kind
   of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy:
     result = 0
@@ -630,7 +608,7 @@ proc firstOrd(t: PType): BiggestInt =
     else:
       assert(t.n.sons[0].kind == nkSym)
       result = t.n.sons[0].sym.position
-  of tyGenericInst, tyDistinct, tyTypeDesc, tyFieldAccessor, tyAlias:
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias:
     result = firstOrd(lastSon(t))
   of tyOrdinal:
     if t.len > 0: result = firstOrd(lastSon(t))
@@ -639,7 +617,7 @@ proc firstOrd(t: PType): BiggestInt =
     internalError("invalid kind for first(" & $t.kind & ')')
     result = 0
 
-proc lastOrd(t: PType): BiggestInt =
+proc lastOrd*(t: PType): BiggestInt =
   case t.kind
   of tyBool: result = 1
   of tyChar: result = 255
@@ -666,7 +644,7 @@ proc lastOrd(t: PType): BiggestInt =
   of tyEnum:
     assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
     result = t.n.sons[sonsLen(t.n) - 1].sym.position
-  of tyGenericInst, tyDistinct, tyTypeDesc, tyFieldAccessor, tyAlias:
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias:
     result = lastOrd(lastSon(t))
   of tyProxy: result = 0
   of tyOrdinal:
@@ -676,7 +654,7 @@ proc lastOrd(t: PType): BiggestInt =
     internalError("invalid kind for last(" & $t.kind & ')')
     result = 0
 
-proc lengthOrd(t: PType): BiggestInt =
+proc lengthOrd*(t: PType): BiggestInt =
   case t.kind
   of tyInt64, tyInt32, tyInt: result = lastOrd(t)
   of tyDistinct: result = lengthOrd(t.sons[0])
@@ -689,6 +667,14 @@ proc lengthOrd(t: PType): BiggestInt =
     else:
       result = lastOrd(t) - firstOrd(t) + 1
 
+proc isCompatibleToCString*(a: PType): bool =
+  if a.kind == tyArray:
+    if (firstOrd(a.sons[0]) == 0) and
+        (skipTypes(a.sons[0], {tyRange, tyGenericInst, tyAlias}).kind in
+            {tyInt..tyInt64, tyUInt..tyUInt64}) and
+        (a.sons[1].kind == tyChar):
+      result = true
+
 # -------------- type equality -----------------------------------------------
 
 type
@@ -989,7 +975,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
       result = a.sym.position == b.sym.position
   of tyGenericInvocation, tyGenericBody, tySequence,
      tyOpenArray, tySet, tyRef, tyPtr, tyVar,
-     tyArray, tyProc, tyVarargs, tyOrdinal, tyTypeClasses, tyFieldAccessor:
+     tyArray, tyProc, tyVarargs, tyOrdinal, tyTypeClasses, tyOpt:
     cycleCheck()
     if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
     result = sameChildrenAux(a, b, c) and sameFlags(a, b)
@@ -1007,7 +993,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
     cycleCheck()
     result = sameTypeAux(a.lastSon, b.lastSon, c)
   of tyNone: result = false
-  of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("sameFlags")
+  of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("sameFlags")
 
 proc sameBackendType*(x, y: PType): bool =
   var c = initSameTypeClosure()
@@ -1089,7 +1075,7 @@ proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
       of nkNone..nkNilLit:
         discard
       else:
-        if n.kind == nkRecCase and kind in {skProc, skConst}:
+        if n.kind == nkRecCase and kind in {skProc, skFunc, skConst}:
           return n[0].typ
         for i in countup(0, sonsLen(n) - 1):
           let it = n.sons[i]
@@ -1107,7 +1093,7 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
 
 proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
                     flags: TTypeAllowedFlags = {}): PType =
-  assert(kind in {skVar, skLet, skConst, skProc, skParam, skResult})
+  assert(kind in {skVar, skLet, skConst, skProc, skFunc, skParam, skResult})
   # if we have already checked the type, return true, because we stop the
   # evaluation if something is wrong:
   result = nil
@@ -1116,7 +1102,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
   var t = skipTypes(typ, abstractInst-{tyTypeDesc})
   case t.kind
   of tyVar:
-    if kind in {skProc, skConst}: return t
+    if kind in {skProc, skFunc, skConst}: return t
     var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc})
     case t2.kind
     of tyVar:
@@ -1144,7 +1130,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
   of tyTypeClasses:
     if not (tfGenericTypeParam in t.flags or taField notin flags): result = t
   of tyGenericBody, tyGenericParam, tyGenericInvocation,
-     tyNone, tyForward, tyFromExpr, tyFieldAccessor:
+     tyNone, tyForward, tyFromExpr:
     result = t
   of tyNil:
     if kind != skConst: result = t
@@ -1160,7 +1146,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
   of tyOpenArray, tyVarargs:
     if kind != skParam: result = t
     else: result = typeAllowedAux(marker, t.sons[0], skVar, flags)
-  of tySequence:
+  of tySequence, tyOpt:
     if t.sons[0].kind != tyEmpty:
       result = typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap})
   of tyArray:
@@ -1176,7 +1162,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
       result = typeAllowedAux(marker, t.sons[i], kind, flags)
       if result != nil: break
   of tyObject, tyTuple:
-    if kind in {skProc, skConst} and
+    if kind in {skProc, skFunc, skConst} and
         t.kind == tyObject and t.sons[0] != nil: return t
     let flags = flags+{taField}
     for i in countup(0, sonsLen(t) - 1):
@@ -1188,7 +1174,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
     # for now same as error node; we say it's a valid type as it should
     # prevent cascading errors:
     result = nil
-  of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("typeAllowedAux")
+  of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("typeAllowedAux")
 
 proc typeAllowed*(t: PType, kind: TSymKind): PType =
   # returns 'nil' on success and otherwise the part of the type that is
@@ -1199,6 +1185,63 @@ proc typeAllowed*(t: PType, kind: TSymKind): PType =
 proc align(address, alignment: BiggestInt): BiggestInt =
   result = (address + (alignment - 1)) and not (alignment - 1)
 
+type
+  OptKind* = enum  ## What to map 'opt T' to internally.
+    oBool      ## opt[T] requires an additional 'bool' field
+    oNil       ## opt[T] has no overhead since 'nil'
+               ## is available
+    oEnum      ## We can use some enum value that is not yet
+               ## used for opt[T]
+    oPtr       ## opt[T] actually introduces a hidden pointer
+               ## in order for the type recursion to work
+
+proc optKind*(typ: PType): OptKind =
+  ## return true iff 'opt[T]' can be mapped to 'T' internally
+  ## because we have a 'nil' value available:
+  assert typ.kind == tyOpt
+  case typ.sons[0].skipTypes(abstractInst).kind
+  of tyRef, tyPtr, tyProc:
+    result = oNil
+  of tyArray, tyObject, tyTuple:
+    result = oPtr
+  of tyBool: result = oEnum
+  of tyEnum:
+    assert(typ.n.sons[0].kind == nkSym)
+    if typ.n.sons[0].sym.position != low(int):
+      result = oEnum
+    else:
+      result = oBool
+  else:
+    result = oBool
+
+proc optLowering*(typ: PType): PType =
+  case optKind(typ)
+  of oNil: result = typ.sons[0]
+  of oPtr:
+    result = newType(tyOptAsRef, typ.owner)
+    result.rawAddSon typ.sons[0]
+  of oBool:
+    result = newType(tyTuple, typ.owner)
+    result.rawAddSon newType(tyBool, typ.owner)
+    result.rawAddSon typ.sons[0]
+  of oEnum:
+    if lastOrd(typ) + 1 < `shl`(BiggestInt(1), 32):
+      result = newType(tyInt32, typ.owner)
+    else:
+      result = newType(tyInt64, typ.owner)
+
+proc optEnumValue*(typ: PType): BiggestInt =
+  assert typ.kind == tyOpt
+  assert optKind(typ) == oEnum
+  let elem = typ.sons[0].skipTypes(abstractInst).kind
+  if elem == tyBool:
+    result = 2
+  else:
+    assert elem == tyEnum
+    assert typ.n.sons[0].sym.position != low(int)
+    result = typ.n.sons[0].sym.position - 1
+
+
 const
   szNonConcreteType* = -3
   szIllegalRecursion* = -2
@@ -1353,13 +1396,21 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
   of tyStatic:
     result = if typ.n != nil: computeSizeAux(typ.lastSon, a)
              else: szUnknownSize
+  of tyOpt:
+    case optKind(typ)
+    of oBool: result = computeSizeAux(lastSon(typ), a) + 1
+    of oEnum:
+      if lastOrd(typ) + 1 < `shl`(BiggestInt(1), 32): result = 4
+      else: result = 8
+    of oNil: result = computeSizeAux(lastSon(typ), a)
+    of oPtr: result = ptrSize
   else:
     #internalError("computeSizeAux()")
     result = szUnknownSize
   typ.size = result
   typ.align = int16(a)
 
-proc computeSize(typ: PType): BiggestInt =
+proc computeSize*(typ: PType): BiggestInt =
   var a: BiggestInt = 1
   result = computeSizeAux(typ, a)
 
@@ -1368,7 +1419,7 @@ proc getReturnType*(s: PSym): PType =
   assert s.kind in skProcKinds
   result = s.typ.sons[0]
 
-proc getSize(typ: PType): BiggestInt =
+proc getSize*(typ: PType): BiggestInt =
   result = computeSize(typ)
   if result < 0: internalError("getSize: " & $typ.kind)
 
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 8d4359db9..08605cad1 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -19,7 +19,7 @@ import ast except getstr
 import
   strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes,
   parser, vmdeps, idents, trees, renderer, options, transf, parseutils,
-  vmmarshal
+  vmmarshal, gorgeimpl
 
 from semfold import leValueConv, ordinalValToString
 from evaltempl import evalTemplate
@@ -66,7 +66,10 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) =
     stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1)
     var info = c.debug[pc]
     # we now use the same format as in system/except.nim
-    var s = toFilename(info)
+    var s = substr(toFilename(info), 0)
+    # this 'substr' prevents a strange corruption. XXX This needs to be
+    # investigated eventually but first attempts to fix it broke everything
+    # see the araq-wip-fixed-writebarrier branch.
     var line = toLinenumber(info)
     if line > 0:
       add(s, '(')
@@ -979,7 +982,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
           let node = regs[rb+i].regToNode
           node.info = c.debug[pc]
           macroCall.add(node)
-        let a = evalTemplate(macroCall, prc, genSymOwner)
+        var a = evalTemplate(macroCall, prc, genSymOwner)
+        if a.kind == nkStmtList and a.len == 1: a = a[0]
         a.recSetFlagIsRef
         ensureKind(rkNode)
         regs[ra].node = a
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index b9bbba551..29c1129b5 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -7,50 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-import ast, types, msgs, os, osproc, streams, options, idents, securehash
-
-proc readOutput(p: Process): (string, int) =
-  result[0] = ""
-  var output = p.outputStream
-  while not output.atEnd:
-    result[0].add(output.readLine)
-    result[0].add("\n")
-  if result[0].len > 0:
-    result[0].setLen(result[0].len - "\n".len)
-  result[1] = p.waitForExit
-
-proc opGorge*(cmd, input, cache: string, info: TLineInfo): (string, int) =
-  let workingDir = parentDir(info.toFullPath)
-  if cache.len > 0:# and optForceFullMake notin gGlobalOptions:
-    let h = secureHash(cmd & "\t" & input & "\t" & cache)
-    let filename = options.toGeneratedFile("gorge_" & $h, "txt")
-    var f: File
-    if open(f, filename):
-      result = (f.readAll, 0)
-      f.close
-      return
-    var readSuccessful = false
-    try:
-      var p = startProcess(cmd, workingDir,
-                           options={poEvalCommand, poStderrToStdout})
-      if input.len != 0:
-        p.inputStream.write(input)
-        p.inputStream.close()
-      result = p.readOutput
-      readSuccessful = true
-      writeFile(filename, result[0])
-    except IOError, OSError:
-      if not readSuccessful: result = ("", -1)
-  else:
-    try:
-      var p = startProcess(cmd, workingDir,
-                           options={poEvalCommand, poStderrToStdout})
-      if input.len != 0:
-        p.inputStream.write(input)
-        p.inputStream.close()
-      result = p.readOutput
-    except IOError, OSError:
-      result = ("", -1)
+import ast, types, msgs, os, streams, options, idents
 
 proc opSlurp*(file: string, info: TLineInfo, module: PSym): string =
   try:
@@ -250,6 +207,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
       result = mapTypeToBracket("ref", mRef, t, info)
   of tyVar: result = mapTypeToBracket("var", mVar, t, info)
   of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)
+  of tyOpt: result = mapTypeToBracket("opt", mOpt, t, info)
   of tyProc:
     if inst:
       result = newNodeX(nkProcTy)
@@ -304,7 +262,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
   of tyNot: result = mapTypeToBracket("not", mNot, t, info)
   of tyAnything: result = atomicType("anything", mNone)
   of tyInferred: internalAssert false
-  of tyStatic, tyFromExpr, tyFieldAccessor:
+  of tyStatic, tyFromExpr:
     if inst:
       if t.n != nil: result = t.n.copyTree
       else: result = atomicType("void", mVoid)
@@ -313,7 +271,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
       result.add atomicType("static", mNone)
       if t.n != nil:
         result.add t.n.copyTree
-  of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("mapTypeToAstX")
+  of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("mapTypeToAstX")
 
 proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode =
   result = mapTypeToAstX(t, info, false, true)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 3d291d8a2..ee9af7de2 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -1289,7 +1289,7 @@ proc checkCanEval(c: PCtx; n: PNode) =
   if s.kind in {skVar, skTemp, skLet, skParam, skResult} and
       not s.isOwnedBy(c.prc.sym) and s.owner != c.module and c.mode != emRepl:
     cannotEval(n)
-  elif s.kind in {skProc, skConverter, skMethod,
+  elif s.kind in {skProc, skFunc, skConverter, skMethod,
                   skIterator} and sfForward in s.flags:
     cannotEval(n)
 
@@ -1537,6 +1537,8 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
       addSon(result, getNullValue(t.sons[i], info))
   of tySet:
     result = newNodeIT(nkCurly, info, t)
+  of tyOpt:
+    result = newNodeIT(nkNilLit, info, t)
   else:
     globalError(info, "cannot create null element for: " & $t.kind)
 
@@ -1712,7 +1714,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     case s.kind
     of skVar, skForVar, skTemp, skLet, skParam, skResult:
       genRdVar(c, n, dest, flags)
-    of skProc, skConverter, skMacro, skTemplate, skMethod, skIterator:
+    of skProc, skFunc, skConverter, skMacro, skTemplate, skMethod, skIterator:
       # 'skTemplate' is only allowed for 'getAst' support:
       if procIsCallback(c, s): discard
       elif sfImportc in s.flags: c.importcSym(n.info, s)
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index 773ab8ff5..1e89ea1e2 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -30,7 +30,7 @@ type
     wMacro, wMethod, wMixin, wMod, wNil,
     wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn,
     wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wUsing, wVar,
-    wWhen, wWhile, wWith, wWithout, wXor, wYield,
+    wWhen, wWhile, wXor, wYield,
 
     wColon, wColonColon, wEquals, wDot, wDotDot,
     wStar, wMinus,
@@ -116,7 +116,7 @@ const
     "out", "proc", "ptr", "raise", "ref", "return",
     "shl", "shr", "static",
     "template", "try", "tuple", "type", "using", "var",
-    "when", "while", "with", "without", "xor",
+    "when", "while", "xor",
     "yield",
 
     ":", "::", "=", ".", "..",
diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim
index 443e8ddf1..fe71e5b31 100644
--- a/compiler/writetracking.nim
+++ b/compiler/writetracking.nim
@@ -248,6 +248,8 @@ proc markWriteOrEscape(w: var W) =
       for p in a.dest:
         if p.kind == skParam and p.owner == w.owner:
           incl(p.flags, sfWrittenTo)
+          if w.owner.kind == skFunc and p.typ.kind != tyVar:
+            localError(a.info, "write access to non-var parameter: " & p.name.s)
 
     if {rootIsResultOrParam, rootIsHeapAccess, markAsEscaping}*a.destInfo != {}:
       var destIsParam = false
diff --git a/doc/keywords.txt b/doc/keywords.txt
index 405bf1981..f8b444924 100644
--- a/doc/keywords.txt
+++ b/doc/keywords.txt
@@ -16,6 +16,6 @@ shl shr static
 template try tuple type
 using
 var
-when while with without
+when while
 xor
 yield
diff --git a/doc/manual/generics.txt b/doc/manual/generics.txt
index 87fcb7828..cceea33c0 100644
--- a/doc/manual/generics.txt
+++ b/doc/manual/generics.txt
@@ -633,6 +633,9 @@ the ``vtptr`` magic produced types bound to ``ptr`` types.
 Symbol lookup in generics
 -------------------------
 
+Open and Closed symbols
+~~~~~~~~~~~~~~~~~~~~~~~
+
 The symbol binding rules in generics are slightly subtle: There are "open" and
 "closed" symbols. A "closed" symbol cannot be re-bound in the instantiation
 context, an "open" symbol can. Per default overloaded symbols are open
@@ -658,6 +661,9 @@ the ``Index`` type is defined *after* the ``==`` for tuples; yet the example
 compiles as the instantiation takes the currently defined symbols into account
 too.
 
+Mixin statement
+---------------
+
 A symbol can be forced to be open by a `mixin`:idx: declaration:
 
 .. code-block:: nim
diff --git a/doc/manual/stmts.txt b/doc/manual/stmts.txt
index 5668b8cc2..3a86a9730 100644
--- a/doc/manual/stmts.txt
+++ b/doc/manual/stmts.txt
@@ -547,9 +547,6 @@ Instead of:
 Using statement
 ---------------
 
-**Warning**: The ``using`` statement is experimental and has to be
-explicitly enabled with the `experimental`:idx: pragma or command line option!
-
 The using statement provides syntactic convenience in modules where
 the same parameter names and types are used over and over. Instead of:
 
@@ -563,7 +560,6 @@ name ``c`` should default to type ``Context``, ``n`` should default to
 ``Node`` etc.:
 
 .. code-block:: nim
-  {.experimental.}
   using
     c: Context
     n: Node
diff --git a/doc/manual/types.txt b/doc/manual/types.txt
index 927cda9e1..5d2ad01d7 100644
--- a/doc/manual/types.txt
+++ b/doc/manual/types.txt
@@ -550,8 +550,7 @@ order. The *names* of the fields also have to be identical.
 
 The assignment operator for tuples copies each component.
 The default assignment operator for objects copies each component. Overloading
-of the assignment operator for objects is not possible, but this will change
-in future versions of the compiler.
+of the assignment operator is described in `type-bound-operations-operator`_. 
 
 .. code-block:: nim
 
diff --git a/doc/tut1.rst b/doc/tut1.rst
index 89893a39a..be5cd8c11 100644
--- a/doc/tut1.rst
+++ b/doc/tut1.rst
@@ -185,9 +185,8 @@ to a storage location:
   var x = "abc" # introduces a new variable `x` and assigns a value to it
   x = "xyz"     # assigns a new value to `x`
 
-``=`` is the *assignment operator*. The assignment operator cannot be
-overloaded, overwritten or forbidden, but this might change in a future version
-of Nim. You can declare multiple variables with a single assignment
+``=`` is the *assignment operator*. The assignment operator can be
+overloaded. You can declare multiple variables with a single assignment 
 statement and all the variables will have the same value:
 
 .. code-block::
diff --git a/koch.nim b/koch.nim
index 6ae45fcb7..6b267ce46 100644
--- a/koch.nim
+++ b/koch.nim
@@ -262,6 +262,9 @@ proc buildTools(latest: bool) =
   let nimgrepExe = "bin/nimgrep".exe
   nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim"
   when defined(windows): buildVccTool()
+
+  nimexec "c -o:" & ("bin/nimresolve".exe) & " tools/nimresolve.nim"
+
   buildNimble(latest)
 
 proc nsis(args: string) =
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index e88002c7b..8c70d2b47 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -75,7 +75,8 @@ type
     nnkClosure,
     nnkGotoState,
     nnkState,
-    nnkBreakState
+    nnkBreakState,
+    nnkFuncDef
 
   NimNodeKinds* = set[NimNodeKind]
   NimTypeKind* = enum  # some types are no longer used, see ast.nim
@@ -96,14 +97,14 @@ type
     ntyError,
     ntyBuiltinTypeClass, ntyUserTypeClass, ntyUserTypeClassInst,
     ntyCompositeTypeClass, ntyInferred, ntyAnd, ntyOr, ntyNot,
-    ntyAnything, ntyStatic, ntyFromExpr, ntyFieldAccessor, ntyVoid
+    ntyAnything, ntyStatic, ntyFromExpr, ntyOpt, ntyVoid
 
   TNimTypeKinds* {.deprecated.} = set[NimTypeKind]
   NimSymKind* = enum
     nskUnknown, nskConditional, nskDynLib, nskParam,
     nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet,
     nskConst, nskResult,
-    nskProc, nskMethod, nskIterator,
+    nskProc, nskFunc, nskMethod, nskIterator,
     nskConverter, nskMacro, nskTemplate, nskField,
     nskEnumField, nskForVar, nskLabel,
     nskStub
@@ -843,7 +844,8 @@ proc last*(node: NimNode): NimNode {.compileTime.} = node[<node.len]
 
 
 const
-  RoutineNodes* = {nnkProcDef, nnkMethodDef, nnkDo, nnkLambda, nnkIteratorDef, nnkTemplateDef, nnkConverterDef}
+  RoutineNodes* = {nnkProcDef, nnkFuncDef, nnkMethodDef, nnkDo, nnkLambda,
+                   nnkIteratorDef, nnkTemplateDef, nnkConverterDef}
   AtomicNodes* = {nnkNone..nnkNilLit}
   CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
     nnkCallStrLit, nnkHiddenCallConv}
diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim
index 72b139202..16580b318 100644
--- a/lib/core/typeinfo.nim
+++ b/lib/core/typeinfo.nim
@@ -54,6 +54,7 @@ type
     akUInt16 = 42,      ## any represents an unsigned in16
     akUInt32 = 43,      ## any represents an unsigned int32
     akUInt64 = 44,      ## any represents an unsigned int64
+#    akOpt = 44+18       ## the builtin 'opt' type.
 
   Any* = object          ## can represent any nim value; NOTE: the wrapped
                           ## value can be modified with its wrapper! This means
diff --git a/lib/genode_cpp/threads.h b/lib/genode_cpp/threads.h
index 043f808f1..a7cb2f17b 100644
--- a/lib/genode_cpp/threads.h
+++ b/lib/genode_cpp/threads.h
@@ -31,8 +31,12 @@ struct Nim::SysThread
 		void entry() override {
 			(_func)(_arg); }
 
-		Thread(Genode::Env &env, Genode::size_t stack_size, Entry func, void *arg)
-		: Genode::Thread(env, "nim-thread", stack_size), _func(func), _arg(arg)
+		Thread(Genode::Env &env, Genode::size_t stack_size, Entry func, void *arg, int affinity)
+		: Genode::Thread(env, "nim-thread", stack_size,
+		                 env.cpu().affinity_space().location_of_index(affinity),
+		                 Genode::Cpu_session::Weight(Genode::Cpu_session::Weight::DEFAULT_WEIGHT-1),
+		                 env.cpu()),
+		  _func(func), _arg(arg)
 		{
 			Genode::Thread::start();
 		}
@@ -40,8 +44,8 @@ struct Nim::SysThread
 
 	Genode::Constructible<Thread> _thread;
 
-	void initThread(Genode::Env *env, Genode::size_t stack_size, Entry func, void *arg) {
-		_thread.construct(*env, stack_size, func, arg); }
+	void initThread(Genode::Env *env, Genode::size_t stack_size, Entry func, void *arg, int aff) {
+		_thread.construct(*env, stack_size, func, arg, aff); }
 
 	void joinThread() {
 		_thread->join(); }
diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim
index 1633d48f7..53dafdda7 100644
--- a/lib/impure/db_sqlite.nim
+++ b/lib/impure/db_sqlite.nim
@@ -129,7 +129,8 @@ proc tryExec*(db: DbConn, query: SqlQuery,
   var q = dbFormat(query, args)
   var stmt: sqlite3.Pstmt
   if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
-    if step(stmt) == SQLITE_DONE:
+    let x = step(stmt)
+    if x in {SQLITE_DONE, SQLITE_ROW}:
       result = finalize(stmt) == SQLITE_OK
 
 proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`])  {.
diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim
index 0d359d9e6..13eb1e759 100644
--- a/lib/js/jsffi.nim
+++ b/lib/js/jsffi.nim
@@ -300,7 +300,7 @@ macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V],
   result = quote do:
     (`dotOp`(`obj`, `field`))()
   for elem in args:
-    result[0].add elem
+    result.add elem
 
 # Iterators:
 
@@ -471,7 +471,7 @@ macro bindMethod*(procedure: typed): auto =
     # construct the `this` parameter:
     thisQuote = quote do:
       var `this` {. nodecl, importc .} : `thisType`
-    call = newNimNode(nnkCall).add(rawProc[0], thisQuote[0][0][0][0])
+    call = newNimNode(nnkCall).add(rawProc[0], thisQuote[0][0][0])
   # construct the procedure call inside the method
   if args.len > 2:
     for idx in 2..args.len-1:
@@ -483,6 +483,6 @@ macro bindMethod*(procedure: typed): auto =
       params,
       rawProc[4],
       rawProc[5],
-      newTree(nnkStmtList, thisQuote[0], call)
+      newTree(nnkStmtList, thisQuote, call)
   )
   result = body
diff --git a/lib/nimbase.h b/lib/nimbase.h
index 70391024f..34a2e43e7 100644
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -373,11 +373,13 @@ static N_INLINE(NI32, float32ToInt32)(float x) {
 
 #define float64ToInt64(x) ((NI64) (x))
 
+#define NIM_STRLIT_FLAG ((NU)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */
+
 #define STRING_LITERAL(name, str, length) \
-  static const struct {                   \
-    TGenericSeq Sup;                      \
-    NIM_CHAR data[(length) + 1];          \
-  } name = {{length, length}, str}
+   static const struct {                   \
+     TGenericSeq Sup;                      \
+     NIM_CHAR data[(length) + 1];          \
+  } name = {{length, (NI) ((NU)length | NIM_STRLIT_FLAG)}, str}
 
 typedef struct TStringDesc* string;
 
diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim
index 06b90768c..1d396d9e0 100644
--- a/lib/packages/docutils/highlite.nim
+++ b/lib/packages/docutils/highlite.nim
@@ -58,8 +58,8 @@ const
     "interface", "is", "isnot", "iterator", "let", "macro", "method",
     "mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc",
     "ptr", "raise", "ref", "return", "shl", "shr", "static",
-    "template", "try", "tuple", "type", "using", "var", "when", "while", "with",
-    "without", "xor", "yield"]
+    "template", "try", "tuple", "type", "using", "var", "when", "while",
+    "xor", "yield"]
 
 proc getSourceLanguage*(name: string): SourceLanguage =
   for i in countup(succ(low(SourceLanguage)), high(SourceLanguage)):
diff --git a/lib/pure/basic2d.nim b/lib/pure/basic2d.nim
deleted file mode 100644
index 31b3814d6..000000000
--- a/lib/pure/basic2d.nim
+++ /dev/null
@@ -1,855 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2013 Robert Persson
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-import math
-import strutils
-
-
-## Basic 2d support with vectors, points, matrices and some basic utilities.
-## Vectors are implemented as direction vectors, ie. when transformed with a matrix
-## the translation part of matrix is ignored.
-## Operators `+` , `-` , `*` , `/` , `+=` , `-=` , `*=` and `/=` are implemented for vectors and scalars.
-##
-## Quick start example:
-##
-## .. code-block:: nim
-##
-##   # Create a matrix which first rotates, then scales and at last translates
-##
-##   var m:Matrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0)
-##
-##   # Create a 2d point at (100,0) and a vector (5,2)
-##
-##   var pt:Point2d=point2d(100.0,0.0)
-##
-##   var vec:Vector2d=vector2d(5.0,2.0)
-##
-##
-##   pt &= m # transforms pt in place
-##
-##   var pt2:Point2d=pt & m #concatenates pt with m and returns a new point
-##
-##   var vec2:Vector2d=vec & m #concatenates vec with m and returns a new vector
-
-
-const
-  DEG360* = PI * 2.0
-    ## 360 degrees in radians.
-  DEG270* = PI * 1.5
-    ## 270 degrees in radians.
-  DEG180* = PI
-    ## 180 degrees in radians.
-  DEG90* = PI / 2.0
-    ## 90 degrees in radians.
-  DEG60* = PI / 3.0
-    ## 60 degrees in radians.
-  DEG45* = PI / 4.0
-    ## 45 degrees in radians.
-  DEG30* = PI / 6.0
-    ## 30 degrees in radians.
-  DEG15* = PI / 12.0
-    ## 15 degrees in radians.
-  RAD2DEGCONST = 180.0 / PI
-    ## used internally by DegToRad and RadToDeg
-
-type
-    Matrix2d* = object
-      ## Implements a row major 2d matrix, which means
-      ## transformations are applied the order they are concatenated.
-      ## The rightmost column of the 3x3 matrix is left out since normally
-      ## not used for geometric transformations in 2d.
-      ax*,ay*,bx*,by*,tx*,ty*:float
-    Point2d* = object
-      ## Implements a non-homogeneous 2d point stored as
-      ## an `x` coordinate and an `y` coordinate.
-      x*,y*:float
-    Vector2d* = object
-      ## Implements a 2d **direction vector** stored as
-      ## an `x` coordinate and an `y` coordinate. Direction vector means,
-      ## that when transforming a vector with a matrix, the translational
-      ## part of the matrix is ignored.
-      x*,y*:float
-{.deprecated: [TMatrix2d: Matrix2d, TPoint2d: Point2d, TVector2d: Vector2d].}
-
-
-# Some forward declarations...
-proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d {.noInit.}
-  ## Creates a new matrix.
-  ## `ax`,`ay` is the local x axis
-  ## `bx`,`by` is the local y axis
-  ## `tx`,`ty` is the translation
-proc vector2d*(x,y:float):Vector2d {.noInit,inline.}
-  ## Returns a new vector (`x`,`y`)
-proc point2d*(x,y:float):Point2d {.noInit,inline.}
-  ## Returns a new point (`x`,`y`)
-
-
-
-let
-  IDMATRIX*:Matrix2d=matrix2d(1.0,0.0,0.0,1.0,0.0,0.0)
-    ## Quick access to an identity matrix
-  ORIGO*:Point2d=point2d(0.0,0.0)
-    ## Quick access to point (0,0)
-  XAXIS*:Vector2d=vector2d(1.0,0.0)
-    ## Quick access to an 2d x-axis unit vector
-  YAXIS*:Vector2d=vector2d(0.0,1.0)
-    ## Quick access to an 2d y-axis unit vector
-
-
-# ***************************************
-#     Private utils
-# ***************************************
-
-proc rtos(val:float):string=
-  return formatFloat(val,ffDefault,0)
-
-proc safeArccos(v:float):float=
-  ## assumes v is in range 0.0-1.0, but clamps
-  ## the value to avoid out of domain errors
-  ## due to rounding issues
-  return arccos(clamp(v,-1.0,1.0))
-
-
-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)=
-  ## 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)
-
-
-# ***************************************
-#     Matrix2d implementation
-# ***************************************
-
-proc setElements*(t:var Matrix2d,ax,ay,bx,by,tx,ty:float) {.inline.}=
-  ## Sets arbitrary elements in an existing matrix.
-  t.ax=ax
-  t.ay=ay
-  t.bx=bx
-  t.by=by
-  t.tx=tx
-  t.ty=ty
-
-proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d =
-  result.setElements(ax,ay,bx,by,tx,ty)
-
-proc `&`*(a,b:Matrix2d):Matrix2d {.noInit.} = #concatenate matrices
-  ## Concatenates matrices returning a new matrix.
-
-  # | a.AX a.AY 0 |   | b.AX b.AY 0 |
-  # | a.BX a.BY 0 | * | b.BX b.BY 0 |
-  # | a.TX a.TY 1 |   | b.TX b.TY 1 |
-  result.setElements(
-    a.ax * b.ax + a.ay * b.bx,
-    a.ax * b.ay + a.ay * b.by,
-    a.bx * b.ax + a.by * b.bx,
-    a.bx * b.ay + a.by * b.by,
-    a.tx * b.ax + a.ty * b.bx + b.tx,
-    a.tx * b.ay + a.ty * b.by + b.ty)
-
-
-proc scale*(s:float):Matrix2d {.noInit.} =
-  ## Returns a new scale matrix.
-  result.setElements(s,0,0,s,0,0)
-
-proc scale*(s:float,org:Point2d):Matrix2d {.noInit.} =
-  ## Returns a new scale matrix using, `org` as scale origin.
-  result.setElements(s,0,0,s,org.x-s*org.x,org.y-s*org.y)
-
-proc stretch*(sx,sy:float):Matrix2d {.noInit.} =
-  ## Returns new a stretch matrix, which is a
-  ## scale matrix with non uniform scale in x and y.
-  result.setElements(sx,0,0,sy,0,0)
-
-proc stretch*(sx,sy:float,org:Point2d):Matrix2d {.noInit.} =
-  ## Returns a new stretch matrix, which is a
-  ## scale matrix with non uniform scale in x and y.
-  ## `org` is used as stretch origin.
-  result.setElements(sx,0,0,sy,org.x-sx*org.x,org.y-sy*org.y)
-
-proc move*(dx,dy:float):Matrix2d {.noInit.} =
-  ## Returns a new translation matrix.
-  result.setElements(1,0,0,1,dx,dy)
-
-proc move*(v:Vector2d):Matrix2d {.noInit.} =
-  ## Returns a new translation matrix from a vector.
-  result.setElements(1,0,0,1,v.x,v.y)
-
-proc rotate*(rad:float):Matrix2d {.noInit.} =
-  ## Returns a new rotation matrix, which
-  ## represents a rotation by `rad` radians
-  let
-    s=sin(rad)
-    c=cos(rad)
-  result.setElements(c,s,-s,c,0,0)
-
-proc rotate*(rad:float,org:Point2d):Matrix2d {.noInit.} =
-  ## Returns a new rotation matrix, which
-  ## represents a rotation by `rad` radians around
-  ## the origin `org`
-  let
-    s=sin(rad)
-    c=cos(rad)
-  result.setElements(c,s,-s,c,org.x+s*org.y-c*org.x,org.y-c*org.y-s*org.x)
-
-proc mirror*(v:Vector2d):Matrix2d {.noInit.} =
-  ## Returns a new mirror matrix, mirroring
-  ## around the line that passes through origo and
-  ## has the direction of `v`
-  let
-    sqx=v.x*v.x
-    sqy=v.y*v.y
-    nd=1.0/(sqx+sqy) #used to normalize invector
-    xy2=v.x*v.y*2.0*nd
-    sqd=nd*(sqx-sqy)
-
-  if nd==Inf or nd==NegInf:
-    return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity
-
-  result.setElements(
-    sqd,xy2,
-    xy2,-sqd,
-    0.0,0.0)
-
-proc mirror*(org:Point2d,v:Vector2d):Matrix2d {.noInit.} =
-  ## Returns a new mirror matrix, mirroring
-  ## around the line that passes through `org` and
-  ## has the direction of `v`
-  let
-    sqx=v.x*v.x
-    sqy=v.y*v.y
-    nd=1.0/(sqx+sqy) #used to normalize invector
-    xy2=v.x*v.y*2.0*nd
-    sqd=nd*(sqx-sqy)
-
-  if nd==Inf or nd==NegInf:
-    return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity
-
-  result.setElements(
-    sqd,xy2,
-    xy2,-sqd,
-    org.x-org.y*xy2-org.x*sqd,org.y-org.x*xy2+org.y*sqd)
-
-
-
-proc skew*(xskew,yskew:float):Matrix2d {.noInit.} =
-  ## Returns a new skew matrix, which has its
-  ## x axis rotated `xskew` radians from the local x axis, and
-  ## y axis rotated `yskew` radians from the local y axis
-  result.setElements(cos(yskew),sin(yskew),-sin(xskew),cos(xskew),0,0)
-
-
-proc `$`* (t:Matrix2d):string {.noInit.} =
-  ## Returns a string representation of the matrix
-  return rtos(t.ax) & "," & rtos(t.ay) &
-    "," & rtos(t.bx) & "," & rtos(t.by) &
-    "," & rtos(t.tx) & "," & rtos(t.ty)
-
-proc isUniform*(t:Matrix2d,tol=1.0e-6):bool=
-  ## Checks if the transform is uniform, that is
-  ## perpendicular axes of equal length, which means (for example)
-  ## it cannot transform a circle into an ellipse.
-  ## `tol` is used as tolerance for both equal length comparison
-  ## and perp. comparison.
-
-  #dot product=0 means perpendicular coord. system:
-  if abs(t.ax*t.bx+t.ay*t.by)<=tol:
-    #subtract squared lengths of axes to check if uniform scaling:
-    if abs((t.ax*t.ax+t.ay*t.ay)-(t.bx*t.bx+t.by*t.by))<=tol:
-      return true
-  return false
-
-proc determinant*(t:Matrix2d):float=
-  ## Computes the determinant of the matrix.
-
-  #NOTE: equivalent with perp.dot product for two 2d vectors
-  return t.ax*t.by-t.bx*t.ay
-
-proc isMirroring* (m:Matrix2d):bool=
-  ## Checks if the `m` is a mirroring matrix,
-  ## which means it will reverse direction of a curve transformed with it
-  return m.determinant<0.0
-
-proc inverse*(m:Matrix2d):Matrix2d {.noInit.} =
-  ## Returns a new matrix, which is the inverse of the matrix
-  ## If the matrix is not invertible (determinant=0), an EDivByZero
-  ## will be raised.
-  let d=m.determinant
-  if d==0.0:
-    raise newException(DivByZeroError,"Cannot invert a zero determinant matrix")
-
-  result.setElements(
-    m.by/d,-m.ay/d,
-    -m.bx/d,m.ax/d,
-    (m.bx*m.ty-m.by*m.tx)/d,
-    (m.ay*m.tx-m.ax*m.ty)/d)
-
-proc equals*(m1:Matrix2d,m2:Matrix2d,tol=1.0e-6):bool=
-  ## Checks if all elements of `m1`and `m2` is equal within
-  ## a given tolerance `tol`.
-  return
-    abs(m1.ax-m2.ax)<=tol and
-    abs(m1.ay-m2.ay)<=tol and
-    abs(m1.bx-m2.bx)<=tol and
-    abs(m1.by-m2.by)<=tol and
-    abs(m1.tx-m2.tx)<=tol and
-    abs(m1.ty-m2.ty)<=tol
-
-proc `=~`*(m1,m2:Matrix2d):bool=
-  ## Checks if `m1`and `m2` is approximately equal, using a
-  ## tolerance of 1e-6.
-  equals(m1,m2)
-
-proc isIdentity*(m:Matrix2d,tol=1.0e-6):bool=
-  ## Checks is a matrix is approximately an identity matrix,
-  ## using `tol` as tolerance for each element.
-  return equals(m,IDMATRIX,tol)
-
-proc apply*(m:Matrix2d,x,y:var float,translate=false)=
-  ## Applies transformation `m` onto `x`,`y`, optionally
-  ## using the translation part of the matrix.
-  if translate: # positional style transform
-    let newx=x*m.ax+y*m.bx+m.tx
-    y=x*m.ay+y*m.by+m.ty
-    x=newx
-  else: # delta style transform
-    let newx=x*m.ax+y*m.bx
-    y=x*m.ay+y*m.by
-    x=newx
-
-
-
-# ***************************************
-#     Vector2d implementation
-# ***************************************
-proc vector2d*(x,y:float):Vector2d = #forward decl.
-  result.x=x
-  result.y=y
-
-proc polarVector2d*(ang:float,len:float):Vector2d {.noInit.} =
-  ## Returns a new vector with angle `ang` and magnitude `len`
-  result.x=cos(ang)*len
-  result.y=sin(ang)*len
-
-proc slopeVector2d*(slope:float,len:float):Vector2d {.noInit.} =
-  ## Returns a new vector having slope (dy/dx) given by
-  ## `slope`, and a magnitude of `len`
-  let ang=arctan(slope)
-  result.x=cos(ang)*len
-  result.y=sin(ang)*len
-
-proc len*(v:Vector2d):float {.inline.}=
-  ## Returns the length of the vector.
-  sqrt(v.x*v.x+v.y*v.y)
-
-proc `len=`*(v:var Vector2d,newlen:float) {.noInit.} =
-  ## Sets the length of the vector, keeping its angle.
-  let fac=newlen/v.len
-
-  if newlen==0.0:
-    v.x=0.0
-    v.y=0.0
-    return
-
-  if fac==Inf or fac==NegInf:
-    #to short for float accuracy
-    #do as good as possible:
-    v.x=newlen
-    v.y=0.0
-  else:
-    v.x*=fac
-    v.y*=fac
-
-proc sqrLen*(v:Vector2d):float {.inline.}=
-  ## Computes the squared length of the vector, which is
-  ## faster than computing the absolute length.
-  v.x*v.x+v.y*v.y
-
-proc angle*(v:Vector2d):float=
-  ## Returns the angle of the vector.
-  ## (The counter clockwise plane angle between posetive x axis and `v`)
-  result=arctan2(v.y,v.x)
-  if result<0.0: result+=DEG360
-
-proc `$` *(v:Vector2d):string=
-  ## String representation of `v`
-  result=rtos(v.x)
-  result.add(",")
-  result.add(rtos(v.y))
-
-
-proc `&` *(v:Vector2d,m:Matrix2d):Vector2d {.noInit.} =
-  ## Concatenate vector `v` with a transformation matrix.
-  ## Transforming a vector ignores the translational part
-  ## of the matrix.
-
-  #             | AX AY 0 |
-  # | X Y 1 | * | BX BY 0 |
-  #             | 0  0  1 |
-  result.x=v.x*m.ax+v.y*m.bx
-  result.y=v.x*m.ay+v.y*m.by
-
-
-proc `&=`*(v:var Vector2d,m:Matrix2d) {.inline.}=
-  ## Applies transformation `m` onto `v` in place.
-  ## Transforming a vector ignores the translational part
-  ## of the matrix.
-
-  #             | AX AY 0 |
-  # | X Y 1 | * | BX BY 0 |
-  #             | 0  0  1 |
-  let newx=v.x*m.ax+v.y*m.bx
-  v.y=v.x*m.ay+v.y*m.by
-  v.x=newx
-
-
-proc tryNormalize*(v:var Vector2d):bool=
-  ## Modifies `v` to have a length of 1.0, keeping its angle.
-  ## If `v` has zero length (and thus no angle), it is left unmodified and
-  ## false is returned, otherwise true is returned.
-
-  let mag=v.len
-
-  if mag==0.0:
-    return false
-
-  v.x/=mag
-  v.y/=mag
-  return true
-
-
-proc normalize*(v:var Vector2d) {.inline.}=
-  ## Modifies `v` to have a length of 1.0, keeping its angle.
-  ## If  `v` has zero length, an EDivByZero will be raised.
-  if not tryNormalize(v):
-    raise newException(DivByZeroError,"Cannot normalize zero length vector")
-
-proc transformNorm*(v:var Vector2d,t:Matrix2d)=
-  ## Applies a normal direction transformation `t` onto `v` in place.
-  ## The resulting vector is *not* normalized.  Transforming a vector ignores the
-  ## translational part of the matrix. If the matrix is not invertible
-  ## (determinant=0), an EDivByZero will be raised.
-
-  # transforming a normal is done by transforming
-  # by the transpose of the inverse of the original matrix
-  # this can be heavily optimized by precompute and inline
-  #             | | AX AY 0 | ^-1| ^T
-  # | X Y 1 | * | | BX BY 0 |    |
-  #             | | 0  0  1 |    |
-  let d=t.determinant
-  if(d==0.0):
-    raise newException(DivByZeroError,"Matrix is not invertible")
-  let newx = (t.by*v.x-t.ay*v.y)/d
-  v.y = (t.ax*v.y-t.bx*v.x)/d
-  v.x = newx
-
-proc transformInv*(v:var Vector2d,t:Matrix2d)=
-  ## Applies inverse of a transformation `t` to `v` in place.
-  ## This is faster than creating an inverse matrix and apply() it.
-  ## Transforming a vector ignores the translational part
-  ## of the matrix. If the matrix is not invertible (determinant=0), an EDivByZero
-  ## will be raised.
-  let d=t.determinant
-
-  if(d==0.0):
-    raise newException(DivByZeroError,"Matrix is not invertible")
-
-  let newx=(t.by*v.x-t.bx*v.y)/d
-  v.y = (t.ax*v.y-t.ay*v.x)/d
-  v.x = newx
-
-proc transformNormInv*(v:var Vector2d,t:Matrix2d)=
-  ## Applies an inverse normal direction transformation `t` onto `v` in place.
-  ## This is faster than creating an inverse
-  ## matrix and transformNorm(...) it. Transforming a vector ignores the
-  ## translational part of the matrix.
-
-  # normal inverse transform is done by transforming
-  # by the inverse of the transpose of the inverse of the org. matrix
-  # which is equivalent with transforming with the transpose.
-  #             | | | AX AY 0 |^-1|^T|^-1                | AX BX 0 |
-  # | X Y 1 | * | | | BX BY 0 |   |  |    =  | X Y 1 | * | AY BY 0 |
-  #             | | | 0  0  1 |   |  |                   | 0  0  1 |
-  # This can be heavily reduced to:
-  let newx=t.ay*v.y+t.ax*v.x
-  v.y=t.by*v.y+t.bx*v.x
-  v.x=newx
-
-proc rotate90*(v:var Vector2d) {.inline.}=
-  ## Quickly rotates vector `v` 90 degrees counter clockwise,
-  ## without using any trigonometrics.
-  swap(v.x,v.y)
-  v.x= -v.x
-
-proc rotate180*(v:var Vector2d){.inline.}=
-  ## Quickly rotates vector `v` 180 degrees counter clockwise,
-  ## without using any trigonometrics.
-  v.x= -v.x
-  v.y= -v.y
-
-proc rotate270*(v:var Vector2d) {.inline.}=
-  ## Quickly rotates vector `v` 270 degrees counter clockwise,
-  ## without using any trigonometrics.
-  swap(v.x,v.y)
-  v.y= -v.y
-
-proc rotate*(v:var Vector2d,rad:float) =
-  ## Rotates vector `v` `rad` radians in place.
-  let
-    s=sin(rad)
-    c=cos(rad)
-    newx=c*v.x-s*v.y
-  v.y=c*v.y+s*v.x
-  v.x=newx
-
-proc scale*(v:var Vector2d,fac:float){.inline.}=
-  ## Scales vector `v` `rad` radians in place.
-  v.x*=fac
-  v.y*=fac
-
-proc stretch*(v:var Vector2d,facx,facy:float){.inline.}=
-  ## Stretches vector `v` `facx` times horizontally,
-  ## and `facy` times vertically.
-  v.x*=facx
-  v.y*=facy
-
-proc mirror*(v:var Vector2d,mirrvec:Vector2d)=
-  ## Mirrors vector `v` using `mirrvec` as mirror direction.
-  let
-    sqx=mirrvec.x*mirrvec.x
-    sqy=mirrvec.y*mirrvec.y
-    nd=1.0/(sqx+sqy) #used to normalize invector
-    xy2=mirrvec.x*mirrvec.y*2.0*nd
-    sqd=nd*(sqx-sqy)
-
-  if nd==Inf or nd==NegInf:
-    return #mirroring around a zero vector is arbitrary=>keep as is is fastest
-
-  let newx=xy2*v.y+sqd*v.x
-  v.y=v.x*xy2-sqd*v.y
-  v.x=newx
-
-
-proc `-` *(v:Vector2d):Vector2d=
-  ## Negates a vector
-  result.x= -v.x
-  result.y= -v.y
-
-# declare templated binary operators
-makeBinOpVector(`+`)
-makeBinOpVector(`-`)
-makeBinOpVector(`*`)
-makeBinOpVector(`/`)
-makeBinOpAssignVector(`+=`)
-makeBinOpAssignVector(`-=`)
-makeBinOpAssignVector(`*=`)
-makeBinOpAssignVector(`/=`)
-
-
-proc dot*(v1,v2:Vector2d):float=
-  ## Computes the dot product of two vectors.
-  ## Returns 0.0 if the vectors are perpendicular.
-  return v1.x*v2.x+v1.y*v2.y
-
-proc cross*(v1,v2:Vector2d):float=
-  ## Computes the cross product of two vectors, also called
-  ## the 'perpendicular dot product' in 2d. Returns 0.0 if the vectors
-  ## are parallel.
-  return v1.x*v2.y-v1.y*v2.x
-
-proc equals*(v1,v2:Vector2d,tol=1.0e-6):bool=
-  ## Checks if two vectors approximately equals with a tolerance.
-  return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol
-
-proc `=~` *(v1,v2:Vector2d):bool=
-  ## Checks if two vectors approximately equals with a
-  ## hardcoded tolerance 1e-6
-  equals(v1,v2)
-
-proc angleTo*(v1,v2:Vector2d):float=
-  ## Returns the smallest of the two possible angles
-  ## between `v1` and `v2` in radians.
-  var
-    nv1=v1
-    nv2=v2
-  if not nv1.tryNormalize or not nv2.tryNormalize:
-    return 0.0 # zero length vector has zero angle to any other vector
-  return safeArccos(dot(nv1,nv2))
-
-proc angleCCW*(v1,v2:Vector2d):float=
-  ## Returns the counter clockwise plane angle from `v1` to `v2`,
-  ## in range 0 - 2*PI
-  let a=v1.angleTo(v2)
-  if v1.cross(v2)>=0.0:
-    return a
-  return DEG360-a
-
-proc angleCW*(v1,v2:Vector2d):float=
-  ## Returns the clockwise plane angle from `v1` to `v2`,
-  ## in range 0 - 2*PI
-  let a=v1.angleTo(v2)
-  if v1.cross(v2)<=0.0:
-    return a
-  return DEG360-a
-
-proc turnAngle*(v1,v2:Vector2d):float=
-  ## Returns the amount v1 should be rotated (in radians) to equal v2,
-  ## in range -PI to PI
-  let a=v1.angleTo(v2)
-  if v1.cross(v2)<=0.0:
-    return -a
-  return a
-
-proc bisect*(v1,v2:Vector2d):Vector2d {.noInit.}=
-  ## Computes the bisector between v1 and v2 as a normalized vector.
-  ## If one of the input vectors has zero length, a normalized version
-  ## of the other is returned. If both input vectors has zero length,
-  ## an arbitrary normalized vector is returned.
-  var
-    vmag1=v1.len
-    vmag2=v2.len
-
-  # zero length vector equals arbitrary vector, just change to magnitude to one to
-  # avoid zero division
-  if vmag1==0.0:
-    if vmag2==0: #both are zero length return any normalized vector
-      return XAXIS
-    vmag1=1.0
-  if vmag2==0.0: vmag2=1.0
-
-  let
-    x1=v1.x/vmag1
-    y1=v1.y/vmag1
-    x2=v2.x/vmag2
-    y2=v2.y/vmag2
-
-  result.x=(x1 + x2) * 0.5
-  result.y=(y1 + y2) * 0.5
-
-  if not result.tryNormalize():
-    # This can happen if vectors are colinear. In this special case
-    # there are actually two bisectors, we select just
-    # one of them (x1,y1 rotated 90 degrees ccw).
-    result.x = -y1
-    result.y = x1
-
-
-
-# ***************************************
-#     Point2d implementation
-# ***************************************
-
-proc point2d*(x,y:float):Point2d =
-  result.x=x
-  result.y=y
-
-proc sqrDist*(a,b:Point2d):float=
-  ## Computes the squared distance between `a` and `b`
-  let dx=b.x-a.x
-  let dy=b.y-a.y
-  result=dx*dx+dy*dy
-
-proc dist*(a,b:Point2d):float {.inline.}=
-  ## Computes the absolute distance between `a` and `b`
-  result=sqrt(sqrDist(a,b))
-
-proc angle*(a,b:Point2d):float=
-  ## Computes the angle of the vector `b`-`a`
-  let dx=b.x-a.x
-  let dy=b.y-a.y
-  result=arctan2(dy,dx)
-  if result<0:
-    result += DEG360
-
-proc `$` *(p:Point2d):string=
-  ## String representation of `p`
-  result=rtos(p.x)
-  result.add(",")
-  result.add(rtos(p.y))
-
-proc `&`*(p:Point2d,t:Matrix2d):Point2d {.noInit,inline.} =
-  ## Concatenates a point `p` with a transform `t`,
-  ## resulting in a new, transformed point.
-
-  #             | AX AY 0 |
-  # | X Y 1 | * | BX BY 0 |
-  #             | TX TY 1 |
-  result.x=p.x*t.ax+p.y*t.bx+t.tx
-  result.y=p.x*t.ay+p.y*t.by+t.ty
-
-proc `&=` *(p:var Point2d,t:Matrix2d) {.inline.}=
-  ## Applies transformation `t` onto `p` in place.
-  let newx=p.x*t.ax+p.y*t.bx+t.tx
-  p.y=p.x*t.ay+p.y*t.by+t.ty
-  p.x=newx
-
-
-proc transformInv*(p:var Point2d,t:Matrix2d){.inline.}=
-  ## Applies the inverse of transformation `t` onto `p` in place.
-  ## If the matrix is not invertable (determinant=0) , EDivByZero will
-  ## be raised.
-
-  #             | AX AY 0 | ^-1
-  # | X Y 1 | * | BX BY 0 |
-  #             | TX TY 1 |
-  let d=t.determinant
-  if d==0.0:
-    raise newException(DivByZeroError,"Cannot invert a zero determinant matrix")
-  let
-    newx= (t.bx*t.ty-t.by*t.tx+p.x*t.by-p.y*t.bx)/d
-  p.y = -(t.ax*t.ty-t.ay*t.tx+p.x*t.ay-p.y*t.ax)/d
-  p.x=newx
-
-
-proc `+`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
-  ## Adds a vector `v` to a point `p`, resulting
-  ## in a new point.
-  result.x=p.x+v.x
-  result.y=p.y+v.y
-
-proc `+=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
-  ## Adds a vector `v` to a point `p` in place.
-  p.x+=v.x
-  p.y+=v.y
-
-proc `-`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} =
-  ## Subtracts a vector `v` from a point `p`, resulting
-  ## in a new point.
-  result.x=p.x-v.x
-  result.y=p.y-v.y
-
-proc `-`*(p1,p2:Point2d):Vector2d {.noInit,inline.} =
-  ## Subtracts `p2`from `p1` resulting in a difference vector.
-  result.x=p1.x-p2.x
-  result.y=p1.y-p2.y
-
-proc `-=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} =
-  ## Subtracts a vector `v` from a point `p` in place.
-  p.x-=v.x
-  p.y-=v.y
-
-proc equals(p1,p2:Point2d,tol=1.0e-6):bool {.inline.}=
-  ## Checks if two points approximately equals with a tolerance.
-  return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol
-
-proc `=~`*(p1,p2:Point2d):bool {.inline.}=
-  ## Checks if two vectors approximately equals with a
-  ## hardcoded tolerance 1e-6
-  equals(p1,p2)
-
-proc polar*(p:Point2d,ang,dist:float):Point2d {.noInit.} =
-  ## Returns a point with a given angle and distance away from `p`
-  result.x=p.x+cos(ang)*dist
-  result.y=p.y+sin(ang)*dist
-
-proc rotate*(p:var Point2d,rad:float)=
-  ## Rotates a point in place `rad` radians around origo.
-  let
-    c=cos(rad)
-    s=sin(rad)
-    newx=p.x*c-p.y*s
-  p.y=p.y*c+p.x*s
-  p.x=newx
-
-proc rotate*(p:var Point2d,rad:float,org:Point2d)=
-  ## Rotates a point in place `rad` radians using `org` as
-  ## center of rotation.
-  let
-    c=cos(rad)
-    s=sin(rad)
-    newx=(p.x - org.x) * c - (p.y - org.y) * s + org.x
-  p.y=(p.y - org.y) * c + (p.x - org.x) * s + org.y
-  p.x=newx
-
-proc scale*(p:var Point2d,fac:float) {.inline.}=
-  ## Scales a point in place `fac` times with world origo as origin.
-  p.x*=fac
-  p.y*=fac
-
-proc scale*(p:var Point2d,fac:float,org:Point2d){.inline.}=
-  ## Scales the point in place `fac` times with `org` as origin.
-  p.x=(p.x - org.x) * fac + org.x
-  p.y=(p.y - org.y) * fac + org.y
-
-proc stretch*(p:var Point2d,facx,facy:float){.inline.}=
-  ## Scales a point in place non uniformly `facx` and `facy` times with
-  ## world origo as origin.
-  p.x*=facx
-  p.y*=facy
-
-proc stretch*(p:var Point2d,facx,facy:float,org:Point2d){.inline.}=
-  ## Scales the point in place non uniformly `facx` and `facy` times with
-  ## `org` as origin.
-  p.x=(p.x - org.x) * facx + org.x
-  p.y=(p.y - org.y) * facy + org.y
-
-proc move*(p:var Point2d,dx,dy:float){.inline.}=
-  ## Translates a point `dx`, `dy` in place.
-  p.x+=dx
-  p.y+=dy
-
-proc move*(p:var Point2d,v:Vector2d){.inline.}=
-  ## Translates a point with vector `v` in place.
-  p.x+=v.x
-  p.y+=v.y
-
-proc sgnArea*(a,b,c:Point2d):float=
-  ## Computes the signed area of the triangle thru points `a`,`b` and `c`
-  ## result>0.0 for counter clockwise triangle
-  ## result<0.0 for clockwise triangle
-  ## This is commonly used to determinate side of a point with respect to a line.
-  return ((b.x - c.x) * (b.y - a.y)-(b.y - c.y) * (b.x - a.x))*0.5
-
-proc area*(a,b,c:Point2d):float=
-  ## Computes the area of the triangle thru points `a`,`b` and `c`
-  return abs(sgnArea(a,b,c))
-
-proc closestPoint*(p:Point2d,pts:varargs[Point2d]):Point2d=
-  ## Returns a point selected from `pts`, that has the closest
-  ## euclidean distance to `p`
-  assert(pts.len>0) # must have at least one point
-
-  var
-    bestidx=0
-    bestdist=p.sqrDist(pts[0])
-    curdist:float
-
-  for idx in 1..high(pts):
-    curdist=p.sqrDist(pts[idx])
-    if curdist<bestdist:
-      bestidx=idx
-      bestdist=curdist
-
-  result=pts[bestidx]
-
-
-# ***************************************
-#     Misc. math utilities that should
-#     probably be in another module.
-# ***************************************
-proc normAngle*(ang:float):float=
-  ## Returns an angle in radians, that is equal to `ang`,
-  ## but in the range 0 to <2*PI
-  if ang>=0.0 and ang<DEG360:
-    return ang
-
-  return ang mod DEG360
-
-proc degToRad*(deg:float):float {.inline.}=
-  ## converts `deg` degrees to radians
-  deg / RAD2DEGCONST
-
-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
deleted file mode 100644
index e2d2464c0..000000000
--- a/lib/pure/basic3d.nim
+++ /dev/null
@@ -1,1040 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2013 Robert Persson
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-import math
-import strutils
-import times
-
-
-## Basic 3d support with vectors, points, matrices and some basic utilities.
-## Vectors are implemented as direction vectors, ie. when transformed with a matrix
-## the translation part of matrix is ignored. The coordinate system used is
-## right handed, because its compatible with 2d coordinate system (rotation around
-## zaxis equals 2d rotation).
-## Operators `+` , `-` , `*` , `/` , `+=` , `-=` , `*=` and `/=` are implemented
-## for vectors and scalars.
-##
-##
-## Quick start example:
-##
-## .. code-block:: nim
-##
-##   # Create a matrix which first rotates, then scales and at last translates
-##
-##   var m:Matrix3d=rotate(PI,vector3d(1,1,2.5)) & scale(2.0) & move(100.0,200.0,300.0)
-##
-##   # Create a 3d point at (100,150,200) and a vector (5,2,3)
-##
-##   var pt:Point3d=point3d(100.0,150.0,200.0)
-##
-##   var vec:Vector3d=vector3d(5.0,2.0,3.0)
-##
-##
-##   pt &= m # transforms pt in place
-##
-##   var pt2:Point3d=pt & m #concatenates pt with m and returns a new point
-##
-##   var vec2:Vector3d=vec & m #concatenates vec with m and returns a new vector
-
-
-
-type
-  Matrix3d* =object
-    ## Implements a row major 3d matrix, which means
-    ## transformations are applied the order they are concatenated.
-    ## This matrix is stored as an 4x4 matrix:
-    ## [ ax ay az aw ]
-    ## [ bx by bz bw ]
-    ## [ cx cy cz cw ]
-    ## [ tx ty tz tw ]
-    ax*,ay*,az*,aw*,  bx*,by*,bz*,bw*,  cx*,cy*,cz*,cw*,  tx*,ty*,tz*,tw*:float
-  Point3d* = object
-    ## Implements a non-homogeneous 3d point stored as
-    ## an `x` , `y` and `z` coordinate.
-    x*,y*,z*:float
-  Vector3d* = object
-    ## Implements a 3d **direction vector** stored as
-    ## an `x` , `y` and `z` coordinate. Direction vector means,
-    ## that when transforming a vector with a matrix, the translational
-    ## part of the matrix is ignored.
-    x*,y*,z*:float
-{.deprecated: [TMatrix3d: Matrix3d, TPoint3d: Point3d, TVector3d: Vector3d].}
-
-
-# Some forward declarations
-proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):Matrix3d {.noInit.}
-  ## Creates a new 4x4 3d transformation matrix.
-  ## `ax` , `ay` , `az` is the local x axis.
-  ## `bx` , `by` , `bz` is the local y axis.
-  ## `cx` , `cy` , `cz` is the local z axis.
-  ## `tx` , `ty` , `tz` is the translation.
-proc vector3d*(x,y,z:float):Vector3d {.noInit,inline.}
-  ## Returns a new 3d vector (`x`,`y`,`z`)
-proc point3d*(x,y,z:float):Point3d {.noInit,inline.}
-  ## Returns a new 4d point (`x`,`y`,`z`)
-proc tryNormalize*(v:var Vector3d):bool
-  ## Modifies `v` to have a length of 1.0, keeping its angle.
-  ## If `v` has zero length (and thus no angle), it is left unmodified and false is
-  ## returned, otherwise true is returned.
-
-
-
-let
-  IDMATRIX*:Matrix3d=matrix3d(
-    1.0,0.0,0.0,0.0,
-    0.0,1.0,0.0,0.0,
-    0.0,0.0,1.0,0.0,
-    0.0,0.0,0.0,1.0)
-    ## Quick access to a 3d identity matrix
-  ORIGO*:Point3d=point3d(0.0,0.0,0.0)
-    ## Quick access to point (0,0)
-  XAXIS*:Vector3d=vector3d(1.0,0.0,0.0)
-    ## Quick access to an 3d x-axis unit vector
-  YAXIS*:Vector3d=vector3d(0.0,1.0,0.0)
-    ## Quick access to an 3d y-axis unit vector
-  ZAXIS*:Vector3d=vector3d(0.0,0.0,1.0)
-    ## Quick access to an 3d z-axis unit vector
-
-
-
-# ***************************************
-#     Private utils
-# ***************************************
-
-proc rtos(val:float):string=
-  return formatFloat(val,ffDefault,0)
-
-proc safeArccos(v:float):float=
-  ## assumes v is in range 0.0-1.0, but clamps
-  ## the value to avoid out of domain errors
-  ## due to rounding issues
-  return arccos(clamp(v,-1.0,1.0))
-
-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.}  =
-    vector3d(s(a.x,b),s(a.y,b),s(a.z,b))
-  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) =
-  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.} =
-    s(a.x,b); s(a.y,b); s(a.z,b)
-
-
-
-# ***************************************
-#     Matrix3d implementation
-# ***************************************
-
-proc setElements*(t:var Matrix3d,ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float) {.inline.}=
-  ## Sets arbitrary elements in an exisitng matrix.
-  t.ax=ax
-  t.ay=ay
-  t.az=az
-  t.aw=aw
-  t.bx=bx
-  t.by=by
-  t.bz=bz
-  t.bw=bw
-  t.cx=cx
-  t.cy=cy
-  t.cz=cz
-  t.cw=cw
-  t.tx=tx
-  t.ty=ty
-  t.tz=tz
-  t.tw=tw
-
-proc matrix3d*(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw:float):Matrix3d =
-  result.setElements(ax,ay,az,aw,bx,by,bz,bw,cx,cy,cz,cw,tx,ty,tz,tw)
-
-proc `&`*(a,b:Matrix3d):Matrix3d {.noinit.} =
-  ## Concatenates matrices returning a new matrix.
-  result.setElements(
-    a.aw*b.tx+a.az*b.cx+a.ay*b.bx+a.ax*b.ax,
-    a.aw*b.ty+a.az*b.cy+a.ay*b.by+a.ax*b.ay,
-    a.aw*b.tz+a.az*b.cz+a.ay*b.bz+a.ax*b.az,
-    a.aw*b.tw+a.az*b.cw+a.ay*b.bw+a.ax*b.aw,
-
-    a.bw*b.tx+a.bz*b.cx+a.by*b.bx+a.bx*b.ax,
-    a.bw*b.ty+a.bz*b.cy+a.by*b.by+a.bx*b.ay,
-    a.bw*b.tz+a.bz*b.cz+a.by*b.bz+a.bx*b.az,
-    a.bw*b.tw+a.bz*b.cw+a.by*b.bw+a.bx*b.aw,
-
-    a.cw*b.tx+a.cz*b.cx+a.cy*b.bx+a.cx*b.ax,
-    a.cw*b.ty+a.cz*b.cy+a.cy*b.by+a.cx*b.ay,
-    a.cw*b.tz+a.cz*b.cz+a.cy*b.bz+a.cx*b.az,
-    a.cw*b.tw+a.cz*b.cw+a.cy*b.bw+a.cx*b.aw,
-
-    a.tw*b.tx+a.tz*b.cx+a.ty*b.bx+a.tx*b.ax,
-    a.tw*b.ty+a.tz*b.cy+a.ty*b.by+a.tx*b.ay,
-    a.tw*b.tz+a.tz*b.cz+a.ty*b.bz+a.tx*b.az,
-    a.tw*b.tw+a.tz*b.cw+a.ty*b.bw+a.tx*b.aw)
-
-
-proc scale*(s:float):Matrix3d {.noInit.} =
-  ## Returns a new scaling matrix.
-  result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0, 0,0,0,1)
-
-proc scale*(s:float,org:Point3d):Matrix3d {.noInit.} =
-  ## Returns a new scaling matrix using, `org` as scale origin.
-  result.setElements(s,0,0,0, 0,s,0,0, 0,0,s,0,
-    org.x-s*org.x,org.y-s*org.y,org.z-s*org.z,1.0)
-
-proc stretch*(sx,sy,sz:float):Matrix3d {.noInit.} =
-  ## Returns new a stretch matrix, which is a
-  ## scale matrix with non uniform scale in x,y and z.
-  result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, 0,0,0,1)
-
-proc stretch*(sx,sy,sz:float,org:Point3d):Matrix3d {.noInit.} =
-  ## Returns a new stretch matrix, which is a
-  ## scale matrix with non uniform scale in x,y and z.
-  ## `org` is used as stretch origin.
-  result.setElements(sx,0,0,0, 0,sy,0,0, 0,0,sz,0, org.x-sx*org.x,org.y-sy*org.y,org.z-sz*org.z,1)
-
-proc move*(dx,dy,dz:float):Matrix3d {.noInit.} =
-  ## Returns a new translation matrix.
-  result.setElements(1,0,0,0, 0,1,0,0, 0,0,1,0, dx,dy,dz,1)
-
-proc move*(v:Vector3d):Matrix3d {.noInit.} =
-  ## Returns a new translation matrix from a vector.
-  result.setElements(1,0,0,0, 0,1,0,0, 0,0,1,0, v.x,v.y,v.z,1)
-
-
-proc rotate*(angle:float,axis:Vector3d):Matrix3d {.noInit.}=
-  ## Creates a rotation matrix that rotates `angle` radians over
-  ## `axis`, which passes through origo.
-
-  # see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf
-  # for how this is computed
-
-  var normax=axis
-  if not normax.tryNormalize: #simplifies matrix computation below a lot
-    raise newException(DivByZeroError,"Cannot rotate around zero length axis")
-
-  let
-    cs=cos(angle)
-    si=sin(angle)
-    omc=1.0-cs
-    usi=normax.x*si
-    vsi=normax.y*si
-    wsi=normax.z*si
-    u2=normax.x*normax.x
-    v2=normax.y*normax.y
-    w2=normax.z*normax.z
-    uvomc=normax.x*normax.y*omc
-    uwomc=normax.x*normax.z*omc
-    vwomc=normax.y*normax.z*omc
-
-  result.setElements(
-    u2+(1.0-u2)*cs, uvomc+wsi, uwomc-vsi, 0.0,
-    uvomc-wsi, v2+(1.0-v2)*cs, vwomc+usi, 0.0,
-    uwomc+vsi, vwomc-usi, w2+(1.0-w2)*cs, 0.0,
-    0.0,0.0,0.0,1.0)
-
-proc rotate*(angle:float,org:Point3d,axis:Vector3d):Matrix3d {.noInit.}=
-  ## Creates a rotation matrix that rotates `angle` radians over
-  ## `axis`, which passes through `org`.
-
-  # see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf
-  # for how this is computed
-
-  var normax=axis
-  if not normax.tryNormalize: #simplifies matrix computation below a lot
-    raise newException(DivByZeroError,"Cannot rotate around zero length axis")
-
-  let
-    u=normax.x
-    v=normax.y
-    w=normax.z
-    u2=u*u
-    v2=v*v
-    w2=w*w
-    cs=cos(angle)
-    omc=1.0-cs
-    si=sin(angle)
-    a=org.x
-    b=org.y
-    c=org.z
-    usi=u*si
-    vsi=v*si
-    wsi=w*si
-    uvomc=normax.x*normax.y*omc
-    uwomc=normax.x*normax.z*omc
-    vwomc=normax.y*normax.z*omc
-
-  result.setElements(
-    u2+(v2+w2)*cs, uvomc+wsi, uwomc-vsi, 0.0,
-    uvomc-wsi, v2+(u2+w2)*cs, vwomc+usi, 0.0,
-    uwomc+vsi, vwomc-usi, w2+(u2+v2)*cs, 0.0,
-    (a*(v2+w2)-u*(b*v+c*w))*omc+(b*w-c*v)*si,
-    (b*(u2+w2)-v*(a*u+c*w))*omc+(c*u-a*w)*si,
-    (c*(u2+v2)-w*(a*u+b*v))*omc+(a*v-b*u)*si,1.0)
-
-
-proc rotateX*(angle:float):Matrix3d {.noInit.}=
-  ## Creates a matrix that rotates around the x-axis with `angle` radians,
-  ## which is also called a 'roll' matrix.
-  let
-    c=cos(angle)
-    s=sin(angle)
-  result.setElements(
-    1,0,0,0,
-    0,c,s,0,
-    0,-s,c,0,
-    0,0,0,1)
-
-proc rotateY*(angle:float):Matrix3d {.noInit.}=
-  ## Creates a matrix that rotates around the y-axis with `angle` radians,
-  ## which is also called a 'pitch' matrix.
-  let
-    c=cos(angle)
-    s=sin(angle)
-  result.setElements(
-    c,0,-s,0,
-    0,1,0,0,
-    s,0,c,0,
-    0,0,0,1)
-
-proc rotateZ*(angle:float):Matrix3d {.noInit.}=
-  ## Creates a matrix that rotates around the z-axis with `angle` radians,
-  ## which is also called a 'yaw' matrix.
-  let
-    c=cos(angle)
-    s=sin(angle)
-  result.setElements(
-    c,s,0,0,
-    -s,c,0,0,
-    0,0,1,0,
-    0,0,0,1)
-
-proc isUniform*(m:Matrix3d,tol=1.0e-6):bool=
-  ## Checks if the transform is uniform, that is
-  ## perpendicular axes of equal length, which means (for example)
-  ## it cannot transform a sphere into an ellipsoid.
-  ## `tol` is used as tolerance for both equal length comparison
-  ## and perpendicular comparison.
-
-  #dot product=0 means perpendicular coord. system, check xaxis vs yaxis and  xaxis vs zaxis
-  if abs(m.ax*m.bx+m.ay*m.by+m.az*m.bz)<=tol and # x vs y
-    abs(m.ax*m.cx+m.ay*m.cy+m.az*m.cz)<=tol and #x vs z
-    abs(m.bx*m.cx+m.by*m.cy+m.bz*m.cz)<=tol: #y vs z
-
-    #subtract squared lengths of axes to check if uniform scaling:
-    let
-      sqxlen=(m.ax*m.ax+m.ay*m.ay+m.az*m.az)
-      sqylen=(m.bx*m.bx+m.by*m.by+m.bz*m.bz)
-      sqzlen=(m.cx*m.cx+m.cy*m.cy+m.cz*m.cz)
-    if abs(sqxlen-sqylen)<=tol and abs(sqxlen-sqzlen)<=tol:
-      return true
-  return false
-
-
-
-proc mirror*(planeperp:Vector3d):Matrix3d {.noInit.}=
-  ## Creates a matrix that mirrors over the plane that has `planeperp` as normal,
-  ## and passes through origo. `planeperp` does not need to be normalized.
-
-  # https://en.wikipedia.org/wiki/Transformation_matrix
-  var n=planeperp
-  if not n.tryNormalize:
-    raise newException(DivByZeroError,"Cannot mirror over a plane with a zero length normal")
-
-  let
-    a=n.x
-    b=n.y
-    c=n.z
-    ab=a*b
-    ac=a*c
-    bc=b*c
-
-  result.setElements(
-    1-2*a*a , -2*ab,-2*ac,0,
-    -2*ab , 1-2*b*b, -2*bc, 0,
-    -2*ac, -2*bc, 1-2*c*c,0,
-    0,0,0,1)
-
-
-proc mirror*(org:Point3d,planeperp:Vector3d):Matrix3d {.noInit.}=
-  ## Creates a matrix that mirrors over the plane that has `planeperp` as normal,
-  ## and passes through `org`. `planeperp` does not need to be normalized.
-
-  # constructs a mirror M like the simpler mirror matrix constructor
-  # above but premultiplies with the inverse traslation of org
-  # and postmultiplies with the translation of org.
-  # With some fiddling this becomes reasonably simple:
-  var n=planeperp
-  if not n.tryNormalize:
-    raise newException(DivByZeroError,"Cannot mirror over a plane with a zero length normal")
-
-  let
-    a=n.x
-    b=n.y
-    c=n.z
-    ab=a*b
-    ac=a*c
-    bc=b*c
-    aa=a*a
-    bb=b*b
-    cc=c*c
-    tx=org.x
-    ty=org.y
-    tz=org.z
-
-  result.setElements(
-    1-2*aa , -2*ab,-2*ac,0,
-    -2*ab , 1-2*bb, -2*bc, 0,
-    -2*ac, -2*bc, 1-2*cc,0,
-    2*(ac*tz+ab*ty+aa*tx),
-    2*(bc*tz+bb*ty+ab*tx),
-    2*(cc*tz+bc*ty+ac*tx) ,1)
-
-
-proc determinant*(m:Matrix3d):float=
-  ## Computes the determinant of matrix `m`.
-
-  # This computation is gotten from ratsimp(optimize(determinant(m)))
-  # in maxima CAS
-  let
-    O1=m.cx*m.tw-m.cw*m.tx
-    O2=m.cy*m.tw-m.cw*m.ty
-    O3=m.cx*m.ty-m.cy*m.tx
-    O4=m.cz*m.tw-m.cw*m.tz
-    O5=m.cx*m.tz-m.cz*m.tx
-    O6=m.cy*m.tz-m.cz*m.ty
-
-  return (O1*m.ay-O2*m.ax-O3*m.aw)*m.bz+
-    (-O1*m.az+O4*m.ax+O5*m.aw)*m.by+
-    (O2*m.az-O4*m.ay-O6*m.aw)*m.bx+
-    (O3*m.az-O5*m.ay+O6*m.ax)*m.bw
-
-
-proc inverse*(m:Matrix3d):Matrix3d {.noInit.}=
-  ## Computes the inverse of matrix `m`. If the matrix
-  ## determinant is zero, thus not invertible, a EDivByZero
-  ## will be raised.
-
-  # this computation comes from optimize(invert(m)) in maxima CAS
-
-  let
-    det=m.determinant
-    O2=m.cy*m.tw-m.cw*m.ty
-    O3=m.cz*m.tw-m.cw*m.tz
-    O4=m.cy*m.tz-m.cz*m.ty
-    O5=m.by*m.tw-m.bw*m.ty
-    O6=m.bz*m.tw-m.bw*m.tz
-    O7=m.by*m.tz-m.bz*m.ty
-    O8=m.by*m.cw-m.bw*m.cy
-    O9=m.bz*m.cw-m.bw*m.cz
-    O10=m.by*m.cz-m.bz*m.cy
-    O11=m.cx*m.tw-m.cw*m.tx
-    O12=m.cx*m.tz-m.cz*m.tx
-    O13=m.bx*m.tw-m.bw*m.tx
-    O14=m.bx*m.tz-m.bz*m.tx
-    O15=m.bx*m.cw-m.bw*m.cx
-    O16=m.bx*m.cz-m.bz*m.cx
-    O17=m.cx*m.ty-m.cy*m.tx
-    O18=m.bx*m.ty-m.by*m.tx
-    O19=m.bx*m.cy-m.by*m.cx
-
-  if det==0.0:
-    raise newException(DivByZeroError,"Cannot normalize zero length vector")
-
-  result.setElements(
-    (m.bw*O4+m.by*O3-m.bz*O2)/det    , (-m.aw*O4-m.ay*O3+m.az*O2)/det,
-    (m.aw*O7+m.ay*O6-m.az*O5)/det    , (-m.aw*O10-m.ay*O9+m.az*O8)/det,
-    (-m.bw*O12-m.bx*O3+m.bz*O11)/det , (m.aw*O12+m.ax*O3-m.az*O11)/det,
-    (-m.aw*O14-m.ax*O6+m.az*O13)/det , (m.aw*O16+m.ax*O9-m.az*O15)/det,
-    (m.bw*O17+m.bx*O2-m.by*O11)/det  , (-m.aw*O17-m.ax*O2+m.ay*O11)/det,
-    (m.aw*O18+m.ax*O5-m.ay*O13)/det  , (-m.aw*O19-m.ax*O8+m.ay*O15)/det,
-    (-m.bx*O4+m.by*O12-m.bz*O17)/det , (m.ax*O4-m.ay*O12+m.az*O17)/det,
-    (-m.ax*O7+m.ay*O14-m.az*O18)/det , (m.ax*O10-m.ay*O16+m.az*O19)/det)
-
-
-proc equals*(m1:Matrix3d,m2:Matrix3d,tol=1.0e-6):bool=
-  ## Checks if all elements of `m1`and `m2` is equal within
-  ## a given tolerance `tol`.
-  return
-    abs(m1.ax-m2.ax)<=tol and
-    abs(m1.ay-m2.ay)<=tol and
-    abs(m1.az-m2.az)<=tol and
-    abs(m1.aw-m2.aw)<=tol and
-    abs(m1.bx-m2.bx)<=tol and
-    abs(m1.by-m2.by)<=tol and
-    abs(m1.bz-m2.bz)<=tol and
-    abs(m1.bw-m2.bw)<=tol and
-    abs(m1.cx-m2.cx)<=tol and
-    abs(m1.cy-m2.cy)<=tol and
-    abs(m1.cz-m2.cz)<=tol and
-    abs(m1.cw-m2.cw)<=tol and
-    abs(m1.tx-m2.tx)<=tol and
-    abs(m1.ty-m2.ty)<=tol and
-    abs(m1.tz-m2.tz)<=tol and
-    abs(m1.tw-m2.tw)<=tol
-
-proc `=~`*(m1,m2:Matrix3d):bool=
-  ## Checks if `m1` and `m2` is approximately equal, using a
-  ## tolerance of 1e-6.
-  equals(m1,m2)
-
-proc transpose*(m:Matrix3d):Matrix3d {.noInit.}=
-  ## Returns the transpose of `m`
-  result.setElements(m.ax,m.bx,m.cx,m.tx,m.ay,m.by,m.cy,m.ty,m.az,m.bz,m.cz,m.tz,m.aw,m.bw,m.cw,m.tw)
-
-proc getXAxis*(m:Matrix3d):Vector3d {.noInit.}=
-  ## Gets the local x axis of `m`
-  result.x=m.ax
-  result.y=m.ay
-  result.z=m.az
-
-proc getYAxis*(m:Matrix3d):Vector3d {.noInit.}=
-  ## Gets the local y axis of `m`
-  result.x=m.bx
-  result.y=m.by
-  result.z=m.bz
-
-proc getZAxis*(m:Matrix3d):Vector3d {.noInit.}=
-  ## Gets the local y axis of `m`
-  result.x=m.cx
-  result.y=m.cy
-  result.z=m.cz
-
-
-proc `$`*(m:Matrix3d):string=
-  ## String representation of `m`
-  return rtos(m.ax) & "," & rtos(m.ay) & "," & rtos(m.az) & "," & rtos(m.aw) &
-    "\n" & rtos(m.bx) & "," & rtos(m.by) & "," & rtos(m.bz) & "," & rtos(m.bw) &
-    "\n" & rtos(m.cx) & "," & rtos(m.cy) & "," & rtos(m.cz) & "," & rtos(m.cw) &
-    "\n" & rtos(m.tx) & "," & rtos(m.ty) & "," & rtos(m.tz) & "," & rtos(m.tw)
-
-proc apply*(m:Matrix3d, x,y,z:var float, translate=false)=
-  ## Applies transformation `m` onto `x` , `y` , `z` , optionally
-  ## using the translation part of the matrix.
-  let
-    oldx=x
-    oldy=y
-    oldz=z
-
-  x=m.cx*oldz+m.bx*oldy+m.ax*oldx
-  y=m.cy*oldz+m.by*oldy+m.ay*oldx
-  z=m.cz*oldz+m.bz*oldy+m.az*oldx
-
-  if translate:
-    x+=m.tx
-    y+=m.ty
-    z+=m.tz
-
-# ***************************************
-#     Vector3d implementation
-# ***************************************
-proc vector3d*(x,y,z:float):Vector3d=
-  result.x=x
-  result.y=y
-  result.z=z
-
-proc len*(v:Vector3d):float=
-  ## Returns the length of the vector `v`.
-  sqrt(v.x*v.x+v.y*v.y+v.z*v.z)
-
-proc `len=`*(v:var Vector3d,newlen:float) {.noInit.} =
-  ## Sets the length of the vector, keeping its direction.
-  ## If the vector has zero length before changing it's length,
-  ## an arbitrary vector of the requested length is returned.
-
-  let fac=newlen/v.len
-
-  if newlen==0.0:
-    v.x=0.0
-    v.y=0.0
-    v.z=0.0
-    return
-
-  if fac==Inf or fac==NegInf:
-    #to short for float accuracy
-    #do as good as possible:
-    v.x=newlen
-    v.y=0.0
-    v.z=0.0
-  else:
-    v.x*=fac
-    v.y*=fac
-    v.z*=fac
-
-
-proc sqrLen*(v:Vector3d):float {.inline.}=
-  ## Computes the squared length of the vector, which is
-  ## faster than computing the absolute length.
-  return v.x*v.x+v.y*v.y+v.z*v.z
-
-proc `$` *(v:Vector3d):string=
-  ## String representation of `v`
-  result=rtos(v.x)
-  result.add(",")
-  result.add(rtos(v.y))
-  result.add(",")
-  result.add(rtos(v.z))
-
-proc `&` *(v:Vector3d,m:Matrix3d):Vector3d {.noInit.} =
-  ## Concatenate vector `v` with a transformation matrix.
-  ## Transforming a vector ignores the translational part
-  ## of the matrix.
-
-  #               | AX AY AZ AW |
-  # | X Y Z 1 | * | BX BY BZ BW |
-  #               | CX CY CZ CW |
-  #               | 0  0  0  1 |
-  let
-    newx=m.cx*v.z+m.bx*v.y+m.ax*v.x
-    newy=m.cy*v.z+m.by*v.y+m.ay*v.x
-  result.z=m.cz*v.z+m.bz*v.y+m.az*v.x
-  result.y=newy
-  result.x=newx
-
-
-proc `&=` *(v:var Vector3d,m:Matrix3d) {.noInit.} =
-  ## Applies transformation `m` onto `v` in place.
-  ## Transforming a vector ignores the translational part
-  ## of the matrix.
-
-  #               | AX AY AZ AW |
-  # | X Y Z 1 | * | BX BY BZ BW |
-  #               | CX CY CZ CW |
-  #               | 0  0  0  1  |
-
-  let
-    newx=m.cx*v.z+m.bx*v.y+m.ax*v.x
-    newy=m.cy*v.z+m.by*v.y+m.ay*v.x
-  v.z=m.cz*v.z+m.bz*v.y+m.az*v.x
-  v.y=newy
-  v.x=newx
-
-proc transformNorm*(v:var Vector3d,m:Matrix3d)=
-  ## Applies a normal direction transformation `m` onto `v` in place.
-  ## The resulting vector is *not* normalized.  Transforming a vector ignores the
-  ## translational part of the matrix. If the matrix is not invertible
-  ## (determinant=0), an EDivByZero will be raised.
-
-  # transforming a normal is done by transforming
-  # by the transpose of the inverse of the original matrix
-
-  # Major reason this simple function is here is that this function can be optimized in the future,
-  # (possibly by hardware) as well as having a consistent API with the 2d version.
-  v&=transpose(inverse(m))
-
-proc transformInv*(v:var Vector3d,m:Matrix3d)=
-  ## Applies the inverse of `m` on vector `v`. Transforming a vector ignores
-  ## the translational part of the matrix.  Transforming a vector ignores the
-  ## translational part of the matrix.
-  ## If the matrix is not invertible (determinant=0), an EDivByZero
-  ## will be raised.
-
-  # Major reason this simple function is here is that this function can be optimized in the future,
-  # (possibly by hardware) as well as having a consistent API with the 2d version.
-  v&=m.inverse
-
-proc transformNormInv*(vec:var Vector3d,m:Matrix3d)=
-  ## Applies an inverse normal direction transformation `m` onto `v` in place.
-  ## This is faster than creating an inverse
-  ## matrix and transformNorm(...) it. Transforming a vector ignores the
-  ## translational part of the matrix.
-
-  # see vector2d:s equivalent for a deeper look how/why this works
-  vec&=m.transpose
-
-proc tryNormalize*(v:var Vector3d):bool=
-  ## Modifies `v` to have a length of 1.0, keeping its angle.
-  ## If `v` has zero length (and thus no angle), it is left unmodified and false is
-  ## returned, otherwise true is returned.
-  let mag=v.len
-
-  if mag==0.0:
-    return false
-
-  v.x/=mag
-  v.y/=mag
-  v.z/=mag
-
-  return true
-
-proc normalize*(v:var Vector3d) {.inline.}=
-  ## Modifies `v` to have a length of 1.0, keeping its angle.
-  ## If  `v` has zero length, an EDivByZero will be raised.
-  if not tryNormalize(v):
-    raise newException(DivByZeroError,"Cannot normalize zero length vector")
-
-proc rotate*(vec:var Vector3d,angle:float,axis:Vector3d)=
-  ## Rotates `vec` in place, with `angle` radians over `axis`, which passes
-  ## through origo.
-
-  # see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf
-  # for how this is computed
-
-  var normax=axis
-  if not normax.tryNormalize:
-    raise newException(DivByZeroError,"Cannot rotate around zero length axis")
-
-  let
-    cs=cos(angle)
-    si=sin(angle)
-    omc=1.0-cs
-    u=normax.x
-    v=normax.y
-    w=normax.z
-    x=vec.x
-    y=vec.y
-    z=vec.z
-    uxyzomc=(u*x+v*y+w*z)*omc
-
-  vec.x=u*uxyzomc+x*cs+(v*z-w*y)*si
-  vec.y=v*uxyzomc+y*cs+(w*x-u*z)*si
-  vec.z=w*uxyzomc+z*cs+(u*y-v*x)*si
-
-proc scale*(v:var Vector3d,s:float)=
-  ## Scales the vector in place with factor `s`
-  v.x*=s
-  v.y*=s
-  v.z*=s
-
-proc stretch*(v:var Vector3d,sx,sy,sz:float)=
-  ## Scales the vector non uniformly with factors `sx` , `sy` , `sz`
-  v.x*=sx
-  v.y*=sy
-  v.z*=sz
-
-proc mirror*(v:var Vector3d,planeperp:Vector3d)=
-  ## Computes the mirrored vector of `v` over the plane
-  ## that has `planeperp` as normal direction.
-  ## `planeperp` does not need to be normalized.
-
-  var n=planeperp
-  n.normalize
-
-  let
-    x=v.x
-    y=v.y
-    z=v.z
-    a=n.x
-    b=n.y
-    c=n.z
-    ac=a*c
-    ab=a*b
-    bc=b*c
-
-  v.x= -2*(ac*z+ab*y+a*a*x)+x
-  v.y= -2*(bc*z+b*b*y+ab*x)+y
-  v.z= -2*(c*c*z+bc*y+ac*x)+z
-
-
-proc `-` *(v:Vector3d):Vector3d=
-  ## Negates a vector
-  result.x= -v.x
-  result.y= -v.y
-  result.z= -v.z
-
-# declare templated binary operators
-makeBinOpVector(`+`)
-makeBinOpVector(`-`)
-makeBinOpVector(`*`)
-makeBinOpVector(`/`)
-makeBinOpAssignVector(`+=`)
-makeBinOpAssignVector(`-=`)
-makeBinOpAssignVector(`*=`)
-makeBinOpAssignVector(`/=`)
-
-proc dot*(v1,v2:Vector3d):float {.inline.}=
-  ## Computes the dot product of two vectors.
-  ## Returns 0.0 if the vectors are perpendicular.
-  return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z
-
-proc cross*(v1,v2:Vector3d):Vector3d {.inline.}=
-  ## Computes the cross product of two vectors.
-  ## The result is a vector which is perpendicular
-  ## to the plane of `v1` and `v2`, which means
-  ## cross(xaxis,yaxis)=zaxis. The magnitude of the result is
-  ## zero if the vectors are colinear.
-  result.x = (v1.y * v2.z) - (v2.y * v1.z)
-  result.y = (v1.z * v2.x) - (v2.z * v1.x)
-  result.z = (v1.x * v2.y) - (v2.x * v1.y)
-
-proc equals*(v1,v2:Vector3d,tol=1.0e-6):bool=
-  ## Checks if two vectors approximately equals with a tolerance.
-  return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol and abs(v2.z-v1.z)<=tol
-
-proc `=~` *(v1,v2:Vector3d):bool=
-  ## Checks if two vectors approximately equals with a
-  ## hardcoded tolerance 1e-6
-  equals(v1,v2)
-
-proc angleTo*(v1,v2:Vector3d):float=
-  ## Returns the smallest angle between v1 and v2,
-  ## which is in range 0-PI
-  var
-    nv1=v1
-    nv2=v2
-  if not nv1.tryNormalize or not nv2.tryNormalize:
-    return 0.0 # zero length vector has zero angle to any other vector
-  return safeArccos(dot(nv1,nv2))
-
-proc arbitraryAxis*(norm:Vector3d):Matrix3d {.noInit.}=
-  ## Computes the rotation matrix that would transform
-  ## world z vector into `norm`. The inverse of this matrix
-  ## is useful to transform a planar 3d object to 2d space.
-  ## This is the same algorithm used to interpret DXF and DWG files.
-  const lim=1.0/64.0
-  var ax,ay,az:Vector3d
-  if abs(norm.x)<lim and abs(norm.y)<lim:
-    ax=cross(YAXIS,norm)
-  else:
-    ax=cross(ZAXIS,norm)
-
-  ax.normalize()
-  ay=cross(norm,ax)
-  ay.normalize()
-  az=cross(ax,ay)
-
-  result.setElements(
-    ax.x,ax.y,ax.z,0.0,
-    ay.x,ay.y,ay.z,0.0,
-    az.x,az.y,az.z,0.0,
-    0.0,0.0,0.0,1.0)
-
-proc bisect*(v1,v2:Vector3d):Vector3d {.noInit.}=
-  ## Computes the bisector between v1 and v2 as a normalized vector.
-  ## If one of the input vectors has zero length, a normalized version
-  ## of the other is returned. If both input vectors has zero length,
-  ## an arbitrary normalized vector `v1` is returned.
-  var
-    vmag1=v1.len
-    vmag2=v2.len
-
-  # zero length vector equals arbitrary vector, just change
-  # magnitude to one to avoid zero division
-  if vmag1==0.0:
-    if vmag2==0: #both are zero length return any normalized vector
-      return XAXIS
-    vmag1=1.0
-  if vmag2==0.0: vmag2=1.0
-
-  let
-    x1=v1.x/vmag1
-    y1=v1.y/vmag1
-    z1=v1.z/vmag1
-    x2=v2.x/vmag2
-    y2=v2.y/vmag2
-    z2=v2.z/vmag2
-
-  result.x=(x1 + x2) * 0.5
-  result.y=(y1 + y2) * 0.5
-  result.z=(z1 + z2) * 0.5
-
-  if not result.tryNormalize():
-    # This can happen if vectors are colinear. In this special case
-    # there are actually inifinitely many bisectors, we select just
-    # one of them.
-    result=v1.cross(XAXIS)
-    if result.sqrLen<1.0e-9:
-      result=v1.cross(YAXIS)
-      if result.sqrLen<1.0e-9:
-        result=v1.cross(ZAXIS) # now we should be guaranteed to have succeeded
-    result.normalize
-
-
-
-# ***************************************
-#     Point3d implementation
-# ***************************************
-proc point3d*(x,y,z:float):Point3d=
-  result.x=x
-  result.y=y
-  result.z=z
-
-proc sqrDist*(a,b:Point3d):float=
-  ## Computes the squared distance between `a`and `b`
-  let dx=b.x-a.x
-  let dy=b.y-a.y
-  let dz=b.z-a.z
-  result=dx*dx+dy*dy+dz*dz
-
-proc dist*(a,b:Point3d):float {.inline.}=
-  ## Computes the absolute distance between `a`and `b`
-  result=sqrt(sqrDist(a,b))
-
-proc `$` *(p:Point3d):string=
-  ## String representation of `p`
-  result=rtos(p.x)
-  result.add(",")
-  result.add(rtos(p.y))
-  result.add(",")
-  result.add(rtos(p.z))
-
-proc `&`*(p:Point3d,m:Matrix3d):Point3d=
-  ## Concatenates a point `p` with a transform `m`,
-  ## resulting in a new, transformed point.
-  result.z=m.cz*p.z+m.bz*p.y+m.az*p.x+m.tz
-  result.y=m.cy*p.z+m.by*p.y+m.ay*p.x+m.ty
-  result.x=m.cx*p.z+m.bx*p.y+m.ax*p.x+m.tx
-
-proc `&=` *(p:var Point3d,m:Matrix3d)=
-  ## Applies transformation `m` onto `p` in place.
-  let
-    x=p.x
-    y=p.y
-    z=p.z
-  p.x=m.cx*z+m.bx*y+m.ax*x+m.tx
-  p.y=m.cy*z+m.by*y+m.ay*x+m.ty
-  p.z=m.cz*z+m.bz*y+m.az*x+m.tz
-
-proc transformInv*(p:var Point3d,m:Matrix3d)=
-  ## Applies the inverse of transformation `m` onto `p` in place.
-  ## If the matrix is not invertable (determinant=0) , EDivByZero will
-  ## be raised.
-
-  # can possibly be more optimized in the future so use this function when possible
-  p&=inverse(m)
-
-
-proc `+`*(p:Point3d,v:Vector3d):Point3d {.noInit,inline.} =
-  ## Adds a vector `v` to a point `p`, resulting
-  ## in a new point.
-  result.x=p.x+v.x
-  result.y=p.y+v.y
-  result.z=p.z+v.z
-
-proc `+=`*(p:var Point3d,v:Vector3d) {.noInit,inline.} =
-  ## Adds a vector `v` to a point `p` in place.
-  p.x+=v.x
-  p.y+=v.y
-  p.z+=v.z
-
-proc `-`*(p:Point3d,v:Vector3d):Point3d {.noInit,inline.} =
-  ## Subtracts a vector `v` from a point `p`, resulting
-  ## in a new point.
-  result.x=p.x-v.x
-  result.y=p.y-v.y
-  result.z=p.z-v.z
-
-proc `-`*(p1,p2:Point3d):Vector3d {.noInit,inline.} =
-  ## Subtracts `p2`from `p1` resulting in a difference vector.
-  result.x=p1.x-p2.x
-  result.y=p1.y-p2.y
-  result.z=p1.z-p2.z
-
-proc `-=`*(p:var Point3d,v:Vector3d) {.noInit,inline.} =
-  ## Subtracts a vector `v` from a point `p` in place.
-  p.x-=v.x
-  p.y-=v.y
-  p.z-=v.z
-
-proc equals(p1,p2:Point3d,tol=1.0e-6):bool {.inline.}=
-  ## Checks if two points approximately equals with a tolerance.
-  return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol and abs(p2.z-p1.z)<=tol
-
-proc `=~`*(p1,p2:Point3d):bool {.inline.}=
-  ## Checks if two vectors approximately equals with a
-  ## hardcoded tolerance 1e-6
-  equals(p1,p2)
-
-proc rotate*(p:var Point3d,rad:float,axis:Vector3d)=
-  ## Rotates point `p` in place `rad` radians about an axis
-  ## passing through origo.
-
-  var v=vector3d(p.x,p.y,p.z)
-  v.rotate(rad,axis) # reuse this code here since doing the same thing and quite complicated
-  p.x=v.x
-  p.y=v.y
-  p.z=v.z
-
-proc rotate*(p:var Point3d,angle:float,org:Point3d,axis:Vector3d)=
-  ## Rotates point `p` in place `rad` radians about an axis
-  ## passing through `org`
-
-  # see PDF document http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.pdf
-  # for how this is computed
-
-  var normax=axis
-  normax.normalize
-
-  let
-    cs=cos(angle)
-    omc=1.0-cs
-    si=sin(angle)
-    u=normax.x
-    v=normax.y
-    w=normax.z
-    a=org.x
-    b=org.y
-    c=org.z
-    x=p.x
-    y=p.y
-    z=p.z
-    uu=u*u
-    vv=v*v
-    ww=w*w
-    ux=u*p.x
-    vy=v*p.y
-    wz=w*p.z
-    au=a*u
-    bv=b*v
-    cw=c*w
-    uxmvymwz=ux-vy-wz
-
-  p.x=(a*(vv+ww)-u*(bv+cw-uxmvymwz))*omc + x*cs + (b*w+v*z-c*v-w*y)*si
-  p.y=(b*(uu+ww)-v*(au+cw-uxmvymwz))*omc + y*cs + (c*u-a*w+w*x-u*z)*si
-  p.z=(c*(uu+vv)-w*(au+bv-uxmvymwz))*omc + z*cs + (a*v+u*y-b*u-v*x)*si
-
-proc scale*(p:var Point3d,fac:float) {.inline.}=
-  ## Scales a point in place `fac` times with world origo as origin.
-  p.x*=fac
-  p.y*=fac
-  p.z*=fac
-
-proc scale*(p:var Point3d,fac:float,org:Point3d){.inline.}=
-  ## Scales the point in place `fac` times with `org` as origin.
-  p.x=(p.x - org.x) * fac + org.x
-  p.y=(p.y - org.y) * fac + org.y
-  p.z=(p.z - org.z) * fac + org.z
-
-proc stretch*(p:var Point3d,facx,facy,facz:float){.inline.}=
-  ## Scales a point in place non uniformly `facx` , `facy` , `facz` times
-  ## with world origo as origin.
-  p.x*=facx
-  p.y*=facy
-  p.z*=facz
-
-proc stretch*(p:var Point3d,facx,facy,facz:float,org:Point3d){.inline.}=
-  ## Scales the point in place non uniformly `facx` , `facy` , `facz` times
-  ## with `org` as origin.
-  p.x=(p.x - org.x) * facx + org.x
-  p.y=(p.y - org.y) * facy + org.y
-  p.z=(p.z - org.z) * facz + org.z
-
-
-proc move*(p:var Point3d,dx,dy,dz:float){.inline.}=
-  ## Translates a point `dx` , `dy` , `dz` in place.
-  p.x+=dx
-  p.y+=dy
-  p.z+=dz
-
-proc move*(p:var Point3d,v:Vector3d){.inline.}=
-  ## Translates a point with vector `v` in place.
-  p.x+=v.x
-  p.y+=v.y
-  p.z+=v.z
-
-proc area*(a,b,c:Point3d):float {.inline.}=
-  ## Computes the area of the triangle thru points `a` , `b` and `c`
-
-  # The area of a planar 3d quadliteral is the magnitude of the cross
-  # product of two edge vectors. Taking this time 0.5 gives the triangle area.
-  return cross(b-a,c-a).len*0.5
-
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index d51a5c388..dbdf17514 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -287,8 +287,6 @@ proc exclImpl[A](s: var HashSet[A], key: A) : bool {. inline .} =
 
   if i >= 0:
     result = false
-    s.data[i].hcode = 0
-    s.data[i].key = default(type(s.data[i].key))
     dec(s.counter)
     while true:         # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
       var j = i         # The correctness of this depends on (h+1) in nextTry,
@@ -300,7 +298,7 @@ proc exclImpl[A](s: var HashSet[A], key: A) : bool {. inline .} =
         if isEmpty(s.data[i].hcode):   # end of collision cluster; So all done
           return
         r = s.data[i].hcode and msk    # "home" location of key@i
-      shallowCopy(s.data[j], s.data[i]) # data[j] will be marked EMPTY next loop
+      shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop
 
 proc missingOrExcl*[A](s: var HashSet[A], key: A): bool =
   ## Excludes `key` in the set `s` and tells if `key` was removed from `s`.
@@ -662,9 +660,12 @@ proc card*[A](s: OrderedSet[A]): int {.inline.} =
 
 template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} =
   var h = s.first
+  var idx = 0
   while h >= 0:
     var nxt = s.data[h].next
-    if isFilled(s.data[h].hcode): yieldStmt
+    if isFilled(s.data[h].hcode):
+      yieldStmt
+      inc(idx)
     h = nxt
 
 iterator items*[A](s: OrderedSet[A]): A =
@@ -689,6 +690,11 @@ iterator items*[A](s: OrderedSet[A]): A =
   forAllOrderedPairs:
     yield s.data[h].key
 
+iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] =
+  assert s.isValid, "The set needs to be initialized"
+  forAllOrderedPairs:
+    yield (idx, s.data[h].key)
+
 proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: Hash): int {.inline.} =
   rawGetKnownHCImpl()
 
@@ -760,6 +766,67 @@ proc incl*[A](s: var HashSet[A], other: OrderedSet[A]) =
   assert other.isValid, "The set `other` needs to be initialized."
   for item in other: incl(s, item)
 
+proc exclImpl[A](s: var OrderedSet[A], key: A) : bool {. inline .} =
+  assert s.isValid, "The set needs to be initialized."
+  var hc: Hash
+  var i = rawGet(s, key, hc)
+  var msk = high(s.data)
+  result = true
+
+  if i >= 0:
+    result = false
+    # Fix ordering
+    if s.first == i:
+      s.first = s.data[i].next
+    else:
+      var itr = s.first
+      while true:
+        if (s.data[itr].next == i):
+          s.data[itr].next = s.data[i].next
+          if s.last == i:
+            s.last = itr
+          break
+        itr = s.data[itr].next
+
+    dec(s.counter)
+    while true:         # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
+      var j = i         # The correctness of this depends on (h+1) in nextTry,
+      var r = j         # though may be adaptable to other simple sequences.
+      s.data[i].hcode = 0              # mark current EMPTY
+      s.data[i].key = default(type(s.data[i].key))
+      s.data[i].next = 0
+      doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
+        i = (i + 1) and msk            # increment mod table size
+        if isEmpty(s.data[i].hcode):   # end of collision cluster; So all done
+          return
+        r = s.data[i].hcode and msk    # "home" location of key@i
+      shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop
+
+proc missingOrExcl*[A](s: var OrderedSet[A], key: A): bool =
+  ## Excludes `key` in the set `s` and tells if `key` was removed from `s`. Efficiency: O(n).
+  ##
+  ## The difference with regards to the `excl() <#excl,TOrderedSet[A],A>`_ proc is
+  ## that this proc returns `true` if `key` was not present in `s`. Example:
+  ##
+  ## .. code-block::
+  ##  var s = toOrderedSet([2, 3, 6, 7])
+  ##  assert s.missingOrExcl(4) == true
+  ##  assert s.missingOrExcl(6) == false
+  exclImpl(s, key)
+
+
+proc excl*[A](s: var OrderedSet[A], key: A) =
+  ## Excludes `key` from the set `s`. Efficiency: O(n).
+  ##
+  ## This doesn't do anything if `key` is not found in `s`. Example:
+  ##
+  ## .. code-block::
+  ##   var s = toOrderedSet([2, 3, 6, 7])
+  ##   s.excl(2)
+  ##   s.excl(2)
+  ##   assert s.len == 3
+  discard exclImpl(s, key)
+
 proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool =
   ## Includes `key` in the set `s` and tells if `key` was added to `s`.
   ##
@@ -986,6 +1053,24 @@ when isMainModule and not defined(release):
       assert a.len == b.card
       assert a.len == 2
 
+    block setPairsIterator:
+      var s = toOrderedSet([1, 3, 5, 7])
+      var items = newSeq[tuple[a: int, b: int]]()
+      for idx, item in s: items.add((idx, item))
+      assert items == @[(0, 1), (1, 3), (2, 5), (3, 7)]
+
+    block exclusions:
+      var s = toOrderedSet([1, 2, 3, 6, 7, 4])
+
+      s.excl(3)
+      s.excl(3)
+      s.excl(1)
+      s.excl(4)
+
+      var items = newSeq[int]()
+      for item in s: items.add item
+      assert items == @[2, 6, 7]
+
     #block orderedSetIterator:
     #  var a = initOrderedSet[int]()
     #  for value in [9, 2, 1, 5, 1, 8, 4, 2]:
@@ -1030,6 +1115,11 @@ when isMainModule and not defined(release):
       if s <= i or mustRehash(s, i):
         echo "performance issue: rightSize() will not elide enlarge() at ", i
 
+    block missingOrExcl:
+      var s = toOrderedSet([2, 3, 6, 7])
+      assert s.missingOrExcl(4) == true
+      assert s.missingOrExcl(6) == false
+
     when not defined(testing):
       echo "Micro tests run successfully."
 
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index 097952588..656114fb1 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -721,6 +721,16 @@ proc getElems*(n: JsonNode, default: seq[JsonNode] = @[]): seq[JsonNode] =
   if n.isNil or n.kind != JArray: return default
   else: return n.elems
 
+proc add*(father, child: JsonNode) =
+  ## Adds `child` to a JArray node `father`.
+  assert father.kind == JArray
+  father.elems.add(child)
+
+proc add*(obj: JsonNode, key: string, val: JsonNode) =
+  ## Sets a field from a `JObject`.
+  assert obj.kind == JObject
+  obj.fields[key] = val
+
 proc `%`*(s: string): JsonNode =
   ## Generic constructor for JSON data. Creates a new `JString JsonNode`.
   new(result)
@@ -759,6 +769,19 @@ proc `%`*[T](elements: openArray[T]): JsonNode =
   result = newJArray()
   for elem in elements: result.add(%elem)
 
+when false:
+  # For 'consistency' we could do this, but that only pushes people further
+  # into that evil comfort zone where they can use Nim without understanding it
+  # causing problems later on.
+  proc `%`*(elements: set[bool]): JsonNode =
+    ## Generic constructor for JSON data. Creates a new `JObject JsonNode`.
+    ## This can only be used with the empty set ``{}`` and is supported
+    ## to prevent the gotcha ``%*{}`` which used to produce an empty
+    ## JSON array.
+    result = newJObject()
+    assert false notin elements, "usage error: only empty sets allowed"
+    assert true notin elements, "usage error: only empty sets allowed"
+
 proc `%`*(o: object): JsonNode =
   ## Generic constructor for JSON data. Creates a new `JObject JsonNode`
   result = newJObject()
@@ -779,27 +802,25 @@ proc `%`*(o: enum): JsonNode =
 proc toJson(x: NimNode): NimNode {.compiletime.} =
   case x.kind
   of nnkBracket: # array
+    if x.len == 0: return newCall(bindSym"newJArray")
     result = newNimNode(nnkBracket)
     for i in 0 .. <x.len:
       result.add(toJson(x[i]))
-
+    result = newCall(bindSym"%", result)
   of nnkTableConstr: # object
+    if x.len == 0: return newCall(bindSym"newJObject")
     result = newNimNode(nnkTableConstr)
     for i in 0 .. <x.len:
       x[i].expectKind nnkExprColonExpr
-      result.add(newNimNode(nnkExprColonExpr).add(x[i][0]).add(toJson(x[i][1])))
-
+      result.add newTree(nnkExprColonExpr, x[i][0], toJson(x[i][1]))
+    result = newCall(bindSym"%", result)
   of nnkCurly: # empty object
-    result = newNimNode(nnkTableConstr)
     x.expectLen(0)
-
+    result = newCall(bindSym"newJObject")
   of nnkNilLit:
-    result = newCall("newJNull")
-
+    result = newCall(bindSym"newJNull")
   else:
-    result = x
-
-  result = prefix(result, "%")
+    result = newCall(bindSym"%", x)
 
 macro `%*`*(x: untyped): untyped =
   ## Convert an expression to a JsonNode directly, without having to specify
@@ -909,16 +930,6 @@ proc contains*(node: JsonNode, val: JsonNode): bool =
 proc existsKey*(node: JsonNode, key: string): bool {.deprecated.} = node.hasKey(key)
   ## Deprecated for `hasKey`
 
-proc add*(father, child: JsonNode) =
-  ## Adds `child` to a JArray node `father`.
-  assert father.kind == JArray
-  father.elems.add(child)
-
-proc add*(obj: JsonNode, key: string, val: JsonNode) =
-  ## Sets a field from a `JObject`.
-  assert obj.kind == JObject
-  obj.fields[key] = val
-
 proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
   ## Sets a field from a `JObject`.
   assert(obj.kind == JObject)
@@ -1203,7 +1214,7 @@ proc parseJson(p: var JsonParser): JsonNode =
     raiseParseErr(p, "{")
 
 when not defined(js):
-  proc parseJson*(s: Stream, filename: string): JsonNode =
+  proc parseJson*(s: Stream, filename: string = ""): JsonNode =
     ## Parses from a stream `s` into a `JsonNode`. `filename` is only needed
     ## for nice error messages.
     ## If `s` contains extra data, it will raise `JsonParsingError`.
@@ -1934,4 +1945,8 @@ when isMainModule:
     except JsonParsingError:
       doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected])
 
+  # bug #6438
+  doAssert($ %*[] == "[]")
+  doAssert($ %*{} == "{}")
+
   echo("Tests succeeded!")
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index 1a62c0bf6..98fc62a3b 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -498,7 +498,7 @@ proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
     # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
     var buf: array[64, char]
     if inet_ntop(name.sin6_family.cint,
-                 addr name, buf.cstring, sizeof(buf).int32).isNil:
+                 addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil:
       raiseOSError(osLastError())
     result = ($buf, Port(nativesockets.ntohs(name.sin6_port)))
   else:
@@ -534,7 +534,7 @@ proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
     # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
     var buf: array[64, char]
     if inet_ntop(name.sin6_family.cint,
-                 addr name, buf.cstring, sizeof(buf).int32).isNil:
+                 addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil:
       raiseOSError(osLastError())
     result = ($buf, Port(nativesockets.ntohs(name.sin6_port)))
   else:
diff --git a/lib/pure/options.nim b/lib/pure/options.nim
index 2abb80016..ad63bbcb6 100644
--- a/lib/pure/options.nim
+++ b/lib/pure/options.nim
@@ -15,7 +15,7 @@
 ## A value of type ``Option[T]`` either contains a value `x` (represented as
 ## ``some(x)``) or is empty (``none(T)``).
 ##
-## This can be useful when you have a value that can be present or not.  The
+## This can be useful when you have a value that can be present or not. The
 ## absence of a value is often represented by ``nil``, but it is not always
 ## available, nor is it always a good solution.
 ##
@@ -67,10 +67,8 @@
 ##     assert(false)  # This will not be reached
 ##   except UnpackError:  # Because an exception is raised
 ##     discard
-
 import typetraits
 
-
 type
   Option*[T] = object
     ## An optional type that stores its value and state separately in a boolean.
@@ -78,7 +76,6 @@ type
     has: bool
   UnpackError* = ref object of ValueError
 
-
 proc some*[T](val: T): Option[T] =
   ## Returns a ``Option`` that has this value.
   result.has = true
@@ -88,14 +85,12 @@ proc none*(T: typedesc): Option[T] =
   ## Returns a ``Option`` for this type that has no value.
   result.has = false
 
-
 proc isSome*[T](self: Option[T]): bool =
   self.has
 
 proc isNone*[T](self: Option[T]): bool =
   not self.has
 
-
 proc unsafeGet*[T](self: Option[T]): T =
   ## Returns the value of a ``some``. Behavior is undefined for ``none``.
   assert self.isSome
@@ -110,12 +105,11 @@ proc get*[T](self: Option[T]): T =
 
 proc get*[T](self: Option[T], otherwise: T): T =
   ## Returns the contents of this option or `otherwise` if the option is none.
-  if self.isSome:
+  if self.has:
     self.val
   else:
     otherwise
 
-
 proc map*[T](self: Option[T], callback: proc (input: T)) =
   ## Applies a callback to the value in this Option
   if self.has:
@@ -123,12 +117,27 @@ proc map*[T](self: Option[T], callback: proc (input: T)) =
 
 proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] =
   ## Applies a callback to the value in this Option and returns an option
-  ## containing the new value. If this option is None, None will be returned
+  ## containing the new value. If this option is None, None will be returned.
   if self.has:
-    some[R]( callback(self.val) )
+    some[R](callback(self.val))
   else:
     none(R)
 
+proc flatten*[A](self: Option[Option[A]]): Option[A] =
+  ## Remove one level of structure in a nested Option.
+  if self.has:
+    self.val
+  else:
+    none(A)
+
+proc flatMap*[A, B](self: Option[A], callback: proc (input: A): Option[B]): Option[B] =
+  ## Applies a callback to the value in this Option and returns an
+  ## option containing the new value. If this option is None, None will be
+  ## returned. Similar to ``map``, with the difference that the callback
+  ## returns an Option, not a raw value. This allows multiple procs with a
+  ## signature of ``A -> Option[B]`` (including A = B) to be chained together.
+  map(self, callback).flatten()
+
 proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] =
   ## Applies a callback to the value in this Option. If the callback returns
   ## `true`, the option is returned as a Some. If it returns false, it is
@@ -138,21 +147,18 @@ proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] =
   else:
     self
 
-
 proc `==`*(a, b: Option): bool =
   ## Returns ``true`` if both ``Option``s are ``none``,
   ## or if they have equal values
   (a.has and b.has and a.val == b.val) or (not a.has and not b.has)
 
-
-proc `$`*[T]( self: Option[T] ): string =
+proc `$`*[T](self: Option[T]): string =
   ## Returns the contents of this option or `otherwise` if the option is none.
   if self.has:
     "Some(" & $self.val & ")"
   else:
     "None[" & T.name & "]"
 
-
 when isMainModule:
   import unittest, sequtils
 
@@ -198,12 +204,12 @@ when isMainModule:
         check false
 
     test "get with a default value":
-      check( some("Correct").get("Wrong") == "Correct" )
-      check( stringNone.get("Correct") == "Correct" )
+      check(some("Correct").get("Wrong") == "Correct")
+      check(stringNone.get("Correct") == "Correct")
 
     test "$":
-      check( $(some("Correct")) == "Some(Correct)" )
-      check( $(stringNone) == "None[string]" )
+      check($(some("Correct")) == "Some(Correct)")
+      check($(stringNone) == "None[string]")
 
     test "map with a void result":
       var procRan = 0
@@ -212,11 +218,38 @@ when isMainModule:
       intNone.map(proc (v: int) = check false)
 
     test "map":
-      check( some(123).map(proc (v: int): int = v * 2) == some(246) )
-      check( intNone.map(proc (v: int): int = v * 2).isNone )
+      check(some(123).map(proc (v: int): int = v * 2) == some(246))
+      check(intNone.map(proc (v: int): int = v * 2).isNone)
 
     test "filter":
-      check( some(123).filter(proc (v: int): bool = v == 123) == some(123) )
-      check( some(456).filter(proc (v: int): bool = v == 123).isNone )
-      check( intNone.filter(proc (v: int): bool = check false).isNone )
-
+      check(some(123).filter(proc (v: int): bool = v == 123) == some(123))
+      check(some(456).filter(proc (v: int): bool = v == 123).isNone)
+      check(intNone.filter(proc (v: int): bool = check false).isNone)
+
+    test "flatMap":
+      proc addOneIfNotZero(v: int): Option[int] =
+        if v != 0:
+          result = some(v + 1)
+        else:
+          result = none(int)
+
+      check(some(1).flatMap(addOneIfNotZero) == some(2))
+      check(some(0).flatMap(addOneIfNotZero) == none(int))
+      check(some(1).flatMap(addOneIfNotZero).flatMap(addOneIfNotZero) == some(3))
+
+      proc maybeToString(v: int): Option[string] =
+        if v != 0:
+          result = some($v)
+        else:
+          result = none(string)
+
+      check(some(1).flatMap(maybeToString) == some("1"))
+
+      proc maybeExclaim(v: string): Option[string] =
+        if v != "":
+          result = some v & "!"
+        else:
+          result = none(string)
+
+      check(some(1).flatMap(maybeToString).flatMap(maybeExclaim) == some("1!"))
+      check(some(0).flatMap(maybeToString).flatMap(maybeExclaim) == none(string))
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index fa723f593..07429b9a9 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -410,13 +410,11 @@ when defined(Windows) and not defined(useNimRtl):
     result.readDataImpl = hsReadData
     result.writeDataImpl = hsWriteData
 
-  proc buildCommandLine(a: string, args: openArray[string]): cstring =
-    var res = quoteShell(a)
+  proc buildCommandLine(a: string, args: openArray[string]): string =
+    result = quoteShell(a)
     for i in 0..high(args):
-      res.add(' ')
-      res.add(quoteShell(args[i]))
-    result = cast[cstring](alloc0(res.len+1))
-    copyMem(result, cstring(res), res.len)
+      result.add(' ')
+      result.add(quoteShell(args[i]))
 
   proc buildEnv(env: StringTableRef): tuple[str: cstring, len: int] =
     var L = 0
@@ -540,11 +538,13 @@ when defined(Windows) and not defined(useNimRtl):
       result.errHandle = FileHandle(si.hStdError)
 
     var cmdl: cstring
+    var cmdRoot: string
     if poEvalCommand in options:
       cmdl = command
       assert args.len == 0
     else:
-      cmdl = buildCommandLine(command, args)
+      cmdRoot = buildCommandLine(command, args)
+      cmdl = cstring(cmdRoot)
     var wd: cstring = nil
     var e = (str: nil.cstring, len: -1)
     if len(workingDir) > 0: wd = workingDir
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index b39d3b691..8b4e6bd05 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -888,7 +888,7 @@ proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
     n = x
   result = newString(len)
   for j in countdown(len-1, 0):
-    result[j] = HexChars[(n and 0xF).int]
+    result[j] = HexChars[int(n and 0xF)]
     n = n shr 4
     # handle negative overflow
     if n == 0 and x < 0: n = -1
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index 28691fcb4..3772a213a 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -509,10 +509,6 @@ macro check*(conditions: untyped): untyped =
   ##    "AKB48".toLowerAscii() == "akb48"
   ##    'C' in teams
   let checked = callsite()[1]
-  var
-    argsAsgns = newNimNode(nnkStmtList)
-    argsPrintOuts = newNimNode(nnkStmtList)
-    counter = 0
 
   template asgn(a: untyped, value: typed) =
     var a = value # XXX: we need "var: var" here in order to
@@ -522,66 +518,71 @@ macro check*(conditions: untyped): untyped =
     when compiles(string($value)):
       checkpoint(name & " was " & $value)
 
-  proc inspectArgs(exp: NimNode): NimNode =
-    result = copyNimTree(exp)
+  proc inspectArgs(exp: NimNode): tuple[assigns, check, printOuts: NimNode] =
+    result.check = copyNimTree(exp)
+    result.assigns = newNimNode(nnkStmtList)
+    result.printOuts = newNimNode(nnkStmtList)
+
+    var counter = 0
+
     if exp[0].kind == nnkIdent and
-        $exp[0] in ["and", "or", "not", "in", "notin", "==", "<=",
+        $exp[0] in ["not", "in", "notin", "==", "<=",
                     ">=", "<", ">", "!=", "is", "isnot"]:
-      for i in countup(1, exp.len - 1):
+
+      for i in 1 ..< exp.len:
         if exp[i].kind notin nnkLiterals:
           inc counter
-          var arg = newIdentNode(":p" & $counter)
-          var argStr = exp[i].toStrLit
-          var paramAst = exp[i]
+          let argStr = exp[i].toStrLit
+          let paramAst = exp[i]
           if exp[i].kind == nnkIdent:
-            argsPrintOuts.add getAst(print(argStr, paramAst))
-          if exp[i].kind in nnkCallKinds:
-            var callVar = newIdentNode(":c" & $counter)
-            argsAsgns.add getAst(asgn(callVar, paramAst))
-            result[i] = callVar
-            argsPrintOuts.add getAst(print(argStr, callVar))
+            result.printOuts.add getAst(print(argStr, paramAst))
+          if exp[i].kind in nnkCallKinds + { nnkDotExpr, nnkBracketExpr }:
+            let callVar = newIdentNode(":c" & $counter)
+            result.assigns.add getAst(asgn(callVar, paramAst))
+            result.check[i] = callVar
+            result.printOuts.add getAst(print(argStr, callVar))
           if exp[i].kind == nnkExprEqExpr:
             # ExprEqExpr
             #   Ident !"v"
             #   IntLit 2
-            result[i] = exp[i][1]
+            result.check[i] = exp[i][1]
           if exp[i].typekind notin {ntyTypeDesc}:
-            argsAsgns.add getAst(asgn(arg, paramAst))
-            argsPrintOuts.add getAst(print(argStr, arg))
+            let arg = newIdentNode(":p" & $counter)
+            result.assigns.add getAst(asgn(arg, paramAst))
+            result.printOuts.add getAst(print(argStr, arg))
             if exp[i].kind != nnkExprEqExpr:
-              result[i] = arg
+              result.check[i] = arg
             else:
-              result[i][1] = arg
+              result.check[i][1] = arg
 
   case checked.kind
   of nnkCallKinds:
-    template rewrite(call, lineInfoLit, callLit,
-                     argAssgs, argPrintOuts) =
+
+    let (assigns, check, printOuts) = inspectArgs(checked)
+    let lineinfo = newStrLitNode(checked.lineinfo)
+    let callLit = checked.toStrLit
+    result = quote do:
       block:
-        argAssgs #all callables (and assignments) are run here
-        if not call:
-          checkpoint(lineInfoLit & ": Check failed: " & callLit)
-          argPrintOuts
+        `assigns`
+        if not `check`:
+          checkpoint(`lineinfo` & ": Check failed: " & `callLit`)
+          `printOuts`
           fail()
 
-    var checkedStr = checked.toStrLit
-    let parameterizedCheck = inspectArgs(checked)
-    result = getAst(rewrite(parameterizedCheck, checked.lineinfo, checkedStr,
-                            argsAsgns, argsPrintOuts))
-
   of nnkStmtList:
     result = newNimNode(nnkStmtList)
-    for i in countup(0, checked.len - 1):
-      if checked[i].kind != nnkCommentStmt:
-        result.add(newCall(!"check", checked[i]))
+    for node in checked:
+      if node.kind != nnkCommentStmt:
+        result.add(newCall(!"check", node))
 
   else:
-    template rewrite(exp, lineInfoLit, expLit) =
-      if not exp:
-        checkpoint(lineInfoLit & ": Check failed: " & expLit)
-        fail()
+    let lineinfo = newStrLitNode(checked.lineinfo)
+    let callLit = checked.toStrLit
 
-    result = getAst(rewrite(checked, checked.lineinfo, checked.toStrLit))
+    result = quote do:
+      if not `checked`:
+        checkpoint(`lineinfo` & ": Check failed: " & `callLit`)
+        fail()
 
 template require*(conditions: untyped) =
   ## Same as `check` except any failed test causes the program to quit
diff --git a/lib/system.nim b/lib/system.nim
index ad958d733..dc3152faf 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -246,6 +246,9 @@ type
   UncheckedArray* {.unchecked.}[T] = array[0, T]
     ## Array with no bounds checking
 
+when defined(nimHasOpt):
+  type opt*{.magic: "Opt".}[T]
+
 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
@@ -409,8 +412,7 @@ when not defined(JS):
 
 when not defined(JS) and not defined(nimscript):
   template space(s: PGenericSeq): int {.dirty.} =
-    s.reserved and not seqShallowFlag
-
+    s.reserved and not (seqShallowFlag or strlitFlag)
   include "system/hti"
 
 type
@@ -718,7 +720,7 @@ proc len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.
   magic: "LengthOpenArray", noSideEffect.}
 proc len*(x: string): int {.magic: "LengthStr", noSideEffect.}
 proc len*(x: cstring): int {.magic: "LengthStr", noSideEffect.}
-proc len*[I, T](x: array[I, T]): int {.magic: "LengthArray", noSideEffect.}
+proc len*(x: (type array)|array): int {.magic: "LengthArray", noSideEffect.}
 proc len*[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}
   ## returns the length of an array, an openarray, a sequence or a string.
   ## This is roughly the same as ``high(T)-low(T)+1``, but its resulting type is
@@ -1329,6 +1331,9 @@ const
     ## "amd64", "mips", "mipsel", "arm", "arm64", "mips64", "mips64el".
 
   seqShallowFlag = low(int)
+  strlitFlag = 1 shl (sizeof(int)*8 - 2) # later versions of the codegen \
+  # emit this flag
+  # for string literals, it allows for some optimizations.
 
 {.push profiler: off.}
 when defined(nimKnowsNimvm):
@@ -1435,7 +1440,12 @@ when defined(nimdoc):
 
 elif defined(genode):
   proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn,
-    importcpp: "genodeEnv->parent().exit(@)", header: "<base/env.h>".}
+    importcpp: "genodeEnv->parent().exit(@); Genode::sleep_forever()",
+    header: "<base/sleep.h>".}
+
+elif defined(nodejs):
+  proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", 
+    importc: "process.exit", noreturn.}
 
 else:
   proc quit*(errorcode: int = QuitSuccess) {.
@@ -3402,10 +3412,10 @@ when hasAlloc or defined(nimscript):
   proc `[]`*(s: string, x: Slice[int]): string {.inline.} =
     ## slice operation for strings.
     ## returns the inclusive range [s[x.a], s[x.b]]:
-    ## 
+    ##
     ## .. code-block:: nim
     ##    var s = "abcdef"
-    ##    assert s[1..3] == "bcd" 
+    ##    assert s[1..3] == "bcd"
     result = s.substr(x.a, x.b)
 
   proc `[]=`*(s: var string, x: Slice[int], b: string) =
@@ -3427,7 +3437,7 @@ when hasAlloc or defined(nimscript):
 proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[int]): seq[T] =
   ## slice operation for arrays.
   ## returns the inclusive range [a[x.a], a[x.b]]:
-  ## 
+  ##
   ## .. code-block:: nim
   ##    var a = [1,2,3,4]
   ##    assert a[0..2] == @[1,2,3]
@@ -3466,7 +3476,7 @@ proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) =
 proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] =
   ## slice operation for sequences.
   ## returns the inclusive range [s[x.a], s[x.b]]:
-  ## 
+  ##
   ## .. code-block:: nim
   ##    var s = @[1,2,3,4]
   ##    assert s[0..2] == @[1,2,3]
@@ -3719,7 +3729,9 @@ proc shallow*(s: var string) {.noSideEffect, inline.} =
   ## purposes.
   when not defined(JS) and not defined(nimscript):
     var s = cast[PGenericSeq](s)
-    s.reserved = s.reserved or seqShallowFlag
+    # string literals cannot become 'shallow':
+    if (s.reserved and strlitFlag) == 0:
+      s.reserved = s.reserved or seqShallowFlag
 
 type
   NimNodeObj = object
diff --git a/lib/system/assign.nim b/lib/system/assign.nim
index 61c33e51b..115df61a7 100644
--- a/lib/system/assign.nim
+++ b/lib/system/assign.nim
@@ -63,12 +63,17 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
     sysAssert(dest != nil, "genericAssignAux 3")
     unsureAsgnRef(x, newSeq(mt, seq.len))
     var dst = cast[ByteAddress](cast[PPointer](dest)[])
-    for i in 0..seq.len-1:
-      genericAssignAux(
-        cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
-        cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
-                     GenericSeqSize),
-        mt.base, shallow)
+    if ntfNoRefs in mt.base.flags:
+      copyMem(cast[pointer](dst +% GenericSeqSize),
+              cast[pointer](cast[ByteAddress](s2) +% GenericSeqSize),
+              seq.len * mt.base.size)
+    else:
+      for i in 0..seq.len-1:
+        genericAssignAux(
+          cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
+          cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
+                      GenericSeqSize),
+          mt.base, shallow)
   of tyObject:
     if mt.base != nil:
       genericAssignAux(dest, src, mt.base, shallow)
@@ -89,6 +94,19 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
                        cast[pointer](s +% i*% mt.base.size), mt.base, shallow)
   of tyRef:
     unsureAsgnRef(cast[PPointer](dest), cast[PPointer](s)[])
+  of tyOptAsRef:
+    let s2 = cast[PPointer](src)[]
+    let d = cast[PPointer](dest)
+    if s2 == nil:
+      unsureAsgnRef(d, s2)
+    else:
+      when declared(usrToCell):
+        let realType = usrToCell(s2).typ
+      else:
+        let realType = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[]
+                       else: mt.base
+      var z = newObj(realType, realType.base.size)
+      genericAssignAux(d, addr z, mt.base, shallow)
   else:
     copyMem(dest, src, mt.size) # copy raw bits
 
@@ -115,6 +133,7 @@ when false:
     of tyPtr: k = "ptr"
     of tyRef: k = "ref"
     of tyVar: k = "var"
+    of tyOptAsRef: k = "optref"
     of tySequence: k = "seq"
     of tyProc: k = "proc"
     of tyPointer: k = "range"
@@ -195,7 +214,7 @@ proc genericReset(dest: pointer, mt: PNimType) =
   var d = cast[ByteAddress](dest)
   sysAssert(mt != nil, "genericReset 2")
   case mt.kind
-  of tyString, tyRef, tySequence:
+  of tyString, tyRef, tyOptAsRef, tySequence:
     unsureAsgnRef(cast[PPointer](dest), nil)
   of tyTuple:
     genericResetAux(dest, mt.node)
diff --git a/lib/system/channels.nim b/lib/system/channels.nim
index 1b90e245f..df6c6d41e 100644
--- a/lib/system/channels.nim
+++ b/lib/system/channels.nim
@@ -144,7 +144,7 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
     for i in 0..(mt.size div mt.base.size)-1:
       storeAux(cast[pointer](d +% i*% mt.base.size),
                cast[pointer](s +% i*% mt.base.size), mt.base, t, mode)
-  of tyRef:
+  of tyRef, tyOptAsRef:
     var s = cast[PPointer](src)[]
     var x = cast[PPointer](dest)
     if s == nil:
diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim
index 65ba2278c..51e138e5e 100644
--- a/lib/system/deepcopy.nim
+++ b/lib/system/deepcopy.nim
@@ -124,7 +124,7 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
     for i in 0..(mt.size div mt.base.size)-1:
       genericDeepCopyAux(cast[pointer](d +% i*% mt.base.size),
                          cast[pointer](s +% i*% mt.base.size), mt.base, tab)
-  of tyRef:
+  of tyRef, tyOptAsRef:
     let s2 = cast[PPointer](src)[]
     if s2 == nil:
       unsureAsgnRef(cast[PPointer](dest), s2)
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index 80aa5cf1b..a2ff72a30 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -349,7 +349,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
     for i in 0..n.len-1:
       # inlined for speed
       if n.sons[i].kind == nkSlot:
-        if n.sons[i].typ.kind in {tyRef, tyString, tySequence}:
+        if n.sons[i].typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}:
           doOperation(cast[PPointer](d +% n.sons[i].offset)[], op)
         else:
           forAllChildrenAux(cast[pointer](d +% n.sons[i].offset),
@@ -366,7 +366,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
   if dest == nil: return # nothing to do
   if ntfNoRefs notin mt.flags:
     case mt.kind
-    of tyRef, tyString, tySequence: # leaf:
+    of tyRef, tyOptAsRef, tyString, tySequence: # leaf:
       doOperation(cast[PPointer](d)[], op)
     of tyObject, tyTuple:
       forAllSlotsAux(dest, mt.node, op)
@@ -379,13 +379,13 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
   gcAssert(cell != nil, "forAllChildren: 1")
   gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2")
   gcAssert(cell.typ != nil, "forAllChildren: 3")
-  gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 4"
+  gcAssert cell.typ.kind in {tyRef, tyOptAsRef, tySequence, tyString}, "forAllChildren: 4"
   let marker = cell.typ.marker
   if marker != nil:
     marker(cellToUsr(cell), op.int)
   else:
     case cell.typ.kind
-    of tyRef: # common case
+    of tyRef, tyOptAsRef: # common case
       forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
     of tySequence:
       var d = cast[ByteAddress](cellToUsr(cell))
@@ -461,7 +461,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
   incTypeSize typ, size
   sysAssert(allocInv(gch.region), "rawNewObj begin")
   acquire(gch)
-  gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
+  gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
   collectCT(gch)
   var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
   #gcAssert typ.kind in {tyString, tySequence} or size >= typ.base.size, "size too small"
@@ -509,7 +509,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
   incTypeSize typ, size
   sysAssert(allocInv(gch.region), "newObjRC1 begin")
   acquire(gch)
-  gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
+  gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
   collectCT(gch)
   sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
 
@@ -945,10 +945,10 @@ when not defined(useNimRtl):
              "[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
              "[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) & "\n"
     when nimCoroutines:
-      result = result & "[GC] number of stacks: " & $gch.stack.len & "\n"
+      result.add "[GC] number of stacks: " & $gch.stack.len & "\n"
       for stack in items(gch.stack):
-        result = result & "[GC]   stack " & stack.bottom.repr & "[GC]     max stack size " & cast[pointer](stack.maxStackSize).repr & "\n"
+        result.add "[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"
+      result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
 
 {.pop.} # profiler: off, stackTrace: off
diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim
index 083c06fe3..6dffc323e 100644
--- a/lib/system/gc2.nim
+++ b/lib/system/gc2.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2015 Andreas Rumpf
+#        (c) Copyright 2017 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -9,17 +9,19 @@
 
 #            Garbage Collector
 #
-# The basic algorithm is *Deferred Reference Counting* with an incremental mark
+# The basic algorithm is an incremental mark
 # and sweep GC to free cycles. It is hard realtime in that if you play
 # according to its rules, no deadline will ever be missed.
-
-# XXX Ensure by smart color masking that the object is not in the ZCT.
+# Since this kind of collector is very bad at recycling dead objects
+# early, Nim's codegen emits ``nimEscape`` calls at strategic
+# places. For this to work even 'unsureAsgnRef' needs to mark things
+# so that only return values need to be considered in ``nimEscape``.
 
 {.push profiler:off.}
 
 const
   CycleIncrease = 2 # is a multiplicative increase
-  InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow
+  InitialCycleThreshold = 512*1024 # start collecting after 500KB
   ZctThreshold = 500  # we collect garbage if the ZCT's size
                       # reaches this threshold
                       # this seems to be a good value
@@ -40,13 +42,11 @@ type
 iterToProc(allObjects, ptr ObjectSpaceIter, allObjectsAsProc)
 
 const
-  rcIncrement = 0b1000 # so that lowest 3 bits are not touched
+  escapedBit = 0b1000 # so that lowest 3 bits are not touched
   rcBlackOrig = 0b000
   rcWhiteOrig = 0b001
   rcGrey = 0b010   # traditional color for incremental mark&sweep
   rcUnused = 0b011
-  ZctFlag = 0b100  # in ZCT
-  rcShift = 3      # shift by rcShift to get the reference counter
   colorMask = 0b011
 type
   WalkOp = enum
@@ -63,13 +63,13 @@ type
 
   GcStat = object
     stackScans: int          # number of performed stack scans (for statistics)
-    cycleCollections: int    # number of performed full collections
+    completedCollections: int    # number of performed full collections
     maxThreshold: int        # max threshold that has been set
     maxStackSize: int        # max stack size
     maxStackCells: int       # max stack cells in ``decStack``
     cycleTableSize: int      # max entries in cycle table
     maxPause: int64          # max measured GC pause in nanoseconds
-  
+
   GcStack {.final, pure.} = object
     when nimCoroutines:
       prev: ptr GcStack
@@ -93,15 +93,13 @@ type
     cycleThreshold: int
     when useCellIds:
       idGenerator: int
-    zct: CellSeq             # the zero count table
-    decStack: CellSeq        # cells in the stack that are to decref again
     greyStack: CellSeq
     recGcLock: int           # prevent recursion via finalizers; no thread lock
     when withRealTime:
       maxPause: Nanos        # max allowed pause in nanoseconds; active if > 0
     region: MemRegion        # garbage collected region
     stat: GcStat
-    additionalRoots: CellSeq # dummy roots for GC_ref/unref
+    additionalRoots: CellSeq # explicit roots for GC_ref/unref
     spaceIter: ObjectSpaceIter
     pDumpHeapFile: pointer # File that is used for GC_dumpHeap
     when hasThreadSupport:
@@ -113,19 +111,25 @@ var
 when not defined(useNimRtl):
   instantiateForRegion(gch.region)
 
+template acquire(gch: GcHeap) =
+  when hasThreadSupport and hasSharedHeap:
+    acquireSys(HeapLock)
+
+template release(gch: GcHeap) =
+  when hasThreadSupport and hasSharedHeap:
+    releaseSys(HeapLock)
+
 proc initGC() =
   when not defined(useNimRtl):
     gch.red = (1-gch.black)
     gch.cycleThreshold = InitialCycleThreshold
     gch.stat.stackScans = 0
-    gch.stat.cycleCollections = 0
+    gch.stat.completedCollections = 0
     gch.stat.maxThreshold = 0
     gch.stat.maxStackSize = 0
     gch.stat.maxStackCells = 0
     gch.stat.cycleTableSize = 0
     # init the rt
-    init(gch.zct)
-    init(gch.decStack)
     init(gch.additionalRoots)
     init(gch.greyStack)
     when hasThreadSupport:
@@ -147,11 +151,6 @@ template gcAssert(cond: bool, msg: string) =
       writeStackTrace()
       quit 1
 
-proc addZCT(s: var CellSeq, c: PCell) {.noinline.} =
-  if (c.refcount and ZctFlag) == 0:
-    c.refcount = c.refcount or ZctFlag
-    add(s, c)
-
 proc cellToUsr(cell: PCell): pointer {.inline.} =
   # convert object (=pointer to refcount) to pointer to userdata
   result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
@@ -168,7 +167,7 @@ proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
   result = usrToCell(c).typ
 
 proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
-  result = int(usrToCell(p).refcount) shr rcShift
+  result = 0
 
 # this that has to equals zero, otherwise we have to round up UnitsPerPage:
 when BitsPerPage mod (sizeof(int)*8) != 0:
@@ -178,6 +177,12 @@ template color(c): expr = c.refCount and colorMask
 template setColor(c, col) =
   c.refcount = c.refcount and not colorMask or col
 
+template markAsEscaped(c: PCell) =
+  c.refcount = c.refcount or escapedBit
+
+template didEscape(c: PCell): bool =
+  (c.refCount and escapedBit) != 0
+
 proc writeCell(file: File; msg: cstring, c: PCell) =
   var kind = -1
   if c.typ != nil: kind = ord(c.typ.kind)
@@ -189,18 +194,18 @@ proc writeCell(file: File; msg: cstring, c: PCell) =
   else:
     let id = c
   when leakDetector:
-    c_fprintf(file, "%s %p %d rc=%ld color=%c from %s(%ld)\n",
-              msg, id, kind, c.refcount shr rcShift, col, c.filename, c.line)
+    c_fprintf(file, "%s %p %d escaped=%ld color=%c from %s(%ld)\n",
+              msg, id, kind, didEscape(c), col, c.filename, c.line)
   else:
-    c_fprintf(file, "%s %p %d rc=%ld color=%c\n",
-              msg, id, kind, c.refcount shr rcShift, col)
+    c_fprintf(file, "%s %p %d escaped=%ld color=%c\n",
+              msg, id, kind, didEscape(c), col)
 
 proc writeCell(msg: cstring, c: PCell) =
   stdout.writeCell(msg, c)
 
 proc myastToStr[T](x: T): string {.magic: "AstToStr", noSideEffect.}
 
-template gcTrace(cell, state: expr): stmt {.immediate.} =
+template gcTrace(cell, state: untyped) =
   when traceGC: writeCell(myastToStr(state), cell)
 
 # forward declarations:
@@ -211,52 +216,17 @@ proc doOperation(p: pointer, op: WalkOp) {.benign.}
 proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
 # we need the prototype here for debugging purposes
 
-when hasThreadSupport and hasSharedHeap:
-  template `--`(x: expr): expr = atomicDec(x, rcIncrement) <% rcIncrement
-  template `++`(x: expr): stmt = discard atomicInc(x, rcIncrement)
-else:
-  template `--`(x: expr): expr =
-    dec(x, rcIncrement)
-    x <% rcIncrement
-  template `++`(x: expr): stmt = inc(x, rcIncrement)
-
-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)
-
 proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
   # we MUST access gch as a global here, because this crosses DLL boundaries!
   discard
 
-proc rtlAddZCT(c: PCell) {.rtl, inl.} =
-  # we MUST access gch as a global here, because this crosses DLL boundaries!
-  addZCT(gch.zct, c)
-
-proc decRef(c: PCell) {.inline.} =
-  gcAssert(isAllocatedPtr(gch.region, c), "decRef: interiorPtr")
-  gcAssert(c.refcount >=% rcIncrement, "decRef")
-  if --c.refcount:
-    rtlAddZCT(c)
-
-proc incRef(c: PCell) {.inline.} =
-  gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr")
-  c.refcount = c.refcount +% rcIncrement
-
 proc nimGCref(p: pointer) {.compilerProc.} =
   let cell = usrToCell(p)
-  incRef(cell)
+  markAsEscaped(cell)
   add(gch.additionalRoots, cell)
 
 proc nimGCunref(p: pointer) {.compilerProc.} =
   let cell = usrToCell(p)
-  decRef(cell)
   var L = gch.additionalRoots.len-1
   var i = L
   let d = gch.additionalRoots.d
@@ -267,6 +237,12 @@ proc nimGCunref(p: pointer) {.compilerProc.} =
       break
     dec(i)
 
+proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} =
+  discard "can we do some freeing here?"
+
+proc nimGCunrefRC1(p: pointer) {.compilerProc, inline.} =
+  discard "can we do some freeing here?"
+
 template markGrey(x: PCell) =
   if x.color != 1-gch.black and gch.phase == Phase.Marking:
     if not isAllocatedPtr(gch.region, x):
@@ -280,59 +256,32 @@ proc GC_addCycleRoot*[T](p: ref T) {.inline.} =
   ## adds 'p' to the cycle candidate set for the cycle collector. It is
   ## necessary if you used the 'acyclic' pragma for optimization
   ## purposes and need to break cycles manually.
-  rtlAddCycleRoot(usrToCell(cast[pointer](p)))
-
-proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} =
-  sysAssert(allocInv(gch.region), "begin nimGCunrefNoCycle")
-  var c = usrToCell(p)
-  gcAssert(isAllocatedPtr(gch.region, c), "nimGCunrefNoCycle: isAllocatedPtr")
-  if --c.refcount:
-    rtlAddZCT(c)
-    sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 2")
-  sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 5")
+  discard
 
-proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
-  # the code generator calls this proc!
+template asgnRefImpl =
   gcAssert(not isOnStack(dest), "asgnRef")
   # BUGFIX: first incRef then decRef!
   if src != nil:
     let s = usrToCell(src)
-    incRef(s)
+    markAsEscaped(s)
     markGrey(s)
-  if dest[] != nil: decRef(usrToCell(dest[]))
   dest[] = src
 
+proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
+  # the code generator calls this proc!
+  asgnRefImpl()
+
 proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} =
-  # the code generator calls this proc if it is known at compile time that no
-  # cycle is possible.
-  gcAssert(not isOnStack(dest), "asgnRefNoCycle")
-  if src != nil:
-    var c = usrToCell(src)
-    ++c.refcount
-    markGrey(c)
-  if dest[] != nil:
-    var c = usrToCell(dest[])
-    if --c.refcount:
-      rtlAddZCT(c)
-  dest[] = src
+  asgnRefImpl()
 
 proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} =
-  # unsureAsgnRef updates the reference counters only if dest is not on the
+  # unsureAsgnRef marks 'src' as grey only if dest is not on the
   # stack. It is used by the code generator if it cannot decide wether a
   # reference is in the stack or not (this can happen for var parameters).
-  if not isOnStack(dest):
-    if src != nil:
-      let s = usrToCell(src)
-      incRef(s)
-      markGrey(s)
-    # XXX finally use assembler for the stack checking instead!
-    # the test for '!= nil' is correct, but I got tired of the segfaults
-    # resulting from the crappy stack checking:
-    if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[]))
-  else:
-    # can't be an interior pointer if it's a stack location!
-    gcAssert(interiorAllocatedPtr(gch.region, dest) == nil,
-             "stack loc AND interior pointer")
+  if src != nil:
+    let s = usrToCell(src)
+    markAsEscaped(s)
+    if not isOnStack(dest): markGrey(s)
   dest[] = src
 
 type
@@ -366,7 +315,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
   if dest == nil: return # nothing to do
   if ntfNoRefs notin mt.flags:
     case mt.kind
-    of tyRef, tyString, tySequence: # leaf:
+    of tyRef, tyOptAsRef, tyString, tySequence: # leaf:
       doOperation(cast[PPointer](d)[], op)
     of tyObject, tyTuple:
       forAllSlotsAux(dest, mt.node, op)
@@ -379,13 +328,13 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
   gcAssert(cell != nil, "forAllChildren: 1")
   gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2")
   gcAssert(cell.typ != nil, "forAllChildren: 3")
-  gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 4"
+  gcAssert cell.typ.kind in {tyRef, tyOptAsRef, tySequence, tyString}, "forAllChildren: 4"
   let marker = cell.typ.marker
   if marker != nil:
     marker(cellToUsr(cell), op.int)
   else:
     case cell.typ.kind
-    of tyRef: # common case
+    of tyRef, tyOptAsRef: # common case
       forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
     of tySequence:
       var d = cast[ByteAddress](cellToUsr(cell))
@@ -396,50 +345,6 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
             GenericSeqSize), cell.typ.base, op)
     else: discard
 
-proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
-  # we check the last 8 entries (cache line) for a slot that could be reused.
-  # In 63% of all cases we succeed here! But we have to optimize the heck
-  # out of this small linear search so that ``newObj`` is not slowed down.
-  #
-  # Slots to try          cache hit
-  # 1                     32%
-  # 4                     59%
-  # 8                     63%
-  # 16                    66%
-  # all slots             68%
-  var L = gch.zct.len
-  var d = gch.zct.d
-  when true:
-    # loop unrolled for performance:
-    template replaceZctEntry(i: expr) =
-      c = d[i]
-      if c.refcount >=% rcIncrement:
-        c.refcount = c.refcount and not ZctFlag
-        d[i] = res
-        return
-    if L > 8:
-      var c: PCell
-      replaceZctEntry(L-1)
-      replaceZctEntry(L-2)
-      replaceZctEntry(L-3)
-      replaceZctEntry(L-4)
-      replaceZctEntry(L-5)
-      replaceZctEntry(L-6)
-      replaceZctEntry(L-7)
-      replaceZctEntry(L-8)
-      add(gch.zct, res)
-    else:
-      d[L] = res
-      inc(gch.zct.len)
-  else:
-    for i in countdown(L-1, max(0, L-8)):
-      var c = d[i]
-      if c.refcount >=% rcIncrement:
-        c.refcount = c.refcount and not ZctFlag
-        d[i] = res
-        return
-    add(gch.zct, res)
-
 {.push stackTrace: off, profiler:off.}
 proc gcInvariant*() =
   sysAssert(allocInv(gch.region), "injected")
@@ -447,10 +352,12 @@ proc gcInvariant*() =
     markForDebug(gch)
 {.pop.}
 
+include gc_common
+
 proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
   # generates a new object and sets its reference counter to 0
   sysAssert(allocInv(gch.region), "rawNewObj begin")
-  gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
+  gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
   collectCT(gch)
   var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
   gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
@@ -461,10 +368,8 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
       res.filename = framePtr.prev.filename
       res.line = framePtr.prev.line
   # refcount is zero, color is black, but mark it to be in the ZCT
-  res.refcount = ZctFlag or allocColor()
+  res.refcount = allocColor()
   sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
-  # its refcount is zero, so add it to the ZCT:
-  addNewObjToZCT(res, gch)
   when logGC: writeCell("new cell", res)
   gcTrace(res, csAllocated)
   when useCellIds:
@@ -493,95 +398,38 @@ proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
   when defined(memProfiler): nimProfile(size)
 
 proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
-  # generates a new object and sets its reference counter to 1
-  sysAssert(allocInv(gch.region), "newObjRC1 begin")
-  gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
-  collectCT(gch)
-  sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
-
-  var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
-  sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc")
-  sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
-  # now it is buffered in the ZCT
-  res.typ = typ
-  when leakDetector:
-    if framePtr != nil and framePtr.prev != nil:
-      res.filename = framePtr.prev.filename
-      res.line = framePtr.prev.line
-  res.refcount = rcIncrement or allocColor() # refcount is 1
-  sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
-  when logGC: writeCell("new cell", res)
-  gcTrace(res, csAllocated)
-  when useCellIds:
-    inc gch.idGenerator
-    res.id = gch.idGenerator
-  result = cellToUsr(res)
-  zeroMem(result, size)
-  sysAssert(allocInv(gch.region), "newObjRC1 end")
-  when defined(memProfiler): nimProfile(size)
+  result = newObj(typ, size)
 
 proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
-  result = newObjRC1(typ, size)
-  cast[PGenericSeq](result).len = len
-  cast[PGenericSeq](result).reserved = len
-  when defined(memProfiler): nimProfile(size)
+  result = newSeq(typ, len)
 
 proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
+  acquire(gch)
   collectCT(gch)
   var ol = usrToCell(old)
-  gcAssert(isAllocatedPtr(gch.region, ol), "growObj: freed pointer?")
-
   sysAssert(ol.typ != nil, "growObj: 1")
   gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
-  sysAssert(allocInv(gch.region), "growObj begin")
 
   var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
   var elemSize = 1
   if ol.typ.kind != tyString: elemSize = ol.typ.base.size
+  incTypeSize ol.typ, newsize
 
-  let oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
+  var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
   copyMem(res, ol, oldsize + sizeof(Cell))
-  zeroMem(cast[pointer](cast[ByteAddress](res) +% oldsize +% sizeof(Cell)),
+  zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
           newsize-oldsize)
   sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
-  # This can be wrong for intermediate temps that are nevertheless on the
-  # heap because of lambda lifting:
-  #gcAssert(res.refcount shr rcShift <=% 1, "growObj: 4")
-  when logGC:
-    writeCell("growObj old cell", ol)
-    writeCell("growObj new cell", res)
-  gcTrace(ol, csZctFreed)
-  gcTrace(res, csAllocated)
-  when reallyDealloc:
-    sysAssert(allocInv(gch.region), "growObj before dealloc")
-    if ol.refcount shr rcShift <=% 1:
-      # free immediately to save space:
-      if (ol.refcount and ZctFlag) != 0:
-        var j = gch.zct.len-1
-        var d = gch.zct.d
-        while j >= 0:
-          if d[j] == ol:
-            d[j] = res
-            break
-          dec(j)
-      rawDealloc(gch.region, ol)
+  when false:
+    # this is wrong since seqs can be shared via 'shallow':
+    when reallyDealloc: rawDealloc(gch.region, ol)
     else:
-      # we split the old refcount in 2 parts. XXX This is still not entirely
-      # correct if the pointer that receives growObj's result is on the stack.
-      # A better fix would be to emit the location specific write barrier for
-      # 'growObj', but this is lots of more work and who knows what new problems
-      # this would create.
-      res.refcount = rcIncrement or allocColor()
-      decRef(ol)
-  else:
-    sysAssert(ol.typ != nil, "growObj: 5")
-    zeroMem(ol, sizeof(Cell))
+      zeroMem(ol, sizeof(Cell))
   when useCellIds:
     inc gch.idGenerator
     res.id = gch.idGenerator
+  release(gch)
   result = cellToUsr(res)
-  sysAssert(allocInv(gch.region), "growObj end")
   when defined(memProfiler): nimProfile(newsize-oldsize)
 
 proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
@@ -637,12 +485,13 @@ proc GC_dumpHeap*(file: File) =
   ## can be translated into "dot" syntax via the "heapdump2dot" tool.
   gch.pDumpHeapFile = file
   var spaceIter: ObjectSpaceIter
-  var d = gch.decStack.d
-  for i in 0 .. < gch.decStack.len:
-    if isAllocatedPtr(gch.region, d[i]):
-      c_fprintf(file, "onstack %p\n", d[i])
-    else:
-      c_fprintf(file, "onstack_invalid %p\n", d[i])
+  when false:
+    var d = gch.decStack.d
+    for i in 0 .. < gch.decStack.len:
+      if isAllocatedPtr(gch.region, d[i]):
+        c_fprintf(file, "onstack %p\n", d[i])
+      else:
+        c_fprintf(file, "onstack_invalid %p\n", d[i])
   for i in 0 .. < globalMarkersLen: globalMarkers[i]()
   while true:
     let x = allObjectsAsProc(gch.region, addr spaceIter)
@@ -667,14 +516,6 @@ proc GC_dumpHeap() =
 
 proc freeCyclicCell(gch: var GcHeap, c: PCell) =
   gcAssert(isAllocatedPtr(gch.region, c), "freeCyclicCell: freed pointer?")
-
-  var d = gch.decStack.d
-  for i in 0..gch.decStack.len-1:
-    if d[i] == c:
-      writeCell("freeing ", c)
-      GC_dumpHeap()
-    gcAssert d[i] != c, "wtf man, freeing obviously alive stuff?!!"
-
   prepareDealloc(c)
   gcTrace(c, csCycFreed)
   when logGC: writeCell("cycle collector dealloc cell", c)
@@ -713,15 +554,6 @@ proc markRoot(gch: var GcHeap, c: PCell) {.inline.} =
   if c.color == 1-gch.black:
     c.setColor(rcGrey)
     add(gch.greyStack, c)
-  elif c.color == rcGrey:
-    var isGrey = false
-    var d = gch.decStack.d
-    for i in 0..gch.decStack.len-1:
-      if d[i] == c:
-        isGrey = true
-        break
-    if not isGrey:
-      gcAssert false, "markRoot: root is already grey?!"
 
 proc markIncremental(gch: var GcHeap): bool =
   var L = addr(gch.greyStack.len)
@@ -741,30 +573,14 @@ proc markIncremental(gch: var GcHeap): bool =
       c.setColor(gch.black)
       forAllChildren(c, waMarkGrey)
     elif c.color == (1-gch.black):
-      gcAssert false, "wtf why are there white object in the greystack?"
+      gcAssert false, "wtf why are there white objects in the greystack?"
     checkTime()
   gcAssert gch.greyStack.len == 0, "markIncremental: greystack not empty "
-
-  # assert that all local roots are black by now:
-  var d = gch.decStack.d
-  var errors = false
-  for i in 0..gch.decStack.len-1:
-    gcAssert(isAllocatedPtr(gch.region, d[i]), "markIncremental: isAllocatedPtr 2")
-    if d[i].color != gch.black:
-      writeCell("not black ", d[i])
-      errors = true
-  gcAssert(not errors, "wtf something wrong hre")
   result = true
 
 proc markGlobals(gch: var GcHeap) =
   for i in 0 .. < globalMarkersLen: globalMarkers[i]()
 
-proc markLocals(gch: var GcHeap) =
-  var d = gch.decStack.d
-  for i in 0 .. < gch.decStack.len:
-    sysAssert isAllocatedPtr(gch.region, d[i]), "markLocals"
-    markRoot(gch, d[i])
-
 proc doOperation(p: pointer, op: WalkOp) =
   if p == nil: return
   var c: PCell = usrToCell(p)
@@ -776,11 +592,7 @@ proc doOperation(p: pointer, op: WalkOp) =
     #if not isAllocatedPtr(gch.region, c):
     #  c_fprintf(stdout, "[GC] decref bug: %p", c)
     gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef")
-    gcAssert(c.refcount >=% rcIncrement, "doOperation 2")
-    #c.refcount = c.refcount -% rcIncrement
-    when logGC: writeCell("decref (from doOperation)", c)
-    decRef(c)
-    #if c.refcount <% rcIncrement: addZCT(gch.zct, c)
+    discard "use me for nimEscape?"
   of waMarkGlobal:
     template handleRoot =
       if gch.dumpHeapFile.isNil:
@@ -811,107 +623,54 @@ proc doOperation(p: pointer, op: WalkOp) =
 proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
   doOperation(d, WalkOp(op))
 
-proc collectZCT(gch: var GcHeap): bool {.benign.}
-
-proc collectCycles(gch: var GcHeap): bool =
-  when hasThreadSupport:
-    for c in gch.toDispose:
-      nimGCunref(c)
+proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
+  # the addresses are not as cells on the stack, so turn them to cells:
+  sysAssert(allocInv(gch.region), "gcMark begin")
+  var cell = usrToCell(p)
+  var c = cast[ByteAddress](cell)
+  if c >% PageSize:
+    # fast check: does it look like a cell?
+    var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
+    if objStart != nil:
+      # mark the cell:
+      markRoot(gch, objStart)
+  sysAssert(allocInv(gch.region), "gcMark end")
 
-  # ensure the ZCT 'color' is not used:
-  while gch.zct.len > 0: discard collectZCT(gch)
+proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
+  forEachStackSlot(gch, gcMark)
 
+proc collectALittle(gch: var GcHeap): bool =
   case gch.phase
   of Phase.None:
-    gch.phase = Phase.Marking
-    markGlobals(gch)
-
-    c_fprintf(stdout, "collectCycles: introduced bug E %ld\n", gch.phase)
-    discard allocInv(gch.region)
+    if getOccupiedMem(gch.region) >= gch.cycleThreshold:
+      gch.phase = Phase.Marking
+      markGlobals(gch)
+      result = collectALittle(gch)
+      #when false: c_fprintf(stdout, "collectALittle: introduced bug E %ld\n", gch.phase)
+      #discard allocInv(gch.region)
   of Phase.Marking:
-    # since locals do not have a write barrier, we need
-    # to keep re-scanning them :-( but there is really nothing we can
-    # do about that.
-    markLocals(gch)
+    when hasThreadSupport:
+      for c in gch.toDispose:
+        nimGCunref(c)
+    prepareForInteriorPointerChecking(gch.region)
+    markStackAndRegisters(gch)
+    inc(gch.stat.stackScans)
     if markIncremental(gch):
       gch.phase = Phase.Sweeping
       gch.red = 1 - gch.red
   of Phase.Sweeping:
     gcAssert gch.greyStack.len == 0, "greystack not empty"
+    when hasThreadSupport:
+      for c in gch.toDispose:
+        nimGCunref(c)
     if sweep(gch):
       gch.phase = Phase.None
       # flip black/white meanings:
       gch.black = 1 - gch.black
       gcAssert gch.red == 1 - gch.black, "red color is wrong"
+      inc(gch.stat.completedCollections)
       result = true
 
-proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
-  # the addresses are not as cells on the stack, so turn them to cells:
-  sysAssert(allocInv(gch.region), "gcMark begin")
-  var cell = usrToCell(p)
-  var c = cast[ByteAddress](cell)
-  if c >% PageSize:
-    # fast check: does it look like a cell?
-    var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
-    if objStart != nil:
-      # mark the cell:
-      objStart.refcount = objStart.refcount +% rcIncrement
-      add(gch.decStack, objStart)
-  sysAssert(allocInv(gch.region), "gcMark end")
-
-include gc_common
-
-proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
-  forEachStackSlot(gch, gcMark)
-
-proc collectZCT(gch: var GcHeap): bool =
-  # Note: Freeing may add child objects to the ZCT! So essentially we do
-  # deep freeing, which is bad for incremental operation. In order to
-  # avoid a deep stack, we move objects to keep the ZCT small.
-  # This is performance critical!
-  var L = addr(gch.zct.len)
-  takeStartTime(100)
-
-  while L[] > 0:
-    var c = gch.zct.d[0]
-    sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr")
-    # remove from ZCT:
-    gcAssert((c.refcount and ZctFlag) == ZctFlag, "collectZCT")
-
-    c.refcount = c.refcount and not ZctFlag
-    gch.zct.d[0] = gch.zct.d[L[] - 1]
-    dec(L[])
-    takeTime()
-    if c.refcount <% rcIncrement and c.color != rcGrey:
-      # It may have a RC > 0, if it is in the hardware stack or
-      # it has not been removed yet from the ZCT. This is because
-      # ``incref`` does not bother to remove the cell from the ZCT
-      # as this might be too slow.
-      # In any case, it should be removed from the ZCT. But not
-      # freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
-      when logGC: writeCell("zct dealloc cell", c)
-      gcTrace(c, csZctFreed)
-      # We are about to free the object, call the finalizer BEFORE its
-      # children are deleted as well, because otherwise the finalizer may
-      # access invalid memory. This is done by prepareDealloc():
-      prepareDealloc(c)
-      forAllChildren(c, waZctDecRef)
-      when reallyDealloc:
-        sysAssert(allocInv(gch.region), "collectZCT: rawDealloc")
-        rawDealloc(gch.region, c)
-      else:
-        sysAssert(c.typ != nil, "collectZCT 2")
-        zeroMem(c, sizeof(Cell))
-    checkTime()
-  result = true
-
-proc unmarkStackAndRegisters(gch: var GcHeap) =
-  var d = gch.decStack.d
-  for i in 0..gch.decStack.len-1:
-    sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters"
-    decRef(d[i])
-  gch.decStack.len = 0
-
 proc collectCTBody(gch: var GcHeap) =
   when withRealTime:
     let t0 = getticks()
@@ -919,22 +678,12 @@ proc collectCTBody(gch: var GcHeap) =
 
   when not nimCoroutines:
     gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
-  sysAssert(gch.decStack.len == 0, "collectCT")
-  prepareForInteriorPointerChecking(gch.region)
-  markStackAndRegisters(gch)
-  gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
-  inc(gch.stat.stackScans)
-  if collectZCT(gch):
-    when cycleGC:
-      if getOccupiedMem(gch.region) >= gch.cycleThreshold or alwaysCycleGC:
-        if collectCycles(gch):
-          inc(gch.stat.cycleCollections)
-          gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
-                                   CycleIncrease)
-          gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
-  unmarkStackAndRegisters(gch)
+  #gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
+  if collectALittle(gch):
+    gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
+                              CycleIncrease)
+    gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
   sysAssert(allocInv(gch.region), "collectCT: end")
-
   when withRealTime:
     let duration = getticks() - t0
     gch.stat.maxPause = max(gch.stat.maxPause, duration)
@@ -955,7 +704,7 @@ proc collectCT(gch: var GcHeap) =
     let stackMarkCosts = max(currentStackSizes() div (16*sizeof(int)), ZctThreshold)
   else:
     let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold)
-  if (gch.zct.len >= stackMarkCosts or (cycleGC and
+  if (gch.greyStack.len >= stackMarkCosts or (cycleGC and
       getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
       gch.recGcLock == 0:
     collectCTBody(gch)
@@ -969,10 +718,9 @@ when withRealTime:
 
   proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
     gch.maxPause = us.toNano
-    if (gch.zct.len >= ZctThreshold or (cycleGC and
-        getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or
-        strongAdvice:
-      collectCTBody(gch)
+    #if (getOccupiedMem(gch.region)>=gch.cycleThreshold) or
+    #    alwaysGC or strongAdvice:
+    collectCTBody(gch)
 
   proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} =
     if stackSize >= 0:
@@ -1010,12 +758,8 @@ when not defined(useNimRtl):
   proc GC_setStrategy(strategy: GC_Strategy) =
     discard
 
-  proc GC_enableMarkAndSweep() =
-    gch.cycleThreshold = InitialCycleThreshold
-
-  proc GC_disableMarkAndSweep() =
-    gch.cycleThreshold = high(gch.cycleThreshold)-1
-    # set to the max value to suppress the cycle detector
+  proc GC_enableMarkAndSweep() = discard
+  proc GC_disableMarkAndSweep() = discard
 
   proc GC_fullCollect() =
     var oldThreshold = gch.cycleThreshold
@@ -1029,17 +773,17 @@ when not defined(useNimRtl):
              "[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
              "[GC] stack scans: " & $gch.stat.stackScans & "\n" &
              "[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
-             "[GC] cycle collections: " & $gch.stat.cycleCollections & "\n" &
+             "[GC] completed collections: " & $gch.stat.completedCollections & "\n" &
              "[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
-             "[GC] zct capacity: " & $gch.zct.cap & "\n" &
+             "[GC] grey stack capacity: " & $gch.greyStack.cap & "\n" &
              "[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
-             "[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000)
+             "[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) & "\n"
     when nimCoroutines:
-      result = result & "[GC] number of stacks: " & $gch.stack.len & "\n"
+      result.add "[GC] number of stacks: " & $gch.stack.len & "\n"
       for stack in items(gch.stack):
-        result = result & "[GC]   stack " & stack.bottom.repr & "[GC]     max stack size " & $stack.maxStackSize & "\n"
+        result.add "[GC]   stack " & stack.bottom.repr & "[GC]     max stack size " & $stack.maxStackSize & "\n"
     else:
-      result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
+      result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
     GC_enable()
 
 {.pop.}
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index 220331e96..484a4db9a 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -373,12 +373,22 @@ proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) =
   ## 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``.
+  template deallocCell(x) =
+    if isCell(x):
+      # cast to PCell is correct here:
+      prepareDealloc(cast[PCell](x))
+
   if runFinalizers:
-    for x in allObjects(gch.region):
-      if isCell(x):
-        # cast to PCell is correct here:
-        var c = cast[PCell](x)
-        prepareDealloc(c)
+    when not declared(allObjectsAsProc):
+      for x in allObjects(gch.region):
+        deallocCell(x)
+    else:
+      var spaceIter: ObjectSpaceIter
+      while true:
+        let x = allObjectsAsProc(gch.region, addr spaceIter)
+        if spaceIter.state < 0: break
+        deallocCell(x)
+
   deallocOsPages(gch.region)
   zeroMem(addr gch.region, sizeof(gch.region))
   if allowGcAfterwards:
diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim
index e03140d05..cfc0dfa8a 100644
--- a/lib/system/gc_ms.nim
+++ b/lib/system/gc_ms.nim
@@ -252,7 +252,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
   if dest == nil: return # nothing to do
   if ntfNoRefs notin mt.flags:
     case mt.kind
-    of tyRef, tyString, tySequence: # leaf:
+    of tyRef, tyOptAsRef, tyString, tySequence: # leaf:
       doOperation(cast[PPointer](d)[], op)
     of tyObject, tyTuple:
       forAllSlotsAux(dest, mt.node, op)
@@ -264,13 +264,13 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
 proc forAllChildren(cell: PCell, op: WalkOp) =
   gcAssert(cell != nil, "forAllChildren: 1")
   gcAssert(cell.typ != nil, "forAllChildren: 2")
-  gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3"
+  gcAssert cell.typ.kind in {tyRef, tyOptAsRef, tySequence, tyString}, "forAllChildren: 3"
   let marker = cell.typ.marker
   if marker != nil:
     marker(cellToUsr(cell), op.int)
   else:
     case cell.typ.kind
-    of tyRef: # common case
+    of tyRef, tyOptAsRef: # common case
       forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
     of tySequence:
       var d = cast[ByteAddress](cellToUsr(cell))
@@ -285,7 +285,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
   # generates a new object and sets its reference counter to 0
   incTypeSize typ, size
   acquire(gch)
-  gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
+  gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
   collectCT(gch)
   var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
   gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
@@ -526,10 +526,10 @@ when not defined(useNimRtl):
              "[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
              "[GC] freed objects: " & $gch.stat.freedObjects & "\n"
     when nimCoroutines:
-      result = result & "[GC] number of stacks: " & $gch.stack.len & "\n"
+      result.add "[GC] number of stacks: " & $gch.stack.len & "\n"
       for stack in items(gch.stack):
-        result = result & "[GC]   stack " & stack.bottom.repr & "[GC]     max stack size " & $stack.maxStackSize & "\n"
+        result.add "[GC]   stack " & stack.bottom.repr & "[GC]     max stack size " & $stack.maxStackSize & "\n"
     else:
-      result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
+      result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
 
 {.pop.}
diff --git a/lib/system/hti.nim b/lib/system/hti.nim
index 69f4f9508..45b1d1cd3 100644
--- a/lib/system/hti.nim
+++ b/lib/system/hti.nim
@@ -62,6 +62,21 @@ type
     tyUInt16,
     tyUInt32,
     tyUInt64,
+    tyOptAsRef, tyUnused1, tyUnused2,
+    tyVarargsHidden,
+    tyUnusedHidden,
+    tyProxyHidden,
+    tyBuiltInTypeClassHidden,
+    tyUserTypeClassHidden,
+    tyUserTypeClassInstHidden,
+    tyCompositeTypeClassHidden,
+    tyInferredHidden,
+    tyAndHidden, tyOrHidden, tyNotHidden,
+    tyAnythingHidden,
+    tyStaticHidden,
+    tyFromExprHidden,
+    tyOpt,
+    tyVoidHidden
 
   TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase
   TNimNode {.codegenType.} = object
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index d2160fdac..824934966 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -564,7 +564,11 @@ else:
 
 when not declared(nimNewSeqOfCap):
   proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
-    result = newObj(typ, addInt(mulInt(cap, typ.base.size), GenericSeqSize))
+    let s = addInt(mulInt(cap, typ.base.size), GenericSeqSize)
+    when declared(newObjNoInit):
+      result = if ntfNoRefs in typ.base.flags: newObjNoInit(typ, s) else: newObj(typ, s)
+    else:
+      result = newObj(typ, s)
     cast[PGenericSeq](result).len = 0
     cast[PGenericSeq](result).reserved = cap
 
diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim
index 73bb91fef..f5b9cf3ed 100644
--- a/lib/system/nimscript.nim
+++ b/lib/system/nimscript.nim
@@ -11,6 +11,15 @@
 # Nim's configuration system now uses Nim for scripting. This module provides
 # a few things that are required for this to work.
 
+const
+  buildOS* {.magic: "BuildOS".}: string = ""
+    ## The OS this build is running on. Can be different from ``system.hostOS``
+    ## for cross compilations.
+
+  buildCPU* {.magic: "BuildCPU".}: string = ""
+    ## The CPU this build is running on. Can be different from ``system.hostCPU``
+    ## for cross compilations.
+
 template builtin = discard
 
 # We know the effects better than the compiler:
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index c3150cb07..90201202c 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -95,6 +95,9 @@ proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
   if str == nil: NimString(nil)
   else: toNimStr(str, str.len)
 
+template wasMoved(x: NimString): bool = false
+# (x.reserved and seqShallowFlag) != 0
+
 proc copyString(src: NimString): NimString {.compilerRtl.} =
   if src != nil:
     if (src.reserved and seqShallowFlag) != 0:
@@ -103,6 +106,16 @@ proc copyString(src: NimString): NimString {.compilerRtl.} =
       result = rawNewStringNoInit(src.len)
       result.len = src.len
       copyMem(addr(result.data), addr(src.data), src.len + 1)
+      sysAssert((seqShallowFlag and result.reserved) == 0, "copyString")
+      when defined(nimShallowStrings):
+        if (src.reserved and strlitFlag) != 0:
+          result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag
+
+proc newOwnedString(src: NimString; n: int): NimString =
+  result = rawNewStringNoInit(n)
+  result.len = n
+  copyMem(addr(result.data), addr(src.data), n)
+  result.data[n] = '\0'
 
 proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
   if src != nil:
@@ -116,6 +129,10 @@ proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
       result = rawNewStringNoInit(src.len)
     result.len = src.len
     copyMem(addr(result.data), addr(src.data), src.len + 1)
+    sysAssert((seqShallowFlag and result.reserved) == 0, "copyStringRC1")
+    when defined(nimShallowStrings):
+      if (src.reserved and strlitFlag) != 0:
+        result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag
 
 proc copyDeepString(src: NimString): NimString {.inline.} =
   if src != nil:
@@ -140,9 +157,12 @@ proc addChar(s: NimString, c: char): NimString =
   # is compilerproc!
   result = s
   if result.len >= result.space:
-    result.reserved = resize(result.space)
+    let r = resize(result.space)
     result = cast[NimString](growObj(result,
-      sizeof(TGenericSeq) + result.reserved + 1))
+      sizeof(TGenericSeq) + r + 1))
+    result.reserved = r
+  elif wasMoved(s):
+    result = newOwnedString(s, s.len)
   result.data[result.len] = c
   result.data[result.len+1] = '\0'
   inc(result.len)
@@ -179,7 +199,7 @@ proc addChar(s: NimString, c: char): NimString =
 #   s = rawNewString(0);
 
 proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
-  if dest.len + addlen <= dest.space:
+  if dest.len + addlen <= dest.space and not wasMoved(dest):
     result = dest
   else: # slow path:
     var sp = max(resize(dest.space), dest.len + addlen)
@@ -200,7 +220,9 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
 
 proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
   var n = max(newLen, 0)
-  if n <= s.space:
+  if wasMoved(s):
+    result = newOwnedString(s, n)
+  elif n <= s.space:
     result = s
   else:
     result = resizeString(s, n)
@@ -218,26 +240,29 @@ proc incrSeq(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
   #  seq[seq->len-1] = x;
   result = seq
   if result.len >= result.space:
-    result.reserved = resize(result.space)
-    result = cast[PGenericSeq](growObj(result, elemSize * result.reserved +
+    let r = resize(result.space)
+    result = cast[PGenericSeq](growObj(result, elemSize * r +
                                GenericSeqSize))
+    result.reserved = r
   inc(result.len)
 
 proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
   # incrSeq version 2
   result = seq
   if result.len >= result.space:
-    result.reserved = resize(result.space)
-    result = cast[PGenericSeq](growObj(result, elemSize * result.reserved +
+    let r = resize(result.space)
+    result = cast[PGenericSeq](growObj(result, elemSize * r +
                                GenericSeqSize))
+    result.reserved = r
 
 proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
     compilerRtl.} =
   result = seq
   if result.space < newLen:
-    result.reserved = max(resize(result.space), newLen)
-    result = cast[PGenericSeq](growObj(result, elemSize * result.reserved +
+    let r = max(resize(result.space), newLen)
+    result = cast[PGenericSeq](growObj(result, elemSize * r +
                                GenericSeqSize))
+    result.reserved = r
   elif newLen < result.len:
     # we need to decref here, otherwise the GC leaks!
     when not defined(boehmGC) and not defined(nogc) and
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index a7a811844..96c045e6b 100644
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -127,7 +127,8 @@ elif defined(genode):
   proc initThread(s: var SysThread,
                   stackSize: culonglong,
                   entry: GenodeThreadProc,
-                  arg: pointer) {.
+                  arg: pointer,
+                  affinity: cuint) {.
     importcpp: "#.initThread(genodeEnv, @)".}
 
   proc threadVarAlloc(): ThreadVarSlot = 0
@@ -567,6 +568,9 @@ when hostOS == "windows":
     setThreadAffinityMask(t.sys, uint(1 shl cpu))
 
 elif defined(genode):
+  var affinityOffset: cuint = 1
+  # CPU affinity offset for next thread, safe to roll-over
+
   proc createThread*[TArg](t: var Thread[TArg],
                            tp: proc (arg: TArg) {.thread, nimcall.},
                            param: TArg) =
@@ -577,7 +581,8 @@ elif defined(genode):
     when hasSharedHeap: t.stackSize = ThreadStackSize
     t.sys.initThread(
       ThreadStackSize.culonglong,
-      threadProcWrapper[TArg], addr(t))
+      threadProcWrapper[TArg], addr(t), affinityOffset)
+    inc affinityOffset
 
   proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
     {.hint: "cannot change Genode thread CPU affinity after initialization".}
diff --git a/tests/array/tarraylen.nim b/tests/array/tarraylen.nim
new file mode 100644
index 000000000..e9612de58
--- /dev/null
+++ b/tests/array/tarraylen.nim
@@ -0,0 +1,18 @@
+discard """
+  output: ""
+"""
+var a: array[0, int]
+doAssert a.len == 0
+doAssert array[0..0, int].len == 1
+doAssert array[0..0, int]([1]).len == 1
+doAssert array[1..1, int].len == 1
+doAssert array[1..1, int]([1]).len == 1
+doAssert array[2, int].len == 2
+doAssert array[2, int]([1, 2]).len == 2
+doAssert array[1..3, int].len == 3
+doAssert array[1..3, int]([1, 2, 3]).len == 3
+doAssert array[0..2, int].len == 3
+doAssert array[0..2, int]([1, 2, 3]).len == 3
+doAssert array[-2 .. -2, int].len == 1
+doAssert([1, 2, 3].len == 3)
+doAssert([42].len == 1)
\ No newline at end of file
diff --git a/tests/casestmt/tcomputedgoto.nim b/tests/casestmt/tcomputedgoto.nim
index f567174af..149072964 100644
--- a/tests/casestmt/tcomputedgoto.nim
+++ b/tests/casestmt/tcomputedgoto.nim
@@ -18,7 +18,7 @@ type
     enumA, enumB, enumC, enumD, enumE, enumLast
 
 proc vm() =
-  var instructions: array [0..100, MyEnum]
+  var instructions: array[0..100, MyEnum]
   instructions[2] = enumC
   instructions[3] = enumD
   instructions[4] = enumA
diff --git a/tests/concepts/t5642.nim b/tests/concepts/t5642.nim
index d1e7bd1dd..f08b4629b 100644
--- a/tests/concepts/t5642.nim
+++ b/tests/concepts/t5642.nim
@@ -19,7 +19,7 @@ proc nrow*(dt: DataTable) : Natural =
   return totalLen
 
 let
- stud = Students (id : @[1,2,3], name : @["Vas", "Pas", "NafNaf"], age : @[10,16,32])
+  stud = Students(id : @[1,2,3], name : @["Vas", "Pas", "NafNaf"], age : @[10,16,32])
 
 echo nrow(stud)
 
diff --git a/tests/constr/tconstr1.nim b/tests/constr/tconstr1.nim
index 28431287c..b9cf5d00b 100644
--- a/tests/constr/tconstr1.nim
+++ b/tests/constr/tconstr1.nim
@@ -14,13 +14,13 @@ type
 
 proc testSem =
   var
-    things: array [0..1, TComplexRecord] = [
+    things: array[0..1, TComplexRecord] = [
       (s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}),
       (s: "hi", x: 69, y: 45, z: 1.0, chars: {'a', 'b', 'c'})]
   write(stdout, things[0].x)
 
 const
-  things: array [0..1, TComplexRecord] = [
+  things: array[0..1, TComplexRecord] = [
     (s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}),
     (s: "hi", x: 69, y: 45, z: 1.0)] #ERROR
   otherThings = [  # the same
diff --git a/tests/constr/tconstr2.nim b/tests/constr/tconstr2.nim
index cd00681b8..b16be6c50 100644
--- a/tests/constr/tconstr2.nim
+++ b/tests/constr/tconstr2.nim
@@ -12,7 +12,7 @@ type
     chars: set[char]]
 
 const
-  things: array [0..1, TComplexRecord] = [
+  things: array[0..1, TComplexRecord] = [
     (s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}),
     (s: "hi", x: 69, y: 45, z: 1.0, chars: {})]
   otherThings = [  # the same
diff --git a/tests/js/tclosures.nim b/tests/js/tclosures.nim
index 0ec4f4743..67243c937 100644
--- a/tests/js/tclosures.nim
+++ b/tests/js/tclosures.nim
@@ -23,29 +23,29 @@ asm """
 """
 
 proc consoleprint (str:cstring): void {.importc: "print", noDecl.}
-proc print* (a: varargs[string, `$`]) = consoleprint "$1: $2" % [consolePrefix, join (a, " ")]
+proc print* (a: varargs[string, `$`]) = consoleprint "$1: $2" % [consolePrefix, join(a, " ")]
 
 type CallbackProc {.importc.} = proc () : cstring
 
 proc regCallback (fn:CallbackProc) {.importc.}
 proc runCallbacks ():cstring {.importc.}
 
-proc `*` (s:string, n:Natural) : string = s.repeat (n)
+proc `*` (s:string, n:Natural) : string = s.repeat(n)
 
 proc outer (i:Natural) : (string, int) =
-    let c = $char (random (93) + 33)
-    let n = random (40)
+    let c = $char(random(93) + 33)
+    let n = random(40)
     let s = c * n
-    proc inner () : cstring = ("[$1]" % $n) & s & " <--"
-    regCallback (inner)
+    proc inner(): cstring = ("[$1]" % $n) & s & " <--"
+    regCallback(inner)
     return (s, n)
 
 var expected = "\n"
 for i in 1 .. 10:
-    let (s, n) = outer (i)
+    let (s, n) = outer(i)
     expected &= ("($1)[$2]" % [$i, $n]) & s & " <--"
     expected &= "\n"
 
-let results = runCallbacks ()
+let results = runCallbacks()
 
 doAssert(expected == results)
diff --git a/tests/macros/tdebugstmt.nim b/tests/macros/tdebugstmt.nim
index 740ae7b05..69e610075 100644
--- a/tests/macros/tdebugstmt.nim
+++ b/tests/macros/tdebugstmt.nim
@@ -21,7 +21,7 @@ macro debug(n: varargs[untyped]): untyped =
     add(result, newCall("writeLine", newIdentNode("stdout"), n[i]))
 
 var
-  a: array [0..10, int]
+  a: array[0..10, int]
   x = "some string"
 a[0] = 42
 a[1] = 45
diff --git a/tests/macros/tvtable.nim b/tests/macros/tvtable.nim
index cc5d7a5d9..dc07a726b 100644
--- a/tests/macros/tvtable.nim
+++ b/tests/macros/tvtable.nim
@@ -58,10 +58,10 @@ proc create(T: typedesc): T =
   result.vtbl = getVTable(T)
 
 proc baseFoo(o: var TBase): int =
-  return cast[fooProc[TBase]](o.vtbl[0]) (o)
+  return cast[fooProc[TBase]](o.vtbl[0])(o)
 
 proc baseBar(o: var TBase) =
-  cast[barProc[TBase]](o.vtbl[1]) (o)
+  cast[barProc[TBase]](o.vtbl[1])(o)
 
 var a = TUserObject1.create
 var b = TUserObject2.create
diff --git a/tests/magics/tlowhigh.nim b/tests/magics/tlowhigh.nim
index 4998b73dc..60ed13c99 100644
--- a/tests/magics/tlowhigh.nim
+++ b/tests/magics/tlowhigh.nim
@@ -8,7 +8,7 @@ type
   myEnum = enum e1, e2, e3, e4, e5
 
 var
-  a: array [myEnum, int]
+  a: array[myEnum, int]
 
 for i in low(a) .. high(a):
   a[i] = 0
diff --git a/tests/method/tnildispatcher.nim b/tests/method/tnildispatcher.nim
new file mode 100644
index 000000000..017e8155f
--- /dev/null
+++ b/tests/method/tnildispatcher.nim
@@ -0,0 +1,21 @@
+discard """
+  outputsub: '''Error: unhandled exception: cannot dispatch; dispatcher is nil [NilAccessError]'''
+  exitcode: 1
+"""
+# bug #5599
+type
+    Base = ref object of RootObj
+    Derived = ref object of Base
+
+method inner(obj: Base) {.base.} =
+    quit "to override"
+
+method outer(obj: Base) {.base.} =
+    echo "outer"
+    obj.inner()
+
+method inner(obj: Derived) =
+    echo "inner Derived"
+
+var x: Derived
+x.outer()
diff --git a/tests/misc/mvarious.nim b/tests/misc/mvarious.nim
index d1587faec..8efe7c92f 100644
--- a/tests/misc/mvarious.nim
+++ b/tests/misc/mvarious.nim
@@ -1,6 +1,6 @@
 # Test a submodule
 
 #type
-#  TStringArr = array [0.. *] of string
+#  TStringArr = array[0.. *] of string
 
 proc exportme* = discard
diff --git a/tests/misc/tmandelbrot.nim b/tests/misc/tmandelbrot.nim
index e9b7a3e5a..504628313 100644
--- a/tests/misc/tmandelbrot.nim
+++ b/tests/misc/tmandelbrot.nim
@@ -20,38 +20,38 @@ proc `*` (a, b: TComplex): TComplex =
 proc abs2 (a: TComplex): float =
     return a.re * a.re + a.im * a.im
 
-var size    = parseInt (paramStr (1))
+var size    = parseInt(paramStr(1))
 var bit     = 128
 var byteAcc = 0
 
-stdout.writeLine ("P4")
-stdout.write ($size)
-stdout.write (" ")
-stdout.writeLine ($size)
+stdout.writeLine("P4")
+stdout.write($size)
+stdout.write(" ")
+stdout.writeLine($size)
 
-var fsize = float (size)
+var fsize = float(size)
 for y in 0 .. size-1:
-    var fy = 2.0 * float (y) / fsize - 1.0
+    var fy = 2.0 * float(y) / fsize - 1.0
     for x in 0 .. size-1:
         var z = (0.0, 0.0)
-        var c = (float (2*x) / fsize - 1.5, fy)
+        var c = (float(2*x) / fsize - 1.5, fy)
 
         block iter:
             for i in 0 .. 49:
                 z = z*z + c
-                if abs2 (z) >= 4.0:
+                if abs2(z) >= 4.0:
                     break iter
             byteAcc = byteAcc + bit
 
         if bit > 1:
             bit = bit div 2
         else:
-            stdout.write (chr (byteAcc))
+            stdout.write(chr(byteAcc))
             bit     = 128
             byteAcc = 0
 
     if bit != 128:
-        stdout.write (chr (byteAcc))
+        stdout.write(chr(byteAcc))
         bit     = 128
         byteAcc = 0
 
diff --git a/tests/misc/tnew.nim b/tests/misc/tnew.nim
index 88e8bd02c..89f34a621 100644
--- a/tests/misc/tnew.nim
+++ b/tests/misc/tnew.nim
@@ -9,7 +9,7 @@ type
     str: string
     le, ri: PNode
 
-  TStressTest = ref array [0..45, array [1..45, TNode]]
+  TStressTest = ref array[0..45, array[1..45, TNode]]
 
 proc finalizer(n: PNode) =
   write(stdout, n.data)
diff --git a/tests/misc/tradix.nim b/tests/misc/tradix.nim
index 36a4f39f6..07674af18 100644
--- a/tests/misc/tradix.nim
+++ b/tests/misc/tradix.nim
@@ -13,16 +13,16 @@ type
     kind: TRadixNodeKind
   TRadixNodeLinear = object of TRadixNode
     len: int8
-    keys: array [0..31, int8]
-    vals: array [0..31, PRadixNode]
+    keys: array[0..31, int8]
+    vals: array[0..31, PRadixNode]
 
   TRadixNodeFull = object of TRadixNode
-    b: array [0..255, PRadixNode]
+    b: array[0..255, PRadixNode]
   TRadixNodeLeafBits = object of TRadixNode
-    b: array [0..7, int]
+    b: array[0..7, int]
   TRadixNodeLeafLinear = object of TRadixNode
     len: int8
-    keys: array [0..31, int8]
+    keys: array[0..31, int8]
 
 var
   root: PRadixNode
diff --git a/tests/misc/tstrdesc.nim b/tests/misc/tstrdesc.nim
index d23160315..1479fcda8 100644
--- a/tests/misc/tstrdesc.nim
+++ b/tests/misc/tstrdesc.nim
@@ -1,12 +1,12 @@
 var
-  x: array [0..2, int]
+  x: array[0..2, int]
 
 x = [0, 1, 2]
 
 type
   TStringDesc {.final.} = object
     len, space: int # len and space without counting the terminating zero
-    data: array [0..0, char] # for the '\0' character
+    data: array[0..0, char] # for the '\0' character
 
 var
   emptyString {.exportc: "emptyString".}: TStringDesc
diff --git a/tests/niminaction/Chapter3/ChatApp/readme.markdown b/tests/niminaction/Chapter3/ChatApp/readme.markdown
new file mode 100644
index 000000000..200b4df1d
--- /dev/null
+++ b/tests/niminaction/Chapter3/ChatApp/readme.markdown
@@ -0,0 +1,26 @@
+# The ChatApp source code
+
+This directory contains the ChatApp project, which is the project that is
+created as part of Chapter 3 of the Nim in Action book.
+
+To compile run:
+
+```
+nim c src/client
+nim c src/server
+```
+
+You can then run the ``server`` in one terminal by executing ``./src/server``.
+
+After doing so you can execute multiple clients in different terminals and have
+them communicate via the server.
+
+To execute a client, make sure to specify the server address and user name
+on the command line:
+
+```bash
+./src/client localhost Peter
+```
+
+You should then be able to start typing in messages and sending them
+by pressing the Enter key.
\ No newline at end of file
diff --git a/tests/niminaction/Chapter3/ChatApp/src/client.nim b/tests/niminaction/Chapter3/ChatApp/src/client.nim
new file mode 100644
index 000000000..4d139655c
--- /dev/null
+++ b/tests/niminaction/Chapter3/ChatApp/src/client.nim
@@ -0,0 +1,54 @@
+import os, threadpool, asyncdispatch, asyncnet
+import protocol
+
+proc connect(socket: AsyncSocket, serverAddr: string) {.async.} =
+  ## Connects the specified AsyncSocket to the specified address.
+  ## Then receives messages from the server continuously.
+  echo("Connecting to ", serverAddr)
+  # Pause the execution of this procedure until the socket connects to
+  # the specified server.
+  await socket.connect(serverAddr, 7687.Port)
+  echo("Connected!")
+  while true:
+    # Pause the execution of this procedure until a new message is received
+    # from the server.
+    let line = await socket.recvLine()
+    # Parse the received message using ``parseMessage`` defined in the
+    # protocol module.
+    let parsed = parseMessage(line)
+    # Display the message to the user.
+    echo(parsed.username, " said ", parsed.message)
+
+echo("Chat application started")
+# Ensure that the correct amount of command line arguments was specified.
+if paramCount() < 2:
+  # Terminate the client early with an error message if there was not
+  # enough command line arguments specified by the user.
+  quit("Please specify the server address, e.g. ./client localhost username")
+
+# Retrieve the first command line argument.
+let serverAddr = paramStr(1)
+# Retrieve the second command line argument.
+let username = paramStr(2)
+# Initialise a new asynchronous socket.
+var socket = newAsyncSocket()
+
+# Execute the ``connect`` procedure in the background asynchronously.
+asyncCheck connect(socket, serverAddr)
+# Execute the ``readInput`` procedure in the background in a new thread.
+var messageFlowVar = spawn stdin.readLine()
+while true:
+  # Check if the ``readInput`` procedure returned a new line of input.
+  if messageFlowVar.isReady():
+    # If a new line of input was returned, we can safely retrieve it
+    # without blocking.
+    # The ``createMessage`` is then used to create a message based on the
+    # line of input. The message is then sent in the background asynchronously.
+    asyncCheck socket.send(createMessage(username, ^messageFlowVar))
+    # Execute the ``readInput`` procedure again, in the background in a
+    # new thread.
+    messageFlowVar = spawn stdin.readLine()
+
+  # Execute the asyncdispatch event loop, to continue the execution of
+  # asynchronous procedures.
+  asyncdispatch.poll()
diff --git a/tests/niminaction/Chapter3/ChatApp/src/client.nim.cfg b/tests/niminaction/Chapter3/ChatApp/src/client.nim.cfg
new file mode 100644
index 000000000..aed303eef
--- /dev/null
+++ b/tests/niminaction/Chapter3/ChatApp/src/client.nim.cfg
@@ -0,0 +1 @@
+--threads:on
diff --git a/tests/niminaction/Chapter3/ChatApp/src/protocol.nim b/tests/niminaction/Chapter3/ChatApp/src/protocol.nim
new file mode 100644
index 000000000..af515861c
--- /dev/null
+++ b/tests/niminaction/Chapter3/ChatApp/src/protocol.nim
@@ -0,0 +1,55 @@
+import json
+
+type
+  Message* = object
+    username*: string
+    message*: string
+
+  MessageParsingError* = object of Exception
+
+proc parseMessage*(data: string): Message {.raises: [MessageParsingError, KeyError].} =
+  var dataJson: JsonNode
+  try:
+    dataJson = parseJson(data)
+  except JsonParsingError:
+    raise newException(MessageParsingError, "Invalid JSON: " &
+                       getCurrentExceptionMsg())
+  except:
+    raise newException(MessageParsingError, "Unknown error: " &
+                       getCurrentExceptionMsg())
+
+  if not dataJson.hasKey("username"):
+    raise newException(MessageParsingError, "Username field missing")
+
+  result.username = dataJson["username"].getStr()
+  if result.username.len == 0:
+    raise newException(MessageParsingError, "Username field is empty")
+
+  if not dataJson.hasKey("message"):
+    raise newException(MessageParsingError, "Message field missing")
+  result.message = dataJson["message"].getStr()
+  if result.message.len == 0:
+    raise newException(MessageParsingError, "Message field is empty")
+
+proc createMessage*(username, message: string): string =
+  result = $(%{
+    "username": %username,
+    "message": %message
+  }) & "\c\l"
+
+when isMainModule:
+  block:
+    let data = """{"username": "dom", "message": "hello"}"""
+    let parsed = parseMessage(data)
+    doAssert parsed.message == "hello"
+    doAssert parsed.username == "dom"
+
+  # Test failure
+  block:
+    try:
+      let parsed = parseMessage("asdasd")
+    except MessageParsingError:
+      doAssert true
+    except:
+      doAssert false
+
diff --git a/tests/niminaction/Chapter3/ChatApp/src/server.nim b/tests/niminaction/Chapter3/ChatApp/src/server.nim
new file mode 100644
index 000000000..8c572aeb0
--- /dev/null
+++ b/tests/niminaction/Chapter3/ChatApp/src/server.nim
@@ -0,0 +1,84 @@
+import asyncdispatch, asyncnet
+
+type
+  Client = ref object
+    socket: AsyncSocket
+    netAddr: string
+    id: int
+    connected: bool
+
+  Server = ref object
+    socket: AsyncSocket
+    clients: seq[Client]
+
+proc newServer(): Server =
+  ## Constructor for creating a new ``Server``.
+  Server(socket: newAsyncSocket(), clients: @[])
+
+proc `$`(client: Client): string =
+  ## Converts a ``Client``'s information into a string.
+  $client.id & "(" & client.netAddr & ")"
+
+proc processMessages(server: Server, client: Client) {.async.} =
+  ## Loops while ``client`` is connected to this server, and checks
+  ## whether as message has been received from ``client``.
+  while true:
+    # Pause execution of this procedure until a line of data is received from
+    # ``client``.
+    let line = await client.socket.recvLine()
+
+    # The ``recvLine`` procedure returns ``""`` (i.e. a string of length 0)
+    # when ``client`` has disconnected.
+    if line.len == 0:
+      echo(client, " disconnected!")
+      client.connected = false
+      # When a socket disconnects it must be closed.
+      client.socket.close()
+      return
+
+    # Display the message that was sent by the client.
+    echo(client, " sent: ", line)
+
+    # Send the message to other clients.
+    for c in server.clients:
+      # Don't send it to the client that sent this or to a client that is
+      # disconnected.
+      if c.id != client.id and c.connected:
+        await c.socket.send(line & "\c\l")
+
+proc loop(server: Server, port = 7687) {.async.} =
+  ## Loops forever and checks for new connections.
+
+  # Bind the port number specified by ``port``.
+  server.socket.bindAddr(port.Port)
+  # Ready the server socket for new connections.
+  server.socket.listen()
+  echo("Listening on localhost:", port)
+
+  while true:
+    # Pause execution of this procedure until a new connection is accepted.
+    let (netAddr, clientSocket) = await server.socket.acceptAddr()
+    echo("Accepted connection from ", netAddr)
+
+    # Create a new instance of Client.
+    let client = Client(
+      socket: clientSocket,
+      netAddr: netAddr,
+      id: server.clients.len,
+      connected: true
+    )
+    # Add this new instance to the server's list of clients.
+    server.clients.add(client)
+    # Run the ``processMessages`` procedure asynchronously in the background,
+    # this procedure will continuously check for new messages from the client.
+    asyncCheck processMessages(server, client)
+
+# Check whether this module has been imported as a dependency to another
+# module, or whether this module is the main module.
+when isMainModule:
+  # Initialise a new server.
+  var server = newServer()
+  echo("Server initialised!")
+  # Execute the ``loop`` procedure. The ``waitFor`` procedure will run the
+  # asyncdispatch event loop until the ``loop`` procedure finishes executing.
+  waitFor loop(server)
\ No newline at end of file
diff --git a/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim b/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim
new file mode 100644
index 000000000..478f533d9
--- /dev/null
+++ b/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim
@@ -0,0 +1,79 @@
+# See this page for info about the format https://wikitech.wikimedia.org/wiki/Analytics/Data/Pagecounts-all-sites
+import tables, parseutils, strutils, threadpool
+
+const filename = "pagecounts-20160101-050000"
+
+type
+  Stats = ref object
+    projectName, pageTitle: string
+    requests, contentSize: int
+
+proc `$`(stats: Stats): string =
+  "(projectName: $#, pageTitle: $#, requests: $#, contentSize: $#)" % [
+    stats.projectName, stats.pageTitle, $stats.requests, $stats.contentSize
+  ]
+
+proc parse(chunk: string): Stats =
+  # Each line looks like: en Main_Page 242332 4737756101
+  result = Stats(projectName: "", pageTitle: "", requests: 0, contentSize: 0)
+
+  var projectName = ""
+  var pageTitle = ""
+  var requests = ""
+  var contentSize = ""
+  for line in chunk.splitLines:
+    var i = 0
+    projectName.setLen(0)
+    i.inc parseUntil(line, projectName, Whitespace, i)
+    i.inc skipWhitespace(line, i)
+    pageTitle.setLen(0)
+    i.inc parseUntil(line, pageTitle, Whitespace, i)
+    i.inc skipWhitespace(line, i)
+    requests.setLen(0)
+    i.inc parseUntil(line, requests, Whitespace, i)
+    i.inc skipWhitespace(line, i)
+    contentSize.setLen(0)
+    i.inc parseUntil(line, contentSize, Whitespace, i)
+    i.inc skipWhitespace(line, i)
+
+    if requests.len == 0 or contentSize.len == 0:
+      # Ignore lines with either of the params that are empty.
+      continue
+
+    let requestsInt = requests.parseInt
+    if requestsInt > result.requests and projectName == "en":
+      result = Stats(
+        projectName: projectName,
+        pageTitle: pageTitle,
+        requests: requestsInt,
+        contentSize: contentSize.parseInt
+      )
+
+proc readChunks(filename: string, chunksize = 1000000): Stats =
+  result = Stats(projectName: "", pageTitle: "", requests: 0, contentSize: 0)
+  var file = open(filename)
+  var responses = newSeq[FlowVar[Stats]]()
+  var buffer = newString(chunksize)
+  var oldBufferLen = 0
+  while not endOfFile(file):
+    let readSize = file.readChars(buffer, oldBufferLen, chunksize - oldBufferLen) + oldBufferLen
+    var chunkLen = readSize
+
+    while chunkLen >= 0 and buffer[chunkLen - 1] notin NewLines:
+      # Find where the last line ends
+      chunkLen.dec
+
+    responses.add(spawn parse(buffer[0 .. <chunkLen]))
+    oldBufferLen = readSize - chunkLen
+    buffer[0 .. <oldBufferLen] = buffer[readSize - oldBufferLen .. ^1]
+
+  for resp in responses:
+    let statistic = ^resp
+    if statistic.requests > result.requests:
+      result = statistic
+
+  file.close()
+
+
+when isMainModule:
+  echo readChunks(filename)
diff --git a/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim.cfg b/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim.cfg
new file mode 100644
index 000000000..aed303eef
--- /dev/null
+++ b/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim.cfg
@@ -0,0 +1 @@
+--threads:on
diff --git a/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim b/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim
new file mode 100644
index 000000000..8df3b6aeb
--- /dev/null
+++ b/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim
@@ -0,0 +1,64 @@
+# See this page for info about the format https://wikitech.wikimedia.org/wiki/Analytics/Data/Pagecounts-all-sites
+import tables, parseutils, strutils, threadpool, re
+
+const filename = "pagecounts-20160101-050000"
+
+type
+  Stats = ref object
+    projectName, pageTitle: string
+    requests, contentSize: int
+
+proc `$`(stats: Stats): string =
+  "(projectName: $#, pageTitle: $#, requests: $#, contentSize: $#)" % [
+    stats.projectName, stats.pageTitle, $stats.requests, $stats.contentSize
+  ]
+
+proc parse(chunk: string): Stats =
+  # Each line looks like: en Main_Page 242332 4737756101
+  result = Stats(projectName: "", pageTitle: "", requests: 0, contentSize: 0)
+
+  var matches: array[4, string]
+  var reg = re"([^\s]+)\s([^\s]+)\s(\d+)\s(\d+)"
+  for line in chunk.splitLines:
+
+    let start = find(line, reg, matches)
+    if start == -1: continue
+
+    let requestsInt = matches[2].parseInt
+    if requestsInt > result.requests and matches[0] == "en":
+      result = Stats(
+        projectName: matches[0],
+        pageTitle: matches[1],
+        requests: requestsInt,
+        contentSize: matches[3].parseInt
+      )
+
+proc readChunks(filename: string, chunksize = 1000000): Stats =
+  result = Stats(projectName: "", pageTitle: "", requests: 0, contentSize: 0)
+  var file = open(filename)
+  var responses = newSeq[FlowVar[Stats]]()
+  var buffer = newString(chunksize)
+  var oldBufferLen = 0
+  while not endOfFile(file):
+    let readSize = file.readChars(buffer, oldBufferLen, chunksize - oldBufferLen) + oldBufferLen
+    var chunkLen = readSize
+
+    while chunkLen >= 0 and buffer[chunkLen - 1] notin NewLines:
+      # Find where the last line ends
+      chunkLen.dec
+
+    responses.add(spawn parse(buffer[0 .. <chunkLen]))
+    oldBufferLen = readSize - chunkLen
+    buffer[0 .. <oldBufferLen] = buffer[readSize - oldBufferLen .. ^1]
+
+  echo("Spawns: ", responses.len)
+  for resp in responses:
+    let statistic = ^resp
+    if statistic.requests > result.requests:
+      result = statistic
+
+  file.close()
+
+
+when isMainModule:
+  echo readChunks(filename)
diff --git a/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim.cfg b/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim.cfg
new file mode 100644
index 000000000..aed303eef
--- /dev/null
+++ b/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim.cfg
@@ -0,0 +1 @@
+--threads:on
diff --git a/tests/niminaction/Chapter6/WikipediaStats/naive.nim b/tests/niminaction/Chapter6/WikipediaStats/naive.nim
new file mode 100644
index 000000000..ed4fba8e2
--- /dev/null
+++ b/tests/niminaction/Chapter6/WikipediaStats/naive.nim
@@ -0,0 +1,29 @@
+# See this page for info about the format https://wikitech.wikimedia.org/wiki/Analytics/Data/Pagecounts-all-sites
+import tables, parseutils, strutils
+
+const filename = "pagecounts-20150101-050000"
+
+proc parse(filename: string): tuple[projectName, pageTitle: string,
+    requests, contentSize: int] =
+  # Each line looks like: en Main_Page 242332 4737756101
+  var file = open(filename)
+  for line in file.lines:
+    var i = 0
+    var projectName = ""
+    i.inc parseUntil(line, projectName, Whitespace, i)
+    i.inc
+    var pageTitle = ""
+    i.inc parseUntil(line, pageTitle, Whitespace, i)
+    i.inc
+    var requests = 0
+    i.inc parseInt(line, requests, i)
+    i.inc
+    var contentSize = 0
+    i.inc parseInt(line, contentSize, i)
+    if requests > result[2] and projectName == "en":
+      result = (projectName, pageTitle, requests, contentSize)
+
+  file.close()
+
+when isMainModule:
+  echo parse(filename)
diff --git a/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim b/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim
new file mode 100644
index 000000000..7181145e9
--- /dev/null
+++ b/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim
@@ -0,0 +1,72 @@
+import os, parseutils, threadpool, strutils
+
+type
+  Stats = ref object
+    domainCode, pageTitle: string
+    countViews, totalSize: int
+
+proc newStats(): Stats =
+  Stats(domainCode: "", pageTitle: "", countViews: 0, totalSize: 0)
+
+proc `$`(stats: Stats): string =
+  "(domainCode: $#, pageTitle: $#, countViews: $#, totalSize: $#)" % [
+    stats.domainCode, stats.pageTitle, $stats.countViews, $stats.totalSize
+  ]
+
+proc parse(line: string, domainCode, pageTitle: var string,
+    countViews, totalSize: var int) =
+  if line.len == 0: return
+  var i = 0
+  domainCode.setLen(0)
+  i.inc parseUntil(line, domainCode, {' '}, i)
+  i.inc
+  pageTitle.setLen(0)
+  i.inc parseUntil(line, pageTitle, {' '}, i)
+  i.inc
+  countViews = 0
+  i.inc parseInt(line, countViews, i)
+  i.inc
+  totalSize = 0
+  i.inc parseInt(line, totalSize, i)
+
+proc parseChunk(chunk: string): Stats =
+  result = newStats()
+  var domainCode = ""
+  var pageTitle = ""
+  var countViews = 0
+  var totalSize = 0
+  for line in splitLines(chunk):
+    parse(line, domainCode, pageTitle, countViews, totalSize)
+    if domainCode == "en" and countViews > result.countViews:
+      result = Stats(domainCode: domainCode, pageTitle: pageTitle,
+                     countViews: countViews, totalSize: totalSize)
+
+proc readPageCounts(filename: string, chunkSize = 1_000_000) =
+  var file = open(filename)
+  var responses = newSeq[FlowVar[Stats]]()
+  var buffer = newString(chunksize)
+  var oldBufferLen = 0
+  while not endOfFile(file):
+    let reqSize = chunksize - oldBufferLen
+    let readSize = file.readChars(buffer, oldBufferLen, reqSize) + oldBufferLen
+    var chunkLen = readSize
+
+    while chunkLen >= 0 and buffer[chunkLen - 1] notin NewLines:
+      chunkLen.dec
+
+    responses.add(spawn parseChunk(buffer[0 .. <chunkLen]))
+    oldBufferLen = readSize - chunkLen
+    buffer[0 .. <oldBufferLen] = buffer[readSize - oldBufferLen .. ^1]
+
+  var mostPopular = newStats()
+  for resp in responses:
+    let statistic = ^resp
+    if statistic.countViews > mostPopular.countViews:
+      mostPopular = statistic
+
+  echo("Most popular is: ", mostPopular)
+
+when isMainModule:
+  const file = "pagecounts-20160101-050000"
+  let filename = getCurrentDir() / file
+  readPageCounts(filename)
\ No newline at end of file
diff --git a/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim.cfg b/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim.cfg
new file mode 100644
index 000000000..9d57ecf93
--- /dev/null
+++ b/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim.cfg
@@ -0,0 +1 @@
+--threads:on
\ No newline at end of file
diff --git a/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim b/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim
new file mode 100644
index 000000000..c62b2f93e
--- /dev/null
+++ b/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim
@@ -0,0 +1,13 @@
+import threadpool
+
+var counter = 0
+
+proc increment(x: int) =
+  for i in 0 .. <x:
+    let value = counter + 1
+    counter = value
+
+spawn increment(10_000)
+spawn increment(10_000)
+sync()
+echo(counter)
\ No newline at end of file
diff --git a/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim.cfg b/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim.cfg
new file mode 100644
index 000000000..9d57ecf93
--- /dev/null
+++ b/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim.cfg
@@ -0,0 +1 @@
+--threads:on
\ No newline at end of file
diff --git a/tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim b/tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim
new file mode 100644
index 000000000..25ad7d5f4
--- /dev/null
+++ b/tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim
@@ -0,0 +1,34 @@
+import os, parseutils
+
+proc parse(line: string, domainCode, pageTitle: var string,
+    countViews, totalSize: var int) =
+  var i = 0
+  domainCode.setLen(0)
+  i.inc parseUntil(line, domainCode, {' '}, i)
+  i.inc
+  pageTitle.setLen(0)
+  i.inc parseUntil(line, pageTitle, {' '}, i)
+  i.inc
+  countViews = 0
+  i.inc parseInt(line, countViews, i)
+  i.inc
+  totalSize = 0
+  i.inc parseInt(line, totalSize, i)
+
+proc readPageCounts(filename: string) =
+  var domainCode = ""
+  var pageTitle = ""
+  var countViews = 0
+  var totalSize = 0
+  var mostPopular = ("", "", 0, 0)
+  for line in filename.lines:
+    parse(line, domainCode, pageTitle, countViews, totalSize)
+    if domainCode == "en" and countViews > mostPopular[2]:
+      mostPopular = (domainCode, pageTitle, countViews, totalSize)
+
+  echo("Most popular is: ", mostPopular)
+
+when isMainModule:
+  const file = "pagecounts-20160101-050000"
+  let filename = getCurrentDir() / file
+  readPageCounts(filename)
\ No newline at end of file
diff --git a/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim b/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim
new file mode 100644
index 000000000..72e8bff12
--- /dev/null
+++ b/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim
@@ -0,0 +1,15 @@
+import threadpool, locks
+
+var counterLock: Lock
+initLock(counterLock)
+var counter {.guard: counterLock.} = 0
+
+proc increment(x: int) =
+  for i in 0 .. <x:
+    let value = counter + 1
+    counter = value
+
+spawn increment(10_000)
+spawn increment(10_000)
+sync()
+echo(counter)
diff --git a/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim.cfg b/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim.cfg
new file mode 100644
index 000000000..9d57ecf93
--- /dev/null
+++ b/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim.cfg
@@ -0,0 +1 @@
+--threads:on
\ No newline at end of file
diff --git a/tests/niminaction/Chapter7/Tweeter/Tweeter.nimble b/tests/niminaction/Chapter7/Tweeter/Tweeter.nimble
new file mode 100644
index 000000000..0a0ffad1a
--- /dev/null
+++ b/tests/niminaction/Chapter7/Tweeter/Tweeter.nimble
@@ -0,0 +1,14 @@
+# Package
+
+version       = "0.1.0"
+author        = "Dominik Picheta"
+description   = "A simple Twitter clone developed in Nim in Action."
+license       = "MIT"
+
+bin = @["tweeter"]
+skipExt = @["nim"]
+
+# Dependencies
+
+requires "nim >= 0.13.1"
+requires "jester >= 0.0.1"
diff --git a/tests/niminaction/Chapter7/Tweeter/public/style.css b/tests/niminaction/Chapter7/Tweeter/public/style.css
new file mode 100644
index 000000000..baacfaf9d
--- /dev/null
+++ b/tests/niminaction/Chapter7/Tweeter/public/style.css
@@ -0,0 +1,117 @@
+body {
+  background-color: #f1f9ea;
+  margin: 0;
+  font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
+}
+
+div#main {
+  width: 80%;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+div#user {
+  background-color: #66ac32;
+  width: 100%;
+  color: #c7f0aa;
+  padding: 5pt;
+}
+
+div#user > h1 {
+  color: #ffffff;
+}
+
+h1 {
+  margin: 0;
+  display: inline;
+  padding-left: 10pt;
+  padding-right: 10pt;
+}
+
+div#user > form {
+  float: right;
+  margin-right: 10pt;
+}
+
+div#user > form > input[type="submit"] {
+  border: 0px none;
+  padding: 5pt;
+  font-size: 108%;
+  color: #ffffff;
+  background-color: #515d47;
+  border-radius: 5px;
+  cursor: pointer;
+}
+
+div#user > form > input[type="submit"]:hover {
+  background-color: #538c29;
+}
+
+
+div#messages {
+  background-color: #a2dc78;
+  width: 90%;
+  margin-left: auto;
+  margin-right: auto;
+  color: #1a1a1a;
+}
+
+div#messages > div {
+  border-left: 1px solid #869979;
+  border-right: 1px solid #869979;
+  border-bottom: 1px solid #869979;
+  padding: 5pt;
+}
+
+div#messages > div > a, div#messages > div > span {
+  color: #475340;
+}
+
+div#messages > div > a:hover {
+  text-decoration: none;
+  color: #c13746;
+}
+
+h3 {
+  margin-bottom: 0;
+  font-weight: normal;
+}
+
+div#login {
+  width: 200px;
+  margin-left: auto;
+  margin-right: auto;
+  margin-top: 20%;
+
+  font-size: 130%;
+}
+
+div#login span.small {
+  display: block;
+  font-size: 56%;
+}
+
+div#newMessage {
+  background-color: #538c29;
+  width: 90%;
+  margin-left: auto;
+  margin-right: auto;
+  color: #ffffff;
+  padding: 5pt;
+}
+
+div#newMessage span {
+  padding-right: 5pt;
+}
+
+div#newMessage form {
+  display: inline;
+}
+
+div#newMessage > form > input[type="text"] {
+  width: 80%;
+}
+
+div#newMessage > form > input[type="submit"] {
+  font-size: 80%;
+}
diff --git a/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim b/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim
new file mode 100644
index 000000000..c7aee1b44
--- /dev/null
+++ b/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim
@@ -0,0 +1,6 @@
+import database
+
+var db = newDatabase()
+db.setup()
+echo("Database created successfully!")
+db.close()
\ No newline at end of file
diff --git a/tests/niminaction/Chapter7/Tweeter/src/database.nim b/tests/niminaction/Chapter7/Tweeter/src/database.nim
new file mode 100644
index 000000000..4faba3f6a
--- /dev/null
+++ b/tests/niminaction/Chapter7/Tweeter/src/database.nim
@@ -0,0 +1,93 @@
+import times, db_sqlite, strutils #<1>
+type #<2>
+  Database* = ref object
+    db*: DbConn
+
+  User* = object #<3>
+    username*: string #<4>
+    following*: seq[string] #<5>
+
+  Message* = object #<6>
+    username*: string #<7>
+    time*: Time #<8>
+    msg*: string #<9>
+
+proc newDatabase*(filename = "tweeter.db"): Database =
+  new result
+  result.db = open(filename, "", "", "")
+
+proc close*(database: Database) =
+ database.db.close()
+
+proc setup*(database: Database) =
+  database.db.exec(sql"""
+    CREATE TABLE IF NOT EXISTS User(
+      username text PRIMARY KEY
+    );
+  """)
+
+  database.db.exec(sql"""
+    CREATE TABLE IF NOT EXISTS Following(
+      follower text,
+      followed_user text,
+      PRIMARY KEY (follower, followed_user),
+      FOREIGN KEY (follower) REFERENCES User(username),
+      FOREIGN KEY (followed_user) REFERENCES User(username)
+    );
+  """)
+
+  database.db.exec(sql"""
+    CREATE TABLE IF NOT EXISTS Message(
+      username text,
+      time integer,
+      msg text NOT NULL,
+      FOREIGN KEY (username) REFERENCES User(username)
+    );
+  """)
+
+proc post*(database: Database, message: Message) =
+  if message.msg.len > 140: #<1>
+    raise newException(ValueError, "Message has to be less than 140 characters.")
+
+  database.db.exec(sql"INSERT INTO Message VALUES (?, ?, ?);", #<2>
+    message.username, $message.time.toSeconds().int, message.msg) #<3>
+
+proc follow*(database: Database, follower: User, user: User) =
+  database.db.exec(sql"INSERT INTO Following VALUES (?, ?);",#<2>
+    follower.username, user.username)
+
+proc create*(database: Database, user: User) =
+  database.db.exec(sql"INSERT INTO User VALUES (?);", user.username) #<2>
+
+proc findUser*(database: Database, username: string, user: var User): bool =
+  let row = database.db.getRow(
+      sql"SELECT username FROM User WHERE username = ?;", username)
+  if row[0].len == 0: return false
+  else: user.username = row[0]
+
+  let following = database.db.getAllRows(
+      sql"SELECT followed_user FROM Following WHERE follower = ?;", username)
+  user.following = @[]
+  for row in following:
+    if row[0].len != 0:
+      user.following.add(row[0])
+
+  return true
+
+proc findMessages*(database: Database, usernames: seq[string],
+    limit = 10): seq[Message] =
+  result = @[]
+  if usernames.len == 0: return
+  var whereClause = " WHERE "
+  for i in 0 .. <usernames.len:
+    whereClause.add("username = ? ")
+    if i != <usernames.len:
+      whereClause.add("or ")
+
+  let messages = database.db.getAllRows(
+      sql("SELECT username, time, msg FROM Message" &
+          whereClause &
+          "ORDER BY time DESC LIMIT " & $limit),
+      usernames)
+  for row in messages:
+    result.add(Message(username: row[0], time: fromSeconds(row[1].parseInt), msg: row[2]))
diff --git a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim
new file mode 100644
index 000000000..b8a36306e
--- /dev/null
+++ b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim
@@ -0,0 +1,62 @@
+import asyncdispatch, times
+
+import jester
+
+import database, views/user, views/general
+
+proc userLogin(db: Database, request: Request, user: var User): bool =
+  if request.cookies.hasKey("username"):
+    if not db.findUser(request.cookies["username"], user):
+      user = User(username: request.cookies["username"], following: @[])
+      db.create(user)
+    return true
+  else:
+    return false
+
+let db = newDatabase()
+routes:
+  get "/":
+    var user: User
+    if db.userLogin(request, user):
+      let messages = db.findMessages(user.following & user.username)
+      resp renderMain(renderTimeline(user.username, messages))
+    else:
+      resp renderMain(renderLogin())
+
+  get "/@name":
+    cond '.' notin @"name"
+    var user: User
+    if not db.findUser(@"name", user):
+      halt "User not found"
+    let messages = db.findMessages(@[user.username])
+
+    var currentUser: User
+    if db.userLogin(request, currentUser):
+      resp renderMain(renderUser(user, currentUser) & renderMessages(messages))
+    else:
+      resp renderMain(renderUser(user) & renderMessages(messages))
+
+  post "/follow":
+    var follower: User
+    var target: User
+    if not db.findUser(@"follower", follower):
+      halt "Follower not found"
+    if not db.findUser(@"target", target):
+      halt "Follow target not found"
+    db.follow(follower, target)
+    redirect(uri("/" & @"target"))
+
+  post "/login":
+    setCookie("username", @"username", getTime().getGMTime() + 2.hours)
+    redirect("/")
+
+  post "/createMessage":
+    let message = Message(
+      username: @"username",
+      time: getTime(),
+      msg: @"message"
+    )
+    db.post(message)
+    redirect("/")
+
+runForever()
diff --git a/tests/niminaction/Chapter7/Tweeter/src/views/general.nim b/tests/niminaction/Chapter7/Tweeter/src/views/general.nim
new file mode 100644
index 000000000..0e920b1de
--- /dev/null
+++ b/tests/niminaction/Chapter7/Tweeter/src/views/general.nim
@@ -0,0 +1,51 @@
+#? stdtmpl(subsChar = '$', metaChar = '#')
+#import "../database"
+#import user
+#import xmltree
+#
+#proc `$!`(text: string): string = escape(text)
+#end proc
+#
+#proc renderMain*(body: string): string =
+#  result = ""
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Tweeter written in Nim</title>
+    <link rel="stylesheet" type="text/css" href="style.css">
+  </head>
+
+  <body>
+    ${body}
+  </body>
+
+</html>
+#end proc
+#
+#proc renderLogin*(): string =
+#  result = ""
+<div id="login">
+  <span>Login</span>
+  <span class="small">Please type in your username...</span>
+  <form action="login" method="post">
+    <input type="text" name="username">
+    <input type="submit" value="Login">
+  </form>
+</div>
+#end proc
+#
+#proc renderTimeline*(username: string, messages: seq[Message]): string =
+#  result = ""
+<div id="user">
+  <h1>${$!username}'s timeline</h1>
+</div>
+<div id="newMessage">
+  <span>New message</span>
+  <form action="createMessage" method="post">
+    <input type="text" name="message">
+    <input type="hidden" name="username" value="${$!username}">
+    <input type="submit" value="Tweet">
+  </form>
+</div>
+${renderMessages(messages)}
+#end proc
\ No newline at end of file
diff --git a/tests/niminaction/Chapter7/Tweeter/src/views/user.nim b/tests/niminaction/Chapter7/Tweeter/src/views/user.nim
new file mode 100644
index 000000000..f3791b493
--- /dev/null
+++ b/tests/niminaction/Chapter7/Tweeter/src/views/user.nim
@@ -0,0 +1,49 @@
+#? stdtmpl(subsChar = '$', metaChar = '#', toString = "xmltree.escape")
+#import "../database"
+#import xmltree
+#import times
+#
+#proc renderUser*(user: User): string =
+#  result = ""
+<div id="user">
+  <h1>${user.username}</h1>
+  <span>Following: ${$user.following.len}</span>
+</div>
+#end proc
+#
+#proc renderUser*(user: User, currentUser: User): string =
+#  result = ""
+<div id="user">
+  <h1>${user.username}</h1>
+  <span>Following: ${$user.following.len}</span>
+  #if user.username notin currentUser.following:
+  <form action="follow" method="post">
+    <input type="hidden" name="follower" value="${currentUser.username}">
+    <input type="hidden" name="target" value="${user.username}">
+    <input type="submit" value="Follow">
+  </form>
+  #end if
+</div>
+#
+#end proc
+#
+#proc renderMessages*(messages: seq[Message]): string =
+#  result = ""
+<div id="messages">
+  #for message in messages:
+    <div>
+      <a href="/${message.username}">${message.username}</a>
+      <span>${message.time.getGMTime().format("HH:mm MMMM d',' yyyy")}</span>
+      <h3>${message.msg}</h3>
+    </div>
+  #end for
+</div>
+#end proc
+#
+#when isMainModule:
+#  echo renderUser(User(username: "d0m96<>", following: @[]))
+#  echo renderMessages(@[
+#    Message(username: "d0m96", time: getTime(), msg: "Hello World!"),
+#    Message(username: "d0m96", time: getTime(), msg: "Testing")
+#  ])
+#end when
diff --git a/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim
new file mode 100644
index 000000000..926ca452c
--- /dev/null
+++ b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim
@@ -0,0 +1,28 @@
+import database, os, times
+
+when isMainModule:
+  removeFile("tweeter_test.db")
+  var db = newDatabase("tweeter_test.db")
+  db.setup()
+
+  db.create(User(username: "d0m96"))
+  db.create(User(username: "nim_lang"))
+
+  db.post(Message(username: "nim_lang", time: getTime() - 4.seconds,
+      msg: "Hello Nim in Action readers"))
+  db.post(Message(username: "nim_lang", time: getTime(),
+      msg: "99.9% off Nim in Action for everyone, for the next minute only!"))
+
+  var dom: User
+  doAssert db.findUser("d0m96", dom)
+  var nim: User
+  doAssert db.findUser("nim_lang", nim)
+  db.follow(dom, nim)
+
+  doAssert db.findUser("d0m96", dom)
+
+  let messages = db.findMessages(dom.following)
+  echo(messages)
+  doAssert(messages[0].msg == "99.9% off Nim in Action for everyone, for the next minute only!")
+  doAssert(messages[1].msg == "Hello Nim in Action readers")
+  echo("All tests finished successfully!")
diff --git a/tests/niminaction/Chapter7/Tweeter/tests/database_test.nims b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nims
new file mode 100644
index 000000000..226905fbf
--- /dev/null
+++ b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nims
@@ -0,0 +1,2 @@
+--path:"../src"
+#switch("path", "./src")
diff --git a/tests/niminaction/Chapter8/canvas/canvas.nim b/tests/niminaction/Chapter8/canvas/canvas.nim
new file mode 100644
index 000000000..713d1e9e2
--- /dev/null
+++ b/tests/niminaction/Chapter8/canvas/canvas.nim
@@ -0,0 +1,19 @@
+import dom
+
+type
+  CanvasRenderingContext* = ref object
+    fillStyle* {.importc.}: cstring
+    strokeStyle* {.importc.}: cstring
+
+{.push importcpp.}
+
+proc getContext*(canvasElement: Element,
+    contextType: cstring): CanvasRenderingContext
+
+proc fillRect*(context: CanvasRenderingContext, x, y, width, height: int)
+
+proc moveTo*(context: CanvasRenderingContext, x, y: int)
+
+proc lineTo*(context: CanvasRenderingContext, x, y: int)
+
+proc stroke*(context: CanvasRenderingContext)
diff --git a/tests/niminaction/Chapter8/canvas/canvas_test.nim b/tests/niminaction/Chapter8/canvas/canvas_test.nim
new file mode 100644
index 000000000..42d222b7b
--- /dev/null
+++ b/tests/niminaction/Chapter8/canvas/canvas_test.nim
@@ -0,0 +1,19 @@
+import canvas, dom
+
+proc onLoad() {.exportc.} =
+  var canvas = document.getElementById("canvas").EmbedElement
+  canvas.width = window.innerWidth
+  canvas.height = window.innerHeight
+  var ctx = canvas.getContext("2d")
+
+  ctx.fillStyle = "#1d4099"
+  ctx.fillRect(0, 0, window.innerWidth, window.innerHeight)
+
+  ctx.strokeStyle = "#ffffff"
+  let letterWidth = 100
+  let letterLeftPos = (window.innerWidth div 2) - (letterWidth div 2)
+  ctx.moveTo(letterLeftPos, 320)
+  ctx.lineTo(letterLeftPos, 110)
+  ctx.lineTo(letterLeftPos + letterWidth, 320)
+  ctx.lineTo(letterLeftPos + letterWidth, 110)
+  ctx.stroke()
diff --git a/tests/niminaction/Chapter8/sdl/sdl.nim b/tests/niminaction/Chapter8/sdl/sdl.nim
new file mode 100644
index 000000000..a1b30281b
--- /dev/null
+++ b/tests/niminaction/Chapter8/sdl/sdl.nim
@@ -0,0 +1,34 @@
+when defined(Windows):
+  const libName* = "SDL2.dll"
+elif defined(Linux):
+  const libName* = "libSDL2.so"
+elif defined(MacOsX):
+  const libName* = "libSDL2.dylib"
+
+type
+  SdlWindow = object
+  SdlWindowPtr* = ptr SdlWindow
+  SdlRenderer = object
+  SdlRendererPtr* = ptr SdlRenderer
+
+const INIT_VIDEO* = 0x00000020
+
+{.push dynlib: libName.}
+proc init*(flags: uint32): cint {.importc: "SDL_Init".}
+
+proc createWindowAndRenderer*(width, height: cint, window_flags: cuint,
+    window: var SdlWindowPtr, renderer: var SdlRendererPtr): cint
+    {.importc: "SDL_CreateWindowAndRenderer".}
+
+proc pollEvent*(event: pointer): cint {.importc: "SDL_PollEvent".}
+
+proc setDrawColor*(renderer: SdlRendererPtr, r, g, b, a: uint8): cint
+    {.importc: "SDL_SetRenderDrawColor", discardable.}
+
+proc present*(renderer: SdlRendererPtr) {.importc: "SDL_RenderPresent".}
+
+proc clear*(renderer: SdlRendererPtr) {.importc: "SDL_RenderClear".}
+
+proc drawLines*(renderer: SdlRendererPtr, points: ptr tuple[x, y: cint],
+    count: cint): cint {.importc: "SDL_RenderDrawLines", discardable.}
+{.pop.}
diff --git a/tests/niminaction/Chapter8/sdl/sdl_test.nim b/tests/niminaction/Chapter8/sdl/sdl_test.nim
new file mode 100644
index 000000000..a572d5231
--- /dev/null
+++ b/tests/niminaction/Chapter8/sdl/sdl_test.nim
@@ -0,0 +1,25 @@
+import os
+import sdl
+
+if sdl.init(INIT_VIDEO) == -1:
+  quit("Couldn't initialise SDL")
+
+var window: SdlWindowPtr
+var renderer: SdlRendererPtr
+if createWindowAndRenderer(640, 480, 0, window, renderer) == -1:
+  quit("Couldn't create a window or renderer")
+
+discard pollEvent(nil)
+renderer.setDrawColor 29, 64, 153, 255
+renderer.clear
+renderer.setDrawColor 255, 255, 255, 255
+var points = [
+  (260'i32, 320'i32),
+  (260'i32, 110'i32),
+  (360'i32, 320'i32),
+  (360'i32, 110'i32)
+]
+renderer.drawLines(addr points[0], points.len.cint)
+
+renderer.present
+sleep(5000)
diff --git a/tests/niminaction/Chapter8/sfml/sfml.nim b/tests/niminaction/Chapter8/sfml/sfml.nim
new file mode 100644
index 000000000..fea85fcd4
--- /dev/null
+++ b/tests/niminaction/Chapter8/sfml/sfml.nim
@@ -0,0 +1,26 @@
+{.passL: "-lsfml-graphics -lsfml-system -lsfml-window".}
+
+type
+  VideoMode* {.importcpp: "sf::VideoMode".} = object
+  RenderWindowObj {.importcpp: "sf::RenderWindow".} = object
+  RenderWindow* = ptr RenderWindowObj
+  Color* {.importcpp: "sf::Color".} = object
+  Event* {.importcpp: "sf::Event".} = object
+
+{.push cdecl, header: "<SFML/Graphics.hpp>".}
+
+proc videoMode*(modeWidth, modeHeight: cuint, modeBitsPerPixel: cuint = 32): VideoMode
+    {.importcpp: "sf::VideoMode(@)", constructor.}
+
+proc newRenderWindow*(mode: VideoMode, title: cstring): RenderWindow
+    {.importcpp: "new sf::RenderWindow(@)", constructor.}
+
+proc pollEvent*(window: RenderWindow, event: var Event): bool
+    {.importcpp: "#.pollEvent(@)".}
+
+proc newColor*(red, green, blue, alpha: uint8): Color
+    {.importcpp: "sf::Color(@)", constructor.}
+
+proc clear*(window: RenderWindow, color: Color) {.importcpp: "#.clear(@)".}
+
+proc display*(window: RenderWindow) {.importcpp: "#.display()".}
diff --git a/tests/niminaction/Chapter8/sfml/sfml_test.nim b/tests/niminaction/Chapter8/sfml/sfml_test.nim
new file mode 100644
index 000000000..49a8176e5
--- /dev/null
+++ b/tests/niminaction/Chapter8/sfml/sfml_test.nim
@@ -0,0 +1,9 @@
+import sfml, os
+var window = newRenderWindow(videoMode(800, 600), "SFML works!")
+
+var event: Event
+discard window.pollEvent(event)
+window.clear(newColor(29, 64, 153, 255))
+window.display()
+
+sleep(1000)
diff --git a/tests/niminaction/Chapter9/configurator/configurator.nim b/tests/niminaction/Chapter9/configurator/configurator.nim
new file mode 100644
index 000000000..0d5627889
--- /dev/null
+++ b/tests/niminaction/Chapter9/configurator/configurator.nim
@@ -0,0 +1,84 @@
+import macros
+
+proc createRefType(ident: NimIdent, identDefs: seq[NimNode]): NimNode =
+  result = newTree(nnkTypeSection,
+    newTree(nnkTypeDef,
+      newIdentNode(ident),
+      newEmptyNode(),
+      newTree(nnkRefTy,
+        newTree(nnkObjectTy,
+          newEmptyNode(),
+          newEmptyNode(),
+          newTree(nnkRecList,
+            identDefs
+          )
+        )
+      )
+    )
+  )
+
+proc toIdentDefs(stmtList: NimNode): seq[NimNode] =
+  expectKind(stmtList, nnkStmtList)
+  result = @[]
+
+  for child in stmtList:
+    expectKind(child, nnkCall)
+    result.add(newIdentDefs(child[0], child[1][0]))
+
+template constructor(ident: untyped): untyped =
+  proc `new ident`(): `ident` =
+    new result
+
+proc createLoadProc(typeName: NimIdent, identDefs: seq[NimNode]): NimNode =
+  var cfgIdent = newIdentNode("cfg")
+  var filenameIdent = newIdentNode("filename")
+  var objIdent = newIdentNode("obj")
+
+  var body = newStmtList()
+  body.add quote do:
+    var `objIdent` = parseFile(`filenameIdent`)
+
+  for identDef in identDefs:
+    let fieldNameIdent = identDef[0]
+    let fieldName = $fieldNameIdent.ident
+    case $identDef[1].ident
+    of "string":
+      body.add quote do:
+        `cfgIdent`.`fieldNameIdent` = `objIdent`[`fieldName`].getStr
+    of "int":
+      body.add quote do:
+        `cfgIdent`.`fieldNameIdent` = `objIdent`[`fieldName`].getNum().int
+    else:
+      doAssert(false, "Not Implemented")
+
+  return newProc(newIdentNode("load"),
+    [newEmptyNode(),
+     newIdentDefs(cfgIdent, newIdentNode(typeName)),
+     newIdentDefs(filenameIdent, newIdentNode("string"))],
+    body)
+
+macro config*(typeName: untyped, fields: untyped): untyped =
+  result = newStmtList()
+
+  let identDefs = toIdentDefs(fields)
+  result.add createRefType(typeName.ident, identDefs)
+  result.add getAst(constructor(typeName.ident))
+  result.add createLoadProc(typeName.ident, identDefs)
+
+  echo treeRepr(typeName)
+  echo treeRepr(fields)
+
+  echo treeRepr(result)
+  echo toStrLit(result)
+  # TODO: Verify that we can export fields in config type so that it can be
+  # used in another module.
+
+import json
+config MyAppConfig:
+  address: string
+  port: int
+
+var myConf = newMyAppConfig()
+myConf.load("myappconfig.cfg")
+echo("Address: ", myConf.address)
+echo("Port: ", myConf.port)
diff --git a/tests/stdlib/mjsonexternproc.nim b/tests/stdlib/mjsonexternproc.nim
new file mode 100644
index 000000000..2967a1168
--- /dev/null
+++ b/tests/stdlib/mjsonexternproc.nim
@@ -0,0 +1,10 @@
+# Test case for https://github.com/nim-lang/Nim/issues/6385
+
+import json
+# export json
+
+proc foo*[T](a: T) =
+  let params = %*{
+    "data": [ 1 ]
+  }
+  echo $params
\ No newline at end of file
diff --git a/tests/stdlib/tjsonexternproc.nim b/tests/stdlib/tjsonexternproc.nim
new file mode 100644
index 000000000..ec90e580d
--- /dev/null
+++ b/tests/stdlib/tjsonexternproc.nim
@@ -0,0 +1,5 @@
+# Test case for https://github.com/nim-lang/Nim/issues/6385
+
+import mjsonexternproc
+# import json
+foo(1)
\ No newline at end of file
diff --git a/tests/stdlib/tmath2.nim b/tests/stdlib/tmath2.nim
index 84a49efa9..eb0506f5f 100644
--- a/tests/stdlib/tmath2.nim
+++ b/tests/stdlib/tmath2.nim
@@ -64,7 +64,7 @@ proc TestLoops() =
 
 var
   glob: int
-  a: array [0..5, int]
+  a: array[0..5, int]
 
 proc main() =
   #glob = 0
diff --git a/tests/stdlib/trepr2.nim b/tests/stdlib/trepr2.nim
index a92024851..300df565d 100644
--- a/tests/stdlib/trepr2.nim
+++ b/tests/stdlib/trepr2.nim
@@ -6,7 +6,7 @@ type
 
   TPoint {.final.} = object
     x, y, z: int
-    s: array [0..1, string]
+    s: array[0..1, string]
     e: TEnum
 
 var
diff --git a/tests/stdlib/tstrset.nim b/tests/stdlib/tstrset.nim
index 15024c3f1..1b253f862 100644
--- a/tests/stdlib/tstrset.nim
+++ b/tests/stdlib/tstrset.nim
@@ -7,10 +7,10 @@ type
     kind: TRadixNodeKind
   TRadixNodeLinear = object of TRadixNode
     len: int8
-    keys: array [0..31, char]
-    vals: array [0..31, PRadixNode]
+    keys: array[0..31, char]
+    vals: array[0..31, PRadixNode]
   TRadixNodeFull = object of TRadixNode
-    b: array [char, PRadixNode]
+    b: array[char, PRadixNode]
   TRadixNodeLeaf = object of TRadixNode
     s: string
   PRadixNodeLinear = ref TRadixNodeLinear
diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim
index 674ce50dd..e4a801871 100644
--- a/tests/stdlib/tunittest.nim
+++ b/tests/stdlib/tunittest.nim
@@ -1,12 +1,23 @@
 discard """
-  nimout: "compile start\ncompile end"
+  output: '''[Suite] suite with only teardown
+
+[Suite] suite with only setup
+
+[Suite] suite with none
+
+[Suite] suite with both
+
+[Suite] bug #4494
+
+[Suite] bug #5571
+
+[Suite] bug #5784
+
+'''
 """
 
 import unittest, sequtils
 
-static:
-  echo "compile start"
-
 proc doThings(spuds: var int): int =
   spuds = 24
   return 99
@@ -103,5 +114,9 @@ suite "bug #5571":
       check: line == "a"
     doTest()
 
-static:
-  echo "compile end"
+suite "bug #5784":
+  test "`or` should short circuit":
+    type Obj = ref object
+      field: int
+    var obj: Obj
+    check obj.isNil or obj.field == 0
diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim
index 7b1dd0df0..f71a4a1e7 100644
--- a/tests/testament/categories.nim
+++ b/tests/testament/categories.nim
@@ -238,6 +238,48 @@ proc jsTests(r: var TResults, cat: Category, options: string) =
   for testfile in ["strutils", "json", "random", "times", "logging"]:
     test "lib/pure/" & testfile & ".nim"
 
+# ------------------------- nim in action -----------
+
+proc testNimInAction(r: var TResults, cat: Category, options: string) =
+  template test(filename: untyped, action: untyped) =
+    testSpec r, makeTest(filename, options, cat, action)
+
+  template testJS(filename: untyped) =
+    testSpec r, makeTest(filename, options, cat, actionCompile, targetJS)
+
+  template testCPP(filename: untyped) =
+    testSpec r, makeTest(filename, options, cat, actionCompile, targetCPP)
+
+  let tests = [
+    "niminaction/Chapter3/ChatApp/src/server",
+    "niminaction/Chapter3/ChatApp/src/client",
+    "niminaction/Chapter6/WikipediaStats/concurrency_regex",
+    "niminaction/Chapter6/WikipediaStats/concurrency",
+    "niminaction/Chapter6/WikipediaStats/naive",
+    "niminaction/Chapter6/WikipediaStats/parallel_counts",
+    "niminaction/Chapter6/WikipediaStats/race_condition",
+    "niminaction/Chapter6/WikipediaStats/sequential_counts",
+    "niminaction/Chapter7/Tweeter/src/tweeter",
+    "niminaction/Chapter7/Tweeter/src/createDatabase",
+    "niminaction/Chapter7/Tweeter/tests/database_test",
+    "niminaction/Chapter8/sdl/sdl_test",
+    ]
+  for testfile in tests:
+    test "tests/" & testfile & ".nim", actionCompile
+
+  # TODO: This doesn't work for some reason ;\
+  # let reject = "tests/niminaction/Chapter6/WikipediaStats" &
+  #              "/unguarded_access.nim"
+  # test reject, actionReject
+
+  let jsFile = "tests/niminaction/Chapter8/canvas/canvas_test.nim"
+  testJS jsFile
+
+  let cppFile = "tests/niminaction/Chapter8/sfml/sfml_test.nim"
+  testCPP cppFile
+
+
+
 # ------------------------- manyloc -------------------------------------------
 #proc runSpecialTests(r: var TResults, options: string) =
 #  for t in ["lib/packages/docutils/highlite"]:
@@ -420,6 +462,8 @@ proc processCategory(r: var TResults, cat: Category, options: string) =
     testNimblePackages(r, cat, pfExtraOnly)
   of "nimble-all":
     testNimblePackages(r, cat, pfAll)
+  of "niminaction":
+    testNimInAction(r, cat, options)
   of "untestable":
     # We can't test it because it depends on a third party.
     discard # TODO: Move untestable tests to someplace else, i.e. nimble repo.
diff --git a/tests/threads/tthreadanalysis.nim b/tests/threads/tthreadanalysis.nim
index 5b0b666ac..8aacc71d1 100644
--- a/tests/threads/tthreadanalysis.nim
+++ b/tests/threads/tthreadanalysis.nim
@@ -9,7 +9,7 @@ discard """
 import os
 
 var
-  thr: array [0..5, Thread[tuple[a, b: int]]]
+  thr: array[0..5, Thread[tuple[a, b: int]]]
 
 proc doNothing() = discard
 
diff --git a/tests/threads/tthreadanalysis2.nim b/tests/threads/tthreadanalysis2.nim
index 93d169f0b..c1ec3ae39 100644
--- a/tests/threads/tthreadanalysis2.nim
+++ b/tests/threads/tthreadanalysis2.nim
@@ -8,7 +8,7 @@ discard """
 import os
 
 var
-  thr: array [0..5, Thread[tuple[a, b: int]]]
+  thr: array[0..5, Thread[tuple[a, b: int]]]
 
 proc doNothing() = discard
 
diff --git a/tests/varres/tvarres3.nim b/tests/varres/tvarres3.nim
index 9fcd79bfc..9a46bfb4e 100644
--- a/tests/varres/tvarres3.nim
+++ b/tests/varres/tvarres3.nim
@@ -6,7 +6,7 @@ var
   g = 5
 
 proc p(): var int =
-  var bla = addr(g) #: array [0..7, int]
+  var bla = addr(g) #: array[0..7, int]
   result = bla[]
 
 p() = 45
diff --git a/tests/vm/tarrayboundeval.nim b/tests/vm/tarrayboundeval.nim
index af9e33339..dc8c7ebdc 100644
--- a/tests/vm/tarrayboundeval.nim
+++ b/tests/vm/tarrayboundeval.nim
@@ -25,7 +25,7 @@ echo myconst, " ", int((KeyMax + 31) / 32)
 
 #bug 1304 or something:
 
-const constArray: array [-3..2, int] = [-3, -2, -1, 0, 1, 2]
+const constArray: array[-3..2, int] = [-3, -2, -1, 0, 1, 2]
 
 echo constArray[-2]
 
diff --git a/tests/vm/tnimnode.nim b/tests/vm/tnimnode.nim
index e5a41e3c2..60e3189b0 100644
--- a/tests/vm/tnimnode.nim
+++ b/tests/vm/tnimnode.nim
@@ -15,9 +15,9 @@ static:
 
 proc checkNode(arg: NimNode; name: string): void {. compileTime .} =
   echo "checking ", name
-  
+
   assertEq arg.lispRepr , "StmtList(DiscardStmt(Empty()))"
-  
+
   node = arg
   nodeArray = [arg]
   nodeSeq[0] = arg
@@ -38,9 +38,9 @@ proc checkNode(arg: NimNode; name: string): void {. compileTime .} =
 static:
   # the root node that is used to generate the Ast
   var stmtList: NimNode
-  
+
   stmtList = newStmtList(nnkDiscardStmt.newTree(newEmptyNode()))
-  
+
   checkNode(stmtList, "direct construction")
 
 
@@ -50,12 +50,12 @@ macro foo(stmtList: untyped): untyped =
 foo:
   discard
 
-  
+
 static:
   stmtList = quote do:
     discard
 
-  checkNode(stmtList, "create with quote")
+  checkNode(newTree(nnkStmtList, stmtList), "create with quote")
 
 
 static:
@@ -64,13 +64,13 @@ static:
     for i in 0 ..< 10:
       discard
 
-  let innerBody = loop[0][2]
+  let innerBody = loop[2]
   innerBody.add newCall(ident"echo", newLit("Hello World"))
 
-  assertEq loop[0][2].lispRepr, innerBody.lispRepr
+  assertEq loop[2].lispRepr, innerBody.lispRepr
 
   echo "OK"
-  
+
 
 static:
   echo "testing creation of comment node"
diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim
index b7112b099..42a58fa8f 100644
--- a/tests/vm/tvmmisc.nim
+++ b/tests/vm/tvmmisc.nim
@@ -1,16 +1,66 @@
-
-
 # bug #4462
 import macros
 
-proc foo(t: typedesc) {.compileTime.} =
-  echo getType(t).treeRepr
+block:
+  proc foo(t: typedesc) {.compileTime.} =
+    assert sameType(getType(t), getType(int))
 
-static:
-  foo(int)
+  static:
+    foo(int)
 
 # #4412
-proc default[T](t: typedesc[T]): T {.inline.} = discard
+block:
+  proc default[T](t: typedesc[T]): T {.inline.} = discard
+
+  static:
+    var x = default(type(0))
 
+# #6379
 static:
-  var x = default(type(0))
+  import algorithm
+  
+  var numArray = [1, 2, 3, 4, -1]
+  numArray.sort(cmp)
+  assert numArray == [-1, 1, 2, 3, 4]
+
+  var str = "cba"
+  str.sort(cmp)
+  assert str == "abc"
+
+# #6086
+import math, sequtils, future
+
+block:
+  proc f: int =
+    toSeq(10..<10_000).filter(
+      a => a == ($a).map(
+        d => (d.ord-'0'.ord).int^4
+      ).sum
+    ).sum
+
+  var a = f()
+  const b = f()
+
+  assert a == b
+
+block:
+  proc f(): seq[char] =
+    result = "hello".map(proc(x: char): char = x)
+
+  var runTime = f()
+  const compTime = f()
+  assert runTime == compTime
+
+# #6083
+block:
+  proc abc(): seq[int] =
+    result = @[0]
+    result.setLen(2)
+    var tmp: int
+      
+    for i in 0 .. <2:
+      inc tmp
+      result[i] = tmp
+
+  const fact1000 = abc()
+  assert fact1000 == @[1, 2]
diff --git a/tools/finish.nim b/tools/finish.nim
index a6f689eac..45d7dd3a8 100644
--- a/tools/finish.nim
+++ b/tools/finish.nim
@@ -91,16 +91,25 @@ when defined(windows):
     except IOError:
       echo "Could not access 'config/nim.cfg' [Error]"
 
-  proc addToPathEnv*(e: string) =
-    var p: string
+  proc tryGetUnicodeValue(path, key: string; handle: HKEY): string =
     try:
-      p = getUnicodeValue(r"Environment", "Path", HKEY_CURRENT_USER)
-    except OSError:
-      p = getUnicodeValue(
+      result = getUnicodeValue(path, key, handle)
+    except:
+      result = ""
+
+  proc addToPathEnv*(e: string) =
+    var p = tryGetUnicodeValue(r"Environment", "Path", HKEY_CURRENT_USER)
+    if p.len == 0:
+      p = tryGetUnicodeValue(
         r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment",
         "Path", HKEY_LOCAL_MACHINE)
     let x = if e.contains(Whitespace): "\"" & e & "\"" else: e
-    setUnicodeValue(r"Environment", "Path", p & ";" & x, HKEY_CURRENT_USER)
+    if p.len > 0:
+      p.add ";"
+      p.add x
+    else:
+      p = x
+    setUnicodeValue(r"Environment", "Path", p, HKEY_CURRENT_USER)
 
   proc createShortcut(src, dest: string; icon = "") =
     var cmd = "bin\\makelink.exe \"" & src & "\" \"\" \"" & dest &
diff --git a/tools/nimresolve.nim b/tools/nimresolve.nim
new file mode 100644
index 000000000..9af24df5a
--- /dev/null
+++ b/tools/nimresolve.nim
@@ -0,0 +1,158 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2017 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Standard tool that resolves import paths.
+
+import
+  os, strutils, parseopt
+
+import "../compiler/nimblecmd"
+
+# You can change these constants to build you own adapted resolver.
+const
+  considerParentDirs = not defined(noParentProjects)
+  considerNimbleDirs = not defined(noNimbleDirs)
+
+const
+  Version = "1.0"
+  Usage = "nimresolve - Nim Resolve Package Path Version " & Version & """
+
+  (c) 2017 Andreas Rumpf
+Usage:
+  nimresolve [options] package
+Options:
+  --source:FILE       the file that requests to resolve 'package'
+  --stdlib:PATH       the path to use for the standard library
+  --project:FILE      the main '.nim' file that was passed to the Nim compiler
+  --subdir:EXPR       the subdir part in: 'import $pkg / subdir'
+  --noNimblePath      do not search the Nimble path to resolve the package
+"""
+
+proc writeHelp() =
+  stdout.write(Usage)
+  stdout.flushFile()
+  quit(0)
+
+proc writeVersion() =
+  stdout.write(Version & "\n")
+  stdout.flushFile()
+  quit(0)
+
+type
+  Task = object
+    source, stdlib, subdir, project, pkg: string
+    noNimblePath: bool
+
+proc findInNimbleDir(t: Task; dir: string): bool =
+  var best = ""
+  var bestv = ""
+  for k, p in os.walkDir(dir, relative=true):
+    if k == pcDir and p.len > t.pkg.len+1 and
+        p[t.pkg.len] == '-' and p.startsWith(t.pkg):
+      let (_, a) = getPathVersion(p)
+      if bestv.len == 0 or bestv < a:
+        bestv = a
+        best = dir / p
+
+  if best.len > 0:
+    var f: File
+    if open(f, best / changeFileExt(t.pkg, ".nimble-link")):
+      # the second line contains what we're interested in, see:
+      # https://github.com/nim-lang/nimble#nimble-link
+      var override = ""
+      discard readLine(f, override)
+      discard readLine(f, override)
+      close(f)
+      if not override.isAbsolute():
+        best = best / override
+      else:
+        best = override
+  let f = if t.subdir.len == 0: t.pkg else: t.subdir
+  let res = addFileExt(best / f, "nim")
+  if best.len > 0 and fileExists(res):
+    echo res
+    result = true
+
+const stdlibDirs = [
+  "pure", "core", "arch",
+  "pure/collections",
+  "pure/concurrency", "impure",
+  "wrappers", "wrappers/linenoise",
+  "windows", "posix", "js"]
+
+proc resolve(t: Task) =
+  template attempt(a) =
+    let x = addFileExt(a, "nim")
+    if fileExists(x):
+      echo x
+      return
+
+  case t.pkg
+  of "stdlib":
+    if t.subdir.len == 0:
+      echo t.stdlib
+      return
+    else:
+      for candidate in stdlibDirs:
+        attempt(t.stdlib / candidate / t.subdir)
+  of "root":
+    let root = t.project.splitFile.dir
+    if t.subdir.len == 0:
+      echo root
+      return
+    else:
+      attempt(root / t.subdir)
+  else:
+    when considerParentDirs:
+      var p = parentDir(t.source.splitFile.dir)
+      # support 'import $karax':
+      let f = if t.subdir.len == 0: t.pkg else: t.subdir
+
+      while p.len > 0:
+        let dir = p / t.pkg
+        if dirExists(dir):
+          attempt(dir / f)
+          # 2nd attempt: try to use 'karax/karax'
+          attempt(dir / t.pkg / f)
+          # 3rd attempt: try to use 'karax/src/karax'
+          attempt(dir / "src" / f)
+          attempt(dir / "src" / t.pkg / f)
+        p = parentDir(p)
+
+    when considerNimbleDirs:
+      if not t.noNimblePath:
+        if findInNimbleDir(t, getHomeDir() / ".nimble" / "pkgs"): return
+        when not defined(windows):
+          if findInNimbleDir(t, "/opt/nimble/pkgs"): return
+
+  quit "cannot resolve: " & (t.pkg / t.subdir)
+
+proc main =
+  var t: Task
+  t.subdir = ""
+  for kind, key, val in getopt():
+    case kind
+    of cmdArgument:
+      t.pkg = key
+    of cmdLongoption, cmdShortOption:
+      case normalize(key)
+      of "source": t.source = val
+      of "stdlib": t.stdlib = val
+      of "project": t.project = val
+      of "subdir": t.subdir = val
+      of "nonimblepath": t.noNimblePath = true
+      of "help", "h": writeHelp()
+      of "version", "v": writeVersion()
+      else: writeHelp()
+    of cmdEnd: assert(false) # cannot happen
+  if t.pkg.len == 0:
+    quit "[Error] no package to resolve."
+  resolve(t)
+
+main()