summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml3
-rw-r--r--appveyor.yml2
-rw-r--r--changelog.md28
-rw-r--r--compiler/ast.nim79
-rw-r--r--compiler/astalgo.nim270
-rw-r--r--compiler/btrees.nim233
-rw-r--r--compiler/ccgcalls.nim30
-rw-r--r--compiler/ccgexprs.nim158
-rw-r--r--compiler/ccgmerge.nim32
-rw-r--r--compiler/ccgstmts.nim59
-rw-r--r--compiler/ccgthreadvars.nim27
-rw-r--r--compiler/ccgtrav.nim3
-rw-r--r--compiler/ccgtypes.nim158
-rw-r--r--compiler/ccgutils.nim115
-rw-r--r--compiler/cgen.nim188
-rw-r--r--compiler/cgendata.nim16
-rw-r--r--compiler/cgmeth.nim10
-rw-r--r--compiler/closureiters.nim52
-rw-r--r--compiler/commands.nim69
-rw-r--r--compiler/condsyms.nim2
-rw-r--r--compiler/configuration.nim362
-rw-r--r--compiler/depends.nim31
-rw-r--r--compiler/destroyer.nim20
-rw-r--r--compiler/dfa.nim7
-rw-r--r--compiler/docgen.nim109
-rw-r--r--compiler/docgen2.nim12
-rw-r--r--compiler/evalffi.nim4
-rw-r--r--compiler/evaltempl.nim11
-rw-r--r--compiler/extccomp.nim200
-rw-r--r--compiler/filter_tmpl.nim2
-rw-r--r--compiler/gorgeimpl.nim5
-rw-r--r--compiler/guards.nim26
-rw-r--r--compiler/hlo.nim6
-rw-r--r--compiler/idents.nim36
-rw-r--r--compiler/importer.nim39
-rw-r--r--compiler/incremental.nim196
-rw-r--r--compiler/jsgen.nim75
-rw-r--r--compiler/jstypes.nim12
-rw-r--r--compiler/lambdalifting.nim142
-rw-r--r--compiler/layouter.nim274
-rw-r--r--compiler/lexer.nim71
-rw-r--r--compiler/liftlocals.nim9
-rw-r--r--compiler/lineinfos.nim267
-rw-r--r--compiler/linter.nim (renamed from compiler/nimfix/pretty.nim)83
-rw-r--r--compiler/lookups.nim72
-rw-r--r--compiler/lowerings.nim115
-rw-r--r--compiler/macrocacheimpl.nim79
-rw-r--r--compiler/magicsys.nim39
-rw-r--r--compiler/main.nim159
-rw-r--r--compiler/modulegraphs.nim41
-rw-r--r--compiler/modulepaths.nim8
-rw-r--r--compiler/modules.nim97
-rw-r--r--compiler/msgs.nim318
-rw-r--r--compiler/ndi.nim12
-rw-r--r--compiler/nim.cfg3
-rw-r--r--compiler/nim.nim27
-rw-r--r--compiler/nimblecmd.nim2
-rw-r--r--compiler/nimconf.nim8
-rw-r--r--compiler/nimeval.nim131
-rw-r--r--compiler/nimfix/nimfix.nim2
-rw-r--r--compiler/nimfix/prettybase.nim79
-rw-r--r--compiler/nimsets.nim55
-rw-r--r--compiler/nversion.nim2
-rw-r--r--compiler/options.nim188
-rw-r--r--compiler/parser.nim198
-rw-r--r--compiler/passaux.nim4
-rw-r--r--compiler/passes.nim95
-rw-r--r--compiler/patterns.nim2
-rw-r--r--compiler/platform.nim60
-rw-r--r--compiler/plugins/active.nim13
-rw-r--r--compiler/plugins/itersgen.nim16
-rw-r--r--compiler/plugins/locals.nim (renamed from compiler/plugins/locals/locals.nim)6
-rw-r--r--compiler/pluginsupport.nim28
-rw-r--r--compiler/pragmas.nim233
-rw-r--r--compiler/prefixmatches.nim6
-rw-r--r--compiler/procfind.nim2
-rw-r--r--compiler/renderer.nim22
-rw-r--r--compiler/reorder.nim73
-rw-r--r--compiler/rod.nim19
-rw-r--r--compiler/rodimpl.nim616
-rw-r--r--compiler/rodread.nim1244
-rw-r--r--compiler/rodwrite.nim659
-rw-r--r--compiler/ropes.nim41
-rw-r--r--compiler/scriptconfig.nim19
-rw-r--r--compiler/sem.nim103
-rw-r--r--compiler/semasgn.nim20
-rw-r--r--compiler/semcall.nim44
-rw-r--r--compiler/semdata.nim15
-rw-r--r--compiler/semexprs.nim275
-rw-r--r--compiler/semfields.nim12
-rw-r--r--compiler/semfold.nim77
-rw-r--r--compiler/semgnrc.nim55
-rw-r--r--compiler/seminst.nim89
-rw-r--r--compiler/semmagic.nim18
-rw-r--r--compiler/semobjconstr.nim6
-rw-r--r--compiler/semparallel.nim8
-rw-r--r--compiler/sempass2.nim38
-rw-r--r--compiler/semstmts.nim137
-rw-r--r--compiler/semtempl.nim26
-rw-r--r--compiler/semtypes.nim229
-rw-r--r--compiler/semtypinst.nim46
-rw-r--r--compiler/service.nim23
-rw-r--r--compiler/sighashes.nim32
-rw-r--r--compiler/sigmatch.nim135
-rw-r--r--compiler/suggest.nim141
-rw-r--r--compiler/syntaxes.nim10
-rw-r--r--compiler/transf.nim84
-rw-r--r--compiler/types.nim267
-rw-r--r--compiler/vm.nim278
-rw-r--r--compiler/vmdef.nim7
-rw-r--r--compiler/vmdeps.nim78
-rw-r--r--compiler/vmgen.nim64
-rw-r--r--compiler/vmmarshal.nim23
-rw-r--r--compiler/vmops.nim23
-rw-r--r--compiler/writetracking.nim3
-rw-r--r--doc/docgen.rst6
-rw-r--r--doc/intern.txt175
-rw-r--r--doc/manual.rst205
-rw-r--r--doc/tut1.rst5
-rw-r--r--doc/tut2.rst6
-rw-r--r--examples/allany.nim26
-rw-r--r--examples/readme.txt3
-rw-r--r--koch.nim10
-rw-r--r--lib/core/macrocache.nim47
-rw-r--r--lib/core/macros.nim3
-rw-r--r--lib/packages/docutils/rst.nim1
-rw-r--r--lib/packages/docutils/rstgen.nim13
-rw-r--r--lib/pure/asyncfile.nim2
-rw-r--r--lib/pure/collections/deques.nim57
-rw-r--r--lib/pure/httpclient.nim2
-rw-r--r--lib/pure/marshal.nim2
-rw-r--r--lib/pure/math.nim20
-rw-r--r--lib/pure/memfiles.nim118
-rw-r--r--lib/pure/os.nim2
-rw-r--r--lib/pure/osproc.nim2
-rw-r--r--lib/pure/parseutils.nim38
-rw-r--r--lib/pure/pegs.nim23
-rw-r--r--lib/pure/streams.nim16
-rw-r--r--lib/pure/strutils.nim74
-rw-r--r--lib/pure/terminal.nim131
-rw-r--r--lib/pure/times.nim47
-rw-r--r--lib/std/varints.nim9
-rw-r--r--lib/system.nim22
-rw-r--r--lib/system/mmdisp.nim3
-rw-r--r--lib/system/nimscript.nim2
-rw-r--r--lib/system/platforms.nim4
-rw-r--r--lib/windows/winlean.nim42
-rw-r--r--nimpretty/nimpretty.nim (renamed from tools/nimpretty.nim)23
-rw-r--r--nimpretty/nimpretty.nim.cfg2
-rw-r--r--nimpretty/tester.nim29
-rw-r--r--nimpretty/tests/exhaustive.nim316
-rw-r--r--nimpretty/tests/expected/exhaustive.nim325
-rw-r--r--nimsuggest/nimsuggest.nim121
-rw-r--r--nimsuggest/nimsuggest.nim.cfg3
-rw-r--r--tests/arithm/tnot.nim58
-rw-r--r--tests/arithm/tshl.nim34
-rw-r--r--tests/async/tasynctry2.nim18
-rw-r--r--tests/compilerapi/exposed.nim3
-rw-r--r--tests/compilerapi/myscript.nim9
-rw-r--r--tests/compilerapi/tcompilerapi.nim47
-rw-r--r--tests/concepts/libs/trie.nim26
-rw-r--r--tests/concepts/libs/trie_database.nim12
-rw-r--r--tests/concepts/t6770.nim27
-rw-r--r--tests/concepts/treversable.nim31
-rw-r--r--tests/concepts/ttrieconcept.nim7
-rw-r--r--tests/cpp/tempty_generic_obj.nim16
-rw-r--r--tests/errmsgs/tproper_stacktrace.nim142
-rw-r--r--tests/float/tfloatrange.nim49
-rw-r--r--tests/generics/t3977.nim4
-rw-r--r--tests/generics/tlateboundgenericparams.nim145
-rw-r--r--tests/global/t5958.nim9
-rw-r--r--tests/lexer/tmissingnl.nim2
-rw-r--r--tests/macros/tmacros1.nim6
-rw-r--r--tests/macros/tmacrostmt.nim4
-rw-r--r--tests/metatype/ttypedesc1.nim34
-rw-r--r--tests/metatype/ttypedesc2.nim14
-rw-r--r--tests/metatype/ttypedesc3.nim6
-rw-r--r--tests/metatype/ttypeselectors.nim6
-rw-r--r--tests/misc/tparamsindefault.nim114
-rw-r--r--tests/niminaction/Chapter1/various1.nim45
-rw-r--r--tests/niminaction/Chapter2/explicit_discard.nim7
-rw-r--r--tests/niminaction/Chapter2/no_def_eq.nim16
-rw-r--r--tests/niminaction/Chapter2/no_iterator.nim7
-rw-r--r--tests/niminaction/Chapter2/no_seq_type.nim6
-rw-r--r--tests/niminaction/Chapter2/resultaccept.nim28
-rw-r--r--tests/niminaction/Chapter2/resultreject.nim33
-rw-r--r--tests/niminaction/Chapter2/various2.nim369
-rw-r--r--tests/niminaction/Chapter3/various3.nim93
-rw-r--r--tests/niminaction/Chapter3/various3.nim.cfg1
-rw-r--r--tests/overflw/toverflw.nim75
-rw-r--r--tests/parser/ttypeclasses.nim39
-rw-r--r--tests/parser/ttypemodifiers.nim527
-rw-r--r--tests/pragmas/treorder.nim3
-rw-r--r--tests/statictypes/tstatictypes.nim109
-rw-r--r--tests/stdlib/tmemfiles2.nim3
-rw-r--r--tests/stdlib/tmemmapstreams.nim53
-rw-r--r--tests/stdlib/tpegs.nim78
-rw-r--r--tests/stdlib/tstrutil.nim49
-rw-r--r--tests/template/tdefined_overload.nim46
-rw-r--r--tests/template/tpattern_with_converter.nim27
-rw-r--r--tests/testament/categories.nim38
-rw-r--r--tests/testament/tester.nim2
-rw-r--r--tests/types/t7905.nim33
-rw-r--r--tests/types/tisopr.nim2
-rw-r--r--tests/vm/tnilref.nim7
-rw-r--r--tests/vm/tnimnode.nim20
-rw-r--r--tests/vm/tref.nim47
-rw-r--r--tests/vm/tvmmisc.nim4
208 files changed, 8889 insertions, 6328 deletions
diff --git a/.travis.yml b/.travis.yml
index 38e1eba28..5a091d0c7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -31,7 +31,6 @@ before_script:
   - cd csources
   - sh build.sh
   - cd ..
-  - sed -i -e 's,cc = gcc,cc = clang,' config/nim.cfg
   - export PATH=$(pwd)/bin${PATH:+:$PATH}
 script:
   - nim c koch
@@ -46,6 +45,8 @@ script:
   - nimble install niminst
   - nim c --taintMode:on -d:nimCoroutines tests/testament/tester
   - tests/testament/tester --pedantic all -d:nimCoroutines
+  - nim c -o:bin/nimpretty nimpretty/nimpretty.nim
+  - nim c -r nimpretty/tester.nim
   - ./koch web
   - ./koch csource
   - ./koch nimsuggest
diff --git a/appveyor.yml b/appveyor.yml
index a79d32e41..daa1d4e48 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -47,6 +47,8 @@ build_script:
   - koch boot -d:release
   - koch nimble
   - nim e tests/test_nimscript.nims
+  - nim c -o:bin/nimpretty.exe nimpretty/nimpretty.nim
+  - nim c -r nimpretty/tester.nim
   - nimble install zip -y
   - nimble install opengl
   - nimble install sdl1
diff --git a/changelog.md b/changelog.md
index cb2a4b91d..3a905d0b9 100644
--- a/changelog.md
+++ b/changelog.md
@@ -48,6 +48,10 @@
 - For string inputs, ``strutils.isUpperAscii`` and ``strutils.isLowerAscii`` now
   require a second mandatory parameter ``skipNonAlpha``.
 
+- The procs ``parseHexInt`` and ``parseOctInt`` now fail on empty strings
+    and strings containing only valid prefixes, e.g. "0x" for hex integers.
+
+
 #### Breaking changes in the compiler
 
 - The undocumented ``#? braces`` parsing mode was removed.
@@ -72,6 +76,10 @@
 - Added the procs ``math.floorMod`` and ``math.floorDiv`` for floor based integer division.
 - Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals.
 - Added the proc ``math.prod`` for product of elements in openArray.
+- Added the proc ``parseBinInt`` to parse a binary integer from a string, which returns the value.
+- ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt``
+- Added the proc ``flush`` for memory mapped files.
+- Added the ``MemMapFileStream``.
 
 ### Library changes
 
@@ -91,10 +99,17 @@
 - The `terminal` module now exports additional procs for generating ANSI color
   codes as strings.
 - Added the parameter ``val`` for the ``CritBitTree[int].inc`` proc.
+- An exception raised from a ``test`` block of ``unittest`` now shows its type in
+  error message.
+- The ``compiler/nimeval`` API was rewritten to simplify the "compiler as an
+  API". Using the Nim compiler and its VM as a scripting engine has never been
+  easier. See ``tests/compilerapi/tcompilerapi.nim`` for an example of how to
+  use the Nim VM in a native Nim application.
 - Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc.
-- An exception raised from ``test`` block of ``unittest`` now shows its type in
-  the error message
 - The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated.
+- The ``pegs`` module now exports getters for the fields of its ``Peg`` and ``NonTerminal``
+  object types. ``Peg``s with child nodes now have the standard ``items`` and ``pairs``
+  iterators.
 
 ### Language additions
 
@@ -104,6 +119,13 @@
 - In order to make ``for`` loops and iterators more flexible to use Nim now
   supports so called "for-loop macros". See
   the `manual <manual.html#macros-for-loop-macros>`_ for more details.
+- the `typedesc` special type has been renamed to just `type`.
+- `static` and `type` are now also modifiers similar to `ref` and `ptr`.
+  They denote the special types `static[T]` and `type[T]`.
+- Forcing compile-time evaluation with `static` now supports specifying
+  the desired target type (as a concrete type or as a type class)
+- The `type` operator now supports checking that the supplied expression
+  matches an expected type constraint.
 
 ### Language changes
 
@@ -131,6 +153,8 @@
 
 - Nim now supports `except` clause in the export statement.
 
+- Range float types, example `range[0.0 .. Inf]`. More details in language manual.
+
 ### Tool changes
 
 - ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 014ffaf26..0e0ff77b8 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -10,7 +10,7 @@
 # abstract syntax tree + symbol table
 
 import
-  msgs, hashes, nversion, options, strutils, std / sha1, ropes, idents,
+  lineinfos, hashes, nversion, options, strutils, std / sha1, ropes, idents,
   intsets, idgen
 
 type
@@ -293,6 +293,10 @@ const
     # the compiler will avoid printing such names
     # in user messages.
 
+  sfHoisted* = sfForward
+    # an expression was hoised to an anonymous variable.
+    # the flag is applied to the var/let symbol
+
   sfNoForward* = sfRegister
     # forward declarations are not required (per module)
   sfReorder* = sfForward
@@ -454,6 +458,9 @@ type
     nfPreventCg # this node should be ignored by the codegen
     nfBlockArg  # this a stmtlist appearing in a call (e.g. a do block)
     nfFromTemplate # a top-level node returned from a template
+    nfDefaultParam # an automatically inserter default parameter
+    nfDefaultRefsParam # a default param value references another parameter
+                       # the flag is applied to proc default values and to calls
 
   TNodeFlags* = set[TNodeFlag]
   TTypeFlag* = enum   # keep below 32 for efficiency reasons (now: beyond that)
@@ -571,8 +578,8 @@ type
   TMagic* = enum # symbols that require compiler magic:
     mNone,
     mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn,
-    mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin,
-    mEcho, mShallowCopy, mSlurp, mStaticExec,
+    mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mType, mTypeOf,
+    mRoof, mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mStatic,
     mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
     mUnaryLt, mInc, mDec, mOrd,
     mNew, mNewFinalize, mNewSeq, mNewSeqOfCap,
@@ -634,14 +641,18 @@ type
     mNaN, mInf, mNegInf,
     mCompileOption, mCompileOptionArg,
     mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel,
-    mNKind, mNSymKind
+    mNKind, mNSymKind,
+
+    mNccValue, mNccInc, mNcsAdd, mNcsIncl, mNcsLen, mNcsAt,
+    mNctPut, mNctLen, mNctGet, mNctHasNext, mNctNext,
+
     mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
     mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
     mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent,
     mNBindSym, mLocals, mNCallSite,
-    mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl,
+    mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym,
     mNHint, mNWarning, mNError,
-    mInstantiationInfo, mGetTypeInfo, mNGenSym,
+    mInstantiationInfo, mGetTypeInfo,
     mNimvm, mIntDefine, mStrDefine, mRunnableExamples,
     mException, mBuiltinType
 
@@ -967,7 +978,7 @@ const
   PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
                                       nfDotSetter, nfDotField,
                                       nfIsRef, nfPreventCg, nfLL,
-                                      nfFromTemplate}
+                                      nfFromTemplate, nfDefaultRefsParam}
   namePos* = 0
   patternPos* = 1    # empty except for term rewriting macros
   genericParamsPos* = 2
@@ -998,8 +1009,8 @@ const
                   skMethod, skConverter}
 
 var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
-var
-  gMainPackageId*: int
+#var
+#  gMainPackageId*: int
 
 proc isCallExpr*(n: PNode): bool =
   result = n.kind in nkCallKinds
@@ -1058,22 +1069,6 @@ proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode =
     result.info = children[0].info
   result.sons = @children
 
-proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
-  result = newNode(kind)
-  result.intVal = intVal
-
-proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode =
-  result = newIntNode(kind, intVal)
-  result.typ = typ
-
-proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode =
-  result = newNode(kind)
-  result.floatVal = floatVal
-
-proc newStrNode*(kind: TNodeKind, strVal: string): PNode =
-  result = newNode(kind)
-  result.strVal = strVal
-
 template previouslyInferred*(t: PType): PType =
   if t.sons.len > 1: t.lastSon else: nil
 
@@ -1095,9 +1090,6 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
   #  writeStacktrace()
   #  MessageOut(name.s & " has id: " & toString(result.id))
 
-var emptyNode* = newNode(nkEmpty) # XXX global variable here!
-# There is a single empty node that is shared! Do not overwrite it!
-
 proc isMetaType*(t: PType): bool =
   return t.kind in tyMetaTypes or
          (t.kind == tyStatic and t.n == nil) or
@@ -1224,18 +1216,35 @@ proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
   result.info = info
   result.typ = typ
 
+proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
+  result = newNode(kind)
+  result.intVal = intVal
+
+proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode =
+  result = newIntNode(kind, intVal)
+  result.typ = typ
+
+proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode =
+  result = newNode(kind)
+  result.floatVal = floatVal
+
+proc newStrNode*(kind: TNodeKind, strVal: string): PNode =
+  result = newNode(kind)
+  result.strVal = strVal
+
+proc newStrNode*(strVal: string; info: TLineInfo): PNode =
+  result = newNodeI(nkStrLit, info)
+  result.strVal = strVal
+
 proc addSon*(father, son: PNode) =
   assert son != nil
   if isNil(father.sons): father.sons = @[]
   add(father.sons, son)
 
-var emptyParams = newNode(nkFormalParams)
-emptyParams.addSon(emptyNode)
-
 proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
-                 params = emptyParams,
+                 params,
                  name, pattern, genericParams,
-                 pragmas, exceptions = ast.emptyNode): PNode =
+                 pragmas, exceptions: PNode): PNode =
   result = newNodeI(kind, info)
   result.sons = @[name, pattern, genericParams, params,
                   pragmas, exceptions, body]
@@ -1702,7 +1711,7 @@ proc isImportedException*(t: PType; conf: ConfigRef): bool =
     result = true
 
 proc isInfixAs*(n: PNode): bool =
-  return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.id == getIdent("as").id
+  return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "as"
 
 proc findUnresolvedStatic*(n: PNode): PNode =
   if n.kind == nkSym and n.typ.kind == tyStatic and n.typ.n == nil:
@@ -1727,3 +1736,5 @@ template incompleteType*(t: PType): bool =
 
 template typeCompleted*(s: PSym) =
   incl s.flags, sfNoForward
+
+template getBody*(s: PSym): PNode = s.ast[bodyPos]
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 15072e175..f474ca65e 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -12,81 +12,58 @@
 # the data structures here are used in various places of the compiler.
 
 import
-  ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils
+  ast, hashes, intsets, strutils, options, lineinfos, ropes, idents, rodutils,
+  msgs
 
 proc hashNode*(p: RootRef): Hash
-proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
+proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
   # Convert a tree into its YAML representation; this is used by the
   # YAML code generator and it is invaluable for debugging purposes.
   # If maxRecDepht <> -1 then it won't print the whole graph.
-proc typeToYaml*(n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope
-proc symToYaml*(n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
-proc lineInfoToStr*(info: TLineInfo): Rope
-
-# ----------------------- node sets: ---------------------------------------
-proc objectSetContains*(t: TObjectSet, obj: RootRef): bool
-  # returns true whether n is in t
-proc objectSetIncl*(t: var TObjectSet, obj: RootRef)
-  # include an element n in the table t
-proc objectSetContainsOrIncl*(t: var TObjectSet, obj: RootRef): bool
-  # more are not needed ...
-
-# ----------------------- str table -----------------------------------------
-proc strTableContains*(t: TStrTable, n: PSym): bool
-proc strTableAdd*(t: var TStrTable, n: PSym)
-proc strTableGet*(t: TStrTable, name: PIdent): PSym
-
-type
-  TTabIter*{.final.} = object # consider all fields here private
-    h*: Hash                  # current hash
-
-proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym
-proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym
-  # usage:
-  # var
-  #   i: TTabIter
-  #   s: PSym
-  # s = InitTabIter(i, table)
-  # while s != nil:
-  #   ...
-  #   s = NextIter(i, table)
-  #
-
-type
-  TIdentIter*{.final.} = object # iterator over all syms with same identifier
-    h*: Hash                    # current hash
-    name*: PIdent
-
-
-proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym
-proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym
-
-# these are for debugging only: They are not really deprecated, but I want
-# the warning so that release versions do not contain debugging statements:
-proc debug*(n: PSym) {.deprecated.}
-proc debug*(n: PType) {.deprecated.}
-proc debug*(n: PNode) {.deprecated.}
-
-template mdbg*: bool {.dirty.} =
-  when compiles(c.module):
-    c.module.fileIdx.int32 == c.config.projectMainIdx
-  elif compiles(c.c.module):
-    c.c.module.fileIdx.int32 == c.c.config.projectMainIdx
-  elif compiles(m.c.module):
-    m.c.module.fileIdx.int32 == m.c.config.projectMainIdx
-  elif compiles(cl.c.module):
-    cl.c.module.fileIdx.int32 == cl.c.config.projectMainIdx
-  elif compiles(p):
-    when compiles(p.lex):
-      p.lex.fileIdx.int32 == p.lex.config.projectMainIdx
+proc typeToYaml*(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope
+proc symToYaml*(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
+proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): Rope
+
+when declared(echo):
+  # these are for debugging only: They are not really deprecated, but I want
+  # the warning so that release versions do not contain debugging statements:
+  proc debug*(n: PSym; conf: ConfigRef = nil) {.deprecated.}
+  proc debug*(n: PType; conf: ConfigRef = nil) {.deprecated.}
+  proc debug*(n: PNode; conf: ConfigRef = nil) {.deprecated.}
+
+  template debug*(x: PSym|PType|PNode) {.deprecated.} =
+    when compiles(c.config):
+      debug(c.config, x)
+    elif compiles(c.graph.config):
+      debug(c.graph.config, x)
     else:
-      p.module.module.fileIdx.int32 == p.config.projectMainIdx
-  elif compiles(m.module.fileIdx):
-    m.module.fileIdx.int32 == m.config.projectMainIdx
-  elif compiles(L.fileIdx):
-    L.fileIdx.int32 == L.config.projectMainIdx
-  else:
-    error()
+      error()
+
+  template debug*(x: auto) {.deprecated.} =
+    echo x
+
+  template mdbg*: bool {.deprecated.} =
+    when compiles(c.graph):
+      c.module.fileIdx == c.graph.config.projectMainIdx
+    elif compiles(c.module):
+      c.module.fileIdx == c.config.projectMainIdx
+    elif compiles(c.c.module):
+      c.c.module.fileIdx == c.c.config.projectMainIdx
+    elif compiles(m.c.module):
+      m.c.module.fileIdx == m.c.config.projectMainIdx
+    elif compiles(cl.c.module):
+      cl.c.module.fileIdx == cl.c.config.projectMainIdx
+    elif compiles(p):
+      when compiles(p.lex):
+        p.lex.fileIdx == p.lex.config.projectMainIdx
+      else:
+        p.module.module.fileIdx == p.config.projectMainIdx
+    elif compiles(m.module.fileIdx):
+      m.module.fileIdx == m.config.projectMainIdx
+    elif compiles(L.fileIdx):
+      L.fileIdx == L.config.projectMainIdx
+    else:
+      error()
 
 # --------------------------- ident tables ----------------------------------
 proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
@@ -102,7 +79,6 @@ proc idNodeTablePut*(t: var TIdNodeTable, key: PIdObj, val: PNode)
 
 proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym
 proc lookupInRecord*(n: PNode, field: PIdent): PSym
-proc getModule*(s: PSym): PSym
 proc mustRehash*(length, counter: int): bool
 proc nextTry*(h, maxHash: Hash): Hash {.inline.}
 
@@ -193,7 +169,7 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym =
     if n.sym.name.id == field.id: result = n.sym
   else: return nil
 
-proc getModule(s: PSym): PSym =
+proc getModule*(s: PSym): PSym =
   result = s
   assert((result.kind == skModule) or (result.owner != result))
   while result != nil and result.kind != skModule: result = result.owner
@@ -250,16 +226,16 @@ proc flagsToStr[T](flags: set[T]): Rope =
       add(result, makeYamlString($x))
     result = "[" & result & "]"
 
-proc lineInfoToStr(info: TLineInfo): Rope =
-  result = "[$1, $2, $3]" % [makeYamlString(toFilename(info)),
+proc lineInfoToStr(conf: ConfigRef; info: TLineInfo): Rope =
+  result = "[$1, $2, $3]" % [makeYamlString(toFilename(conf, info)),
                              rope(toLinenumber(info)),
                              rope(toColumn(info))]
 
-proc treeToYamlAux(n: PNode, marker: var IntSet,
+proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet,
                    indent, maxRecDepth: int): Rope
-proc symToYamlAux(n: PSym, marker: var IntSet,
+proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet,
                   indent, maxRecDepth: int): Rope
-proc typeToYamlAux(n: PType, marker: var IntSet,
+proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet,
                    indent, maxRecDepth: int): Rope
 
 proc ropeConstr(indent: int, c: openArray[Rope]): Rope =
@@ -273,7 +249,7 @@ proc ropeConstr(indent: int, c: openArray[Rope]): Rope =
     inc(i, 2)
   addf(result, "$N$1}", [rspaces(indent)])
 
-proc symToYamlAux(n: PSym, marker: var IntSet, indent: int,
+proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent: int,
                   maxRecDepth: int): Rope =
   if n == nil:
     result = rope("null")
@@ -281,20 +257,20 @@ proc symToYamlAux(n: PSym, marker: var IntSet, indent: int,
     result = "\"$1 @$2\"" % [rope(n.name.s), rope(
         strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
   else:
-    var ast = treeToYamlAux(n.ast, marker, indent + 2, maxRecDepth - 1)
+    var ast = treeToYamlAux(conf, n.ast, marker, indent + 2, maxRecDepth - 1)
     result = ropeConstr(indent, [rope("kind"),
                                  makeYamlString($n.kind),
                                  rope("name"), makeYamlString(n.name.s),
-                                 rope("typ"), typeToYamlAux(n.typ, marker,
+                                 rope("typ"), typeToYamlAux(conf, n.typ, marker,
                                    indent + 2, maxRecDepth - 1),
-                                 rope("info"), lineInfoToStr(n.info),
+                                 rope("info"), lineInfoToStr(conf, n.info),
                                  rope("flags"), flagsToStr(n.flags),
                                  rope("magic"), makeYamlString($n.magic),
                                  rope("ast"), ast, rope("options"),
                                  flagsToStr(n.options), rope("position"),
                                  rope(n.position)])
 
-proc typeToYamlAux(n: PType, marker: var IntSet, indent: int,
+proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int,
                    maxRecDepth: int): Rope =
   if n == nil:
     result = rope("null")
@@ -306,15 +282,15 @@ proc typeToYamlAux(n: PType, marker: var IntSet, indent: int,
       result = rope("[")
       for i in countup(0, sonsLen(n) - 1):
         if i > 0: add(result, ",")
-        addf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(n.sons[i],
+        addf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n.sons[i],
             marker, indent + 4, maxRecDepth - 1)])
       addf(result, "$N$1]", [rspaces(indent + 2)])
     else:
       result = rope("null")
     result = ropeConstr(indent, [rope("kind"),
                                  makeYamlString($n.kind),
-                                 rope("sym"), symToYamlAux(n.sym, marker,
-        indent + 2, maxRecDepth - 1), rope("n"), treeToYamlAux(n.n, marker,
+                                 rope("sym"), symToYamlAux(conf, n.sym, marker,
+        indent + 2, maxRecDepth - 1), rope("n"), treeToYamlAux(conf, n.n, marker,
         indent + 2, maxRecDepth - 1), rope("flags"), flagsToStr(n.flags),
                                  rope("callconv"),
                                  makeYamlString(CallingConvToStr[n.callConv]),
@@ -322,7 +298,7 @@ proc typeToYamlAux(n: PType, marker: var IntSet, indent: int,
                                  rope("align"), rope(n.align),
                                  rope("sons"), result])
 
-proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
+proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int,
                    maxRecDepth: int): Rope =
   if n == nil:
     result = rope("null")
@@ -330,7 +306,7 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
     var istr = rspaces(indent + 2)
     result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)]
     if maxRecDepth != 0:
-      addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
+      addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
       case n.kind
       of nkCharLit..nkInt64Lit:
         addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
@@ -344,7 +320,7 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
           addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
       of nkSym:
         addf(result, ",$N$1\"sym\": $2",
-             [istr, symToYamlAux(n.sym, marker, indent + 2, maxRecDepth)])
+             [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth)])
       of nkIdent:
         if n.ident != nil:
           addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
@@ -355,27 +331,27 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
           addf(result, ",$N$1\"sons\": [", [istr])
           for i in countup(0, sonsLen(n) - 1):
             if i > 0: add(result, ",")
-            addf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(n.sons[i],
+            addf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(conf, n.sons[i],
                 marker, indent + 4, maxRecDepth - 1)])
           addf(result, "$N$1]", [istr])
       addf(result, ",$N$1\"typ\": $2",
-           [istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)])
+           [istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth)])
     addf(result, "$N$1}", [rspaces(indent)])
 
-proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope =
+proc treeToYaml(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope =
   var marker = initIntSet()
-  result = treeToYamlAux(n, marker, indent, maxRecDepth)
+  result = treeToYamlAux(conf, n, marker, indent, maxRecDepth)
 
-proc typeToYaml(n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope =
+proc typeToYaml(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope =
   var marker = initIntSet()
-  result = typeToYamlAux(n, marker, indent, maxRecDepth)
+  result = typeToYamlAux(conf, n, marker, indent, maxRecDepth)
 
-proc symToYaml(n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope =
+proc symToYaml(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope =
   var marker = initIntSet()
-  result = symToYamlAux(n, marker, indent, maxRecDepth)
+  result = symToYamlAux(conf, n, marker, indent, maxRecDepth)
 
-proc debugTree*(n: PNode, indent: int, maxRecDepth: int; renderType=false): Rope
-proc debugType(n: PType, maxRecDepth=100): Rope =
+proc debugTree*(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int; renderType=false): Rope
+proc debugType(conf: ConfigRef; n: PType, maxRecDepth=100): Rope =
   if n == nil:
     result = rope("null")
   else:
@@ -385,7 +361,7 @@ proc debugType(n: PType, maxRecDepth=100): Rope =
       add(result, n.sym.name.s)
     if n.kind in IntegralTypes and n.n != nil:
       add(result, ", node: ")
-      add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true))
+      add(result, debugTree(conf, n.n, 2, maxRecDepth-1, renderType=true))
     if (n.kind != tyString) and (sonsLen(n) > 0) and maxRecDepth != 0:
       add(result, "(")
       for i in countup(0, sonsLen(n) - 1):
@@ -393,13 +369,13 @@ proc debugType(n: PType, maxRecDepth=100): Rope =
         if n.sons[i] == nil:
           add(result, "null")
         else:
-          add(result, debugType(n.sons[i], maxRecDepth-1))
+          add(result, debugType(conf, n.sons[i], maxRecDepth-1))
       if n.kind == tyObject and n.n != nil:
         add(result, ", node: ")
-        add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true))
+        add(result, debugTree(conf, n.n, 2, maxRecDepth-1, renderType=true))
       add(result, ")")
 
-proc debugTree(n: PNode, indent: int, maxRecDepth: int;
+proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int;
                renderType=false): Rope =
   if n == nil:
     result = rope("null")
@@ -409,7 +385,7 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
              [istr, makeYamlString($n.kind)]
     when defined(useNodeIds):
       addf(result, ",$N$1\"id\": $2", [istr, rope(n.id)])
-    addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
+    addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
     if maxRecDepth != 0:
       addf(result, ",$N$1\"flags\": $2", [istr, rope($n.flags)])
       case n.kind
@@ -429,7 +405,7 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
         #     [istr, symToYaml(n.sym, indent, maxRecDepth),
         #     rope(n.sym.id)])
         if renderType and n.sym.typ != nil:
-          addf(result, ",$N$1\"typ\": $2", [istr, debugType(n.sym.typ, 2)])
+          addf(result, ",$N$1\"typ\": $2", [istr, debugType(conf, n.sym.typ, 2)])
       of nkIdent:
         if n.ident != nil:
           addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
@@ -440,27 +416,28 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
           addf(result, ",$N$1\"sons\": [", [istr])
           for i in countup(0, sonsLen(n) - 1):
             if i > 0: add(result, ",")
-            addf(result, "$N$1$2", [rspaces(indent + 4), debugTree(n.sons[i],
+            addf(result, "$N$1$2", [rspaces(indent + 4), debugTree(conf, n.sons[i],
                 indent + 4, maxRecDepth - 1, renderType)])
           addf(result, "$N$1]", [istr])
     addf(result, "$N$1}", [rspaces(indent)])
 
-proc debug(n: PSym) =
-  if n == nil:
-    echo("null")
-  elif n.kind == skUnknown:
-    echo("skUnknown")
-  else:
-    #writeLine(stdout, $symToYaml(n, 0, 1))
-    echo("$1_$2: $3, $4, $5, $6" % [
-      n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags),
-      $lineInfoToStr(n.info), $n.kind])
+when declared(echo):
+  proc debug(n: PSym; conf: ConfigRef) =
+    if n == nil:
+      echo("null")
+    elif n.kind == skUnknown:
+      echo("skUnknown")
+    else:
+      #writeLine(stdout, $symToYaml(n, 0, 1))
+      echo("$1_$2: $3, $4, $5, $6" % [
+        n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags),
+        $lineInfoToStr(conf, n.info), $n.kind])
 
-proc debug(n: PType) =
-  echo($debugType(n))
+  proc debug(n: PType; conf: ConfigRef) =
+    echo($debugType(conf, n))
 
-proc debug(n: PNode) =
-  echo($debugTree(n, 0, 100))
+  proc debug(n: PNode; conf: ConfigRef) =
+    echo($debugTree(conf, n, 0, 100))
 
 proc nextTry(h, maxHash: Hash): Hash =
   result = ((5 * h) + 1) and maxHash
@@ -468,7 +445,7 @@ proc nextTry(h, maxHash: Hash): Hash =
   # generates each int in range(maxHash) exactly once (see any text on
   # random-number generation for proof).
 
-proc objectSetContains(t: TObjectSet, obj: RootRef): bool =
+proc objectSetContains*(t: TObjectSet, obj: RootRef): bool =
   # returns true whether n is in t
   var h: Hash = hashNode(obj) and high(t.data) # start with real hash value
   while t.data[h] != nil:
@@ -492,12 +469,12 @@ proc objectSetEnlarge(t: var TObjectSet) =
     if t.data[i] != nil: objectSetRawInsert(n, t.data[i])
   swap(t.data, n)
 
-proc objectSetIncl(t: var TObjectSet, obj: RootRef) =
+proc objectSetIncl*(t: var TObjectSet, obj: RootRef) =
   if mustRehash(len(t.data), t.counter): objectSetEnlarge(t)
   objectSetRawInsert(t.data, obj)
   inc(t.counter)
 
-proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
+proc objectSetContainsOrIncl*(t: var TObjectSet, obj: RootRef): bool =
   # returns true if obj is already in the string table:
   var h: Hash = hashNode(obj) and high(t.data)
   while true:
@@ -515,7 +492,7 @@ proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool =
   inc(t.counter)
   result = false
 
-proc strTableContains(t: TStrTable, n: PSym): bool =
+proc strTableContains*(t: TStrTable, n: PSym): bool =
   var h: Hash = n.name.h and high(t.data) # start with real hash value
   while t.data[h] != nil:
     if (t.data[h] == n):
@@ -571,7 +548,7 @@ proc strTableEnlarge(t: var TStrTable) =
     if t.data[i] != nil: strTableRawInsert(n, t.data[i])
   swap(t.data, n)
 
-proc strTableAdd(t: var TStrTable, n: PSym) =
+proc strTableAdd*(t: var TStrTable, n: PSym) =
   if mustRehash(len(t.data), t.counter): strTableEnlarge(t)
   strTableRawInsert(t.data, n)
   inc(t.counter)
@@ -607,7 +584,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym; onConflictKeepOld=false): bool {.d
   inc(t.counter)
   result = false
 
-proc strTableGet(t: TStrTable, name: PIdent): PSym =
+proc strTableGet*(t: TStrTable, name: PIdent): PSym =
   var h: Hash = name.h and high(t.data)
   while true:
     result = t.data[h]
@@ -615,13 +592,13 @@ proc strTableGet(t: TStrTable, name: PIdent): PSym =
     if result.name.id == name.id: break
     h = nextTry(h, high(t.data))
 
-proc initIdentIter(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
-  ti.h = s.h
-  ti.name = s
-  if tab.counter == 0: result = nil
-  else: result = nextIdentIter(ti, tab)
 
-proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
+type
+  TIdentIter* = object # iterator over all syms with same identifier
+    h*: Hash           # current hash
+    name*: PIdent
+
+proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym =
   var h = ti.h and high(tab.data)
   var start = h
   result = tab.data[h]
@@ -634,6 +611,12 @@ proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
     result = tab.data[h]
   ti.h = nextTry(h, high(tab.data))
 
+proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
+  ti.h = s.h
+  ti.name = s
+  if tab.counter == 0: result = nil
+  else: result = nextIdentIter(ti, tab)
+
 proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable,
                          excluding: IntSet): PSym =
   var h: Hash = ti.h and high(tab.data)
@@ -657,20 +640,33 @@ proc firstIdentExcluding*(ti: var TIdentIter, tab: TStrTable, s: PIdent,
   if tab.counter == 0: result = nil
   else: result = nextIdentExcluding(ti, tab, excluding)
 
-proc initTabIter(ti: var TTabIter, tab: TStrTable): PSym =
-  ti.h = 0                    # we start by zero ...
-  if tab.counter == 0:
-    result = nil              # FIX 1: removed endless loop
-  else:
-    result = nextIter(ti, tab)
+type
+  TTabIter* = object
+    h: Hash
 
-proc nextIter(ti: var TTabIter, tab: TStrTable): PSym =
+proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym =
+  # usage:
+  # var
+  #   i: TTabIter
+  #   s: PSym
+  # s = InitTabIter(i, table)
+  # while s != nil:
+  #   ...
+  #   s = NextIter(i, table)
+  #
   result = nil
   while (ti.h <= high(tab.data)):
     result = tab.data[ti.h]
     inc(ti.h)                 # ... and increment by one always
     if result != nil: break
 
+proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym =
+  ti.h = 0
+  if tab.counter == 0:
+    result = nil
+  else:
+    result = nextIter(ti, tab)
+
 iterator items*(tab: TStrTable): PSym =
   var it: TTabIter
   var s = initTabIter(it, tab)
diff --git a/compiler/btrees.nim b/compiler/btrees.nim
new file mode 100644
index 000000000..6cd6e51f4
--- /dev/null
+++ b/compiler/btrees.nim
@@ -0,0 +1,233 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## BTree implementation with few features, but good enough for the
+## Nim compiler's needs.
+
+const
+  M = 512    # max children per B-tree node = M-1
+             # (must be even and greater than 2)
+  Mhalf = M div 2
+
+type
+  Node[Key, Val] = ref object
+    entries: int
+    keys: array[M, Key]
+    case isInternal: bool
+    of false:
+      vals: array[M, Val]
+    of true:
+      links: array[M, Node[Key, Val]]
+  BTree*[Key, Val] = object
+    root: Node[Key, Val]
+    entries: int      ## number of key-value pairs
+
+proc initBTree*[Key, Val](): BTree[Key, Val] =
+  BTree[Key, Val](root: Node[Key, Val](entries: 0, isInternal: false))
+
+template less(a, b): bool = cmp(a, b) < 0
+template eq(a, b): bool = cmp(a, b) == 0
+
+proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val =
+  var x = b.root
+  while x.isInternal:
+    for j in 0 ..< x.entries:
+      if j+1 == x.entries or less(key, x.keys[j+1]):
+        x = x.links[j]
+        break
+  assert(not x.isInternal)
+  for j in 0 ..< x.entries:
+    if eq(key, x.keys[j]): return x.vals[j]
+
+proc contains*[Key, Val](b: BTree[Key, Val], key: Key): bool =
+  var x = b.root
+  while x.isInternal:
+    for j in 0 ..< x.entries:
+      if j+1 == x.entries or less(key, x.keys[j+1]):
+        x = x.links[j]
+        break
+  assert(not x.isInternal)
+  for j in 0 ..< x.entries:
+    if eq(key, x.keys[j]): return true
+  return false
+
+proc copyHalf[Key, Val](h, result: Node[Key, Val]) =
+  for j in 0 ..< Mhalf:
+    result.keys[j] = h.keys[Mhalf + j]
+  if h.isInternal:
+    for j in 0 ..< Mhalf:
+      result.links[j] = h.links[Mhalf + j]
+  else:
+    for j in 0 ..< Mhalf:
+      shallowCopy(result.vals[j], h.vals[Mhalf + j])
+
+proc split[Key, Val](h: Node[Key, Val]): Node[Key, Val] =
+  ## split node in half
+  result = Node[Key, Val](entries: Mhalf, isInternal: h.isInternal)
+  h.entries = Mhalf
+  copyHalf(h, result)
+
+proc insert[Key, Val](h: Node[Key, Val], key: Key, val: Val): Node[Key, Val] =
+  #var t = Entry(key: key, val: val, next: nil)
+  var newKey = key
+  var j = 0
+  if not h.isInternal:
+    while j < h.entries:
+      if less(key, h.keys[j]): break
+      inc j
+    for i in countdown(h.entries, j+1):
+      shallowCopy(h.vals[i], h.vals[i-1])
+    h.vals[j] = val
+  else:
+    var newLink: Node[Key, Val] = nil
+    while j < h.entries:
+      if j+1 == h.entries or less(key, h.keys[j+1]):
+        let u = insert(h.links[j], key, val)
+        inc j
+        if u == nil: return nil
+        newKey = u.keys[0]
+        newLink = u
+        break
+      inc j
+    for i in countdown(h.entries, j+1):
+      h.links[i] = h.links[i-1]
+    h.links[j] = newLink
+
+  for i in countdown(h.entries, j+1):
+    h.keys[i] = h.keys[i-1]
+  h.keys[j] = newKey
+  inc h.entries
+  return if h.entries < M: nil else: split(h)
+
+proc add*[Key, Val](b: var BTree[Key, Val]; key: Key; val: Val) =
+  let u = insert(b.root, key, val)
+  inc b.entries
+  if u == nil: return
+
+  # need to split root
+  let t = Node[Key, Val](entries: 2, isInternal: true)
+  t.keys[0] = b.root.keys[0]
+  t.links[0] = b.root
+  t.keys[1] = u.keys[0]
+  t.links[1] = u
+  b.root = t
+
+proc toString[Key, Val](h: Node[Key, Val], indent: string; result: var string) =
+  if not h.isInternal:
+    for j in 0..<h.entries:
+      result.add(indent)
+      result.add($h.keys[j] & " " & $h.vals[j] & "\n")
+  else:
+    for j in 0..<h.entries:
+      if j > 0: result.add(indent & "(" & $h.keys[j] & ")\n")
+      toString(h.links[j], indent & "   ", result)
+
+proc `$`[Key, Val](b: BTree[Key, Val]): string =
+  result = ""
+  toString(b.root, "", result)
+
+proc hasNext*[Key, Val](b: BTree[Key, Val]; index: int): bool =
+  result = index < b.entries
+
+proc countSubTree[Key, Val](it: Node[Key, Val]): int =
+  if it.isInternal:
+    result = 0
+    for k in 0..<it.entries:
+      inc result, countSubTree(it.links[k])
+  else:
+    result = it.entries
+
+proc next*[Key, Val](b: BTree[Key, Val]; index: int): (Key, Val, int) =
+  var it = b.root
+  var i = index
+  # navigate to the right leaf:
+  while it.isInternal:
+    var sum = 0
+    for k in 0..<it.entries:
+      let c = countSubTree(it.links[k])
+      inc sum, c
+      if sum > i:
+        it = it.links[k]
+        dec i, (sum - c)
+        break
+  result = (it.keys[i], it.vals[i], index+1)
+
+iterator pairs*[Key, Val](b: BTree[Key, Val]): (Key, Val) =
+  var i = 0
+  while hasNext(b, i):
+    let (k, v, i2) = next(b, i)
+    i = i2
+    yield (k, v)
+
+proc len*[Key, Val](b: BTree[Key, Val]): int {.inline.} = b.entries
+
+when isMainModule:
+
+  import random, tables
+
+  proc main =
+    var st = initBTree[string, string]()
+    st.add("www.cs.princeton.edu", "abc")
+    st.add("www.princeton.edu",    "128.112.128.15")
+    st.add("www.yale.edu",         "130.132.143.21")
+    st.add("www.simpsons.com",     "209.052.165.60")
+    st.add("www.apple.com",        "17.112.152.32")
+    st.add("www.amazon.com",       "207.171.182.16")
+    st.add("www.ebay.com",         "66.135.192.87")
+    st.add("www.cnn.com",          "64.236.16.20")
+    st.add("www.google.com",       "216.239.41.99")
+    st.add("www.nytimes.com",      "199.239.136.200")
+    st.add("www.microsoft.com",    "207.126.99.140")
+    st.add("www.dell.com",         "143.166.224.230")
+    st.add("www.slashdot.org",     "66.35.250.151")
+    st.add("www.espn.com",         "199.181.135.201")
+    st.add("www.weather.com",      "63.111.66.11")
+    st.add("www.yahoo.com",        "216.109.118.65")
+
+    assert st.getOrDefault("www.cs.princeton.edu") == "abc"
+    assert st.getOrDefault("www.harvardsucks.com") == nil
+
+    assert st.getOrDefault("www.simpsons.com") == "209.052.165.60"
+    assert st.getOrDefault("www.apple.com") == "17.112.152.32"
+    assert st.getOrDefault("www.ebay.com") == "66.135.192.87"
+    assert st.getOrDefault("www.dell.com") == "143.166.224.230"
+    assert(st.entries == 16)
+
+    for k, v in st:
+      echo k, ": ", v
+
+    when false:
+      var b2 = initBTree[string, string]()
+      const iters = 10_000
+      for i in 1..iters:
+        b2.add($i, $(iters - i))
+      for i in 1..iters:
+        let x = b2.getOrDefault($i)
+        if x != $(iters - i):
+          echo "got ", x, ", but expected ", iters - i
+      echo b2.entries
+
+    when true:
+      var b2 = initBTree[int, string]()
+      var t2 = initTable[int, string]()
+      const iters = 100_000
+      for i in 1..iters:
+        let x = rand(high(int))
+        if not t2.hasKey(x):
+          doAssert b2.getOrDefault(x).len == 0, " what, tree has this element " & $x
+          t2[x] = $x
+          b2.add(x, $x)
+
+      doAssert b2.entries == t2.len
+      echo "unique entries ", b2.entries
+      for k, v in t2:
+        doAssert $k == v
+        doAssert b2.getOrDefault(k) == $k
+
+  main()
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 22733f6ac..2f9cc822b 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -24,7 +24,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
   # getUniqueType() is too expensive here:
   var typ = skipTypes(ri.sons[0].typ, abstractInst)
   if typ.sons[0] != nil:
-    if isInvalidReturnType(typ.sons[0]):
+    if isInvalidReturnType(p.config, typ.sons[0]):
       if params != nil: pl.add(~", ")
       # beware of 'result = p(result)'. We may need to allocate a temporary:
       if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
@@ -33,13 +33,13 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
         elif d.k notin {locTemp} and not hasNoInit(ri):
           # reset before pass as 'result' var:
           discard "resetLoc(p, d)"
-        add(pl, addrLoc(d))
+        add(pl, addrLoc(p.config, d))
         add(pl, ~");$n")
         line(p, cpsStmts, pl)
       else:
         var tmp: TLoc
         getTemp(p, typ.sons[0], tmp, needsInit=true)
-        add(pl, addrLoc(tmp))
+        add(pl, addrLoc(p.config, tmp))
         add(pl, ~");$n")
         line(p, cpsStmts, pl)
         genAssignment(p, d, tmp, {}) # no need for deep copying
@@ -101,7 +101,7 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
     let ty = skipTypes(a.t, abstractVar+{tyPtr})
     case ty.kind
     of tyArray:
-      let first = firstOrd(ty)
+      let first = firstOrd(p.config, ty)
       if first == 0:
         result = "($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)]
       else:
@@ -128,13 +128,13 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
       else:
         result = "$1->data, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p)]
     of tyArray:
-      result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]
+      result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
     of tyPtr, tyRef:
       case lastSon(a.t).kind
       of tyString, tySequence:
         result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)]
       of tyArray:
-        result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))]
+        result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]
       else:
         internalError(p.config, "openArrayLoc: " & typeToString(a.t))
     else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
@@ -151,9 +151,9 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
   elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
     var n = if n.kind != nkHiddenAddr: n else: n.sons[0]
     result = openArrayLoc(p, n)
-  elif ccgIntroducedPtr(param):
+  elif ccgIntroducedPtr(p.config, param):
     initLocExpr(p, n, a)
-    result = addrLoc(a)
+    result = addrLoc(p.config, a)
   elif p.module.compileToCpp and param.typ.kind == tyVar and
       n.kind == nkHiddenAddr:
     initLocExprSingleUse(p, n.sons[0], a)
@@ -163,7 +163,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
     if callee.kind == nkSym and
         {sfImportC, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportC} and
         {lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
-      result = addrLoc(a)
+      result = addrLoc(p.config, a)
     else:
       result = rdLoc(a)
   else:
@@ -230,7 +230,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
   let rawProc = getRawProcType(p, typ)
   let callPattern = if tfIterator in typ.flags: PatIter else: PatProc
   if typ.sons[0] != nil:
-    if isInvalidReturnType(typ.sons[0]):
+    if isInvalidReturnType(p.config, typ.sons[0]):
       if sonsLen(ri) > 1: add(pl, ~", ")
       # beware of 'result = p(result)'. We may need to allocate a temporary:
       if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
@@ -240,12 +240,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
         elif d.k notin {locTemp} and not hasNoInit(ri):
           # reset before pass as 'result' var:
           discard "resetLoc(p, d)"
-        add(pl, addrLoc(d))
+        add(pl, addrLoc(p.config, d))
         genCallPattern()
       else:
         var tmp: TLoc
         getTemp(p, typ.sons[0], tmp, needsInit=true)
-        add(pl, addrLoc(tmp))
+        add(pl, addrLoc(p.config, tmp))
         genCallPattern()
         genAssignment(p, d, tmp, {}) # no need for deep copying
     else:
@@ -508,20 +508,20 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
     add(pl, ~": ")
     add(pl, genArg(p, ri.sons[i], param, ri))
   if typ.sons[0] != nil:
-    if isInvalidReturnType(typ.sons[0]):
+    if isInvalidReturnType(p.config, typ.sons[0]):
       if sonsLen(ri) > 1: add(pl, ~" ")
       # beware of 'result = p(result)'. We always allocate a temporary:
       if d.k in {locTemp, locNone}:
         # We already got a temp. Great, special case it:
         if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true)
         add(pl, ~"Result: ")
-        add(pl, addrLoc(d))
+        add(pl, addrLoc(p.config, d))
         add(pl, ~"];$n")
         line(p, cpsStmts, pl)
       else:
         var tmp: TLoc
         getTemp(p, typ.sons[0], tmp, needsInit=true)
-        add(pl, addrLoc(tmp))
+        add(pl, addrLoc(p.config, tmp))
         add(pl, ~"];$n")
         line(p, cpsStmts, pl)
         genAssignment(p, d, tmp, {}) # no need for deep copying
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 20b68b0aa..0e8af5af5 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -59,7 +59,7 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
     else:
       result = rope("NIM_NIL")
   of nkStrLit..nkTripleStrLit:
-    case skipTypes(ty, abstractVarRange).kind
+    case skipTypes(ty, abstractVarRange + {tyStatic}).kind
     of tyNil:
       result = genNilStringLiteral(p.module, n.info)
     of tyString:
@@ -114,8 +114,8 @@ proc genRawSetData(cs: TBitSet, size: int): Rope =
 
 proc genSetNode(p: BProc, n: PNode): Rope =
   var cs: TBitSet
-  var size = int(getSize(n.typ))
-  toBitSet(n, cs)
+  var size = int(getSize(p.config, n.typ))
+  toBitSet(p.config, n, cs)
   if size > 8:
     let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
     result = p.module.tmpBase & rope(id)
@@ -185,13 +185,13 @@ proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     #    lineF(p, cpsStmts, '$1 = $2;$n', [rdLoc(dest), rdLoc(src)])
     if canFormAcycle(dest.t):
       linefmt(p, cpsStmts, "#asgnRef((void**) $1, $2);$n",
-              addrLoc(dest), rdLoc(src))
+              addrLoc(p.config, dest), rdLoc(src))
     else:
       linefmt(p, cpsStmts, "#asgnRefNoCycle((void**) $1, $2);$n",
-              addrLoc(dest), rdLoc(src))
+              addrLoc(p.config, dest), rdLoc(src))
   else:
     linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
-            addrLoc(dest), rdLoc(src))
+            addrLoc(p.config, dest), rdLoc(src))
 
 proc asgnComplexity(n: PNode): int =
   if n != nil:
@@ -260,13 +260,13 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
       useStringh(p.module)
       linefmt(p, cpsStmts,
            "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
-           addrLoc(dest), addrLoc(src), rdLoc(dest))
+           addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest))
     else:
       linefmt(p, cpsStmts, "#genericShallowAssign((void*)$1, (void*)$2, $3);$n",
-              addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t, dest.lode.info))
+              addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info))
   else:
     linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
-            addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t, dest.lode.info))
+            addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info))
 
 proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   # This function replaces all other methods for generating
@@ -284,7 +284,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
       genRefAssign(p, dest, src, flags)
     else:
       linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n",
-              addrLoc(dest), rdLoc(src),
+              addrLoc(p.config, dest), rdLoc(src),
               genTypeInfo(p.module, dest.t, dest.lode.info))
   of tyString:
     if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
@@ -301,7 +301,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
         linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", tmp.rdLoc)
       else:
         linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n",
-               addrLoc(dest), rdLoc(src))
+               addrLoc(p.config, dest), rdLoc(src))
   of tyProc:
     if needsComplexAssignment(dest.t):
       # optimize closure assignment:
@@ -346,7 +346,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     if needsComplexAssignment(dest.t):
       linefmt(p, cpsStmts,     # XXX: is this correct for arrays?
            "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
-           addrLoc(dest), addrLoc(src),
+           addrLoc(p.config, dest), addrLoc(p.config, src),
            genTypeInfo(p.module, dest.t, dest.lode.info))
     else:
       useStringh(p.module)
@@ -356,10 +356,10 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
            "$1 = $2;$n",
            rdLoc(dest), rdLoc(src))
   of tySet:
-    if mapType(ty) == ctArray:
+    if mapType(p.config, ty) == ctArray:
       useStringh(p.module)
       linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
-              rdLoc(dest), rdLoc(src), rope(getSize(dest.t)))
+              rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t)))
     else:
       linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
   of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString,
@@ -371,8 +371,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     #writeStackTrace()
     #echo p.currLineInfo, " requesting"
     linefmt(p, cpsStmts, "#memTrackerWrite((void*)$1, $2, $3, $4);$n",
-            addrLoc(dest), rope getSize(dest.t),
-            makeCString(p.currLineInfo.toFullPath),
+            addrLoc(p.config, dest), rope getSize(p.config, dest.t),
+            makeCString(toFullPath(p.config, p.currLineInfo)),
             rope p.currLineInfo.safeLineNm)
 
 proc genDeepCopy(p: BProc; dest, src: TLoc) =
@@ -381,31 +381,31 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) =
       var tmp: TLoc
       getTemp(p, a.t, tmp)
       genAssignment(p, tmp, a, {})
-      addrLoc(tmp)
+      addrLoc(p.config, tmp)
     else:
-      addrLoc(a)
+      addrLoc(p.config, a)
 
-  var ty = skipTypes(dest.t, abstractVarRange)
+  var ty = skipTypes(dest.t, abstractVarRange + {tyStatic})
   case ty.kind
   of tyPtr, tyRef, tyProc, tyTuple, tyObject, tyArray:
     # XXX optimize this
     linefmt(p, cpsStmts, "#genericDeepCopy((void*)$1, (void*)$2, $3);$n",
-            addrLoc(dest), addrLocOrTemp(src),
+            addrLoc(p.config, dest), addrLocOrTemp(src),
             genTypeInfo(p.module, dest.t, dest.lode.info))
   of tySequence, tyString:
     linefmt(p, cpsStmts, "#genericSeqDeepCopy($1, $2, $3);$n",
-            addrLoc(dest), rdLoc(src),
+            addrLoc(p.config, dest), rdLoc(src),
             genTypeInfo(p.module, dest.t, dest.lode.info))
   of tyOpenArray, tyVarargs:
     linefmt(p, cpsStmts,
          "#genericDeepCopyOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
-         addrLoc(dest), addrLocOrTemp(src),
+         addrLoc(p.config, dest), addrLocOrTemp(src),
          genTypeInfo(p.module, dest.t, dest.lode.info))
   of tySet:
-    if mapType(ty) == ctArray:
+    if mapType(p.config, ty) == ctArray:
       useStringh(p.module)
       linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
-              rdLoc(dest), rdLoc(src), rope(getSize(dest.t)))
+              rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t)))
     else:
       linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
   of tyPointer, tyChar, tyBool, tyEnum, tyCString,
@@ -491,15 +491,15 @@ proc unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
 
 proc binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc;
                             frmt: string): Rope =
-  var size = getSize(t)
-  let storage = if size < platform.intSize: rope("NI")
+  var size = getSize(p.config, t)
+  let storage = if size < p.config.target.intSize: rope("NI")
                 else: getTypeDesc(p.module, t)
   result = getTempName(p.module)
   linefmt(p, cpsLocals, "$1 $2;$n", storage, result)
   lineCg(p, cpsStmts, frmt, result, rdCharLoc(a), rdCharLoc(b))
-  if size < platform.intSize or t.kind in {tyRange, tyEnum}:
+  if size < p.config.target.intSize or t.kind in {tyRange, tyEnum}:
     linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseOverflow();$n",
-            result, intLiteral(firstOrd(t)), intLiteral(lastOrd(t)))
+            result, intLiteral(firstOrd(p.config, t)), intLiteral(lastOrd(p.config, t)))
 
 proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   const
@@ -547,8 +547,8 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   t = skipTypes(e.typ, abstractRange)
   if optOverflowCheck in p.options:
     linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n",
-            rdLoc(a), intLiteral(firstOrd(t)))
-  putIntoDest(p, d, e, opr[m] % [rdLoc(a), rope(getSize(t) * 8)])
+            rdLoc(a), intLiteral(firstOrd(p.config, t)))
+  putIntoDest(p, d, e, opr[m] % [rdLoc(a), rope(getSize(p.config, t) * 8)])
 
 proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   const
@@ -604,8 +604,8 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
   # 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
+  s = max(getSize(p.config, a.t), getSize(p.config, b.t)) * 8
+  k = getSize(p.config, a.t) * 8
   putIntoDest(p, d, e,
               binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s),
                                       getSimpleTypeDesc(p.module, e.typ), rope(k)])
@@ -658,7 +658,7 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   initLocExpr(p, e.sons[1], a)
   t = skipTypes(e.typ, abstractRange)
   putIntoDest(p, d, e,
-              unArithTab[op] % [rdLoc(a), rope(getSize(t) * 8),
+              unArithTab[op] % [rdLoc(a), rope(getSize(p.config, t) * 8),
                 getSimpleTypeDesc(p.module, e.typ)])
 
 proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
@@ -667,7 +667,7 @@ proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
       tfVarIsPtr notin skipTypes(typ, abstractInst).flags
 
 proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
-  let mt = mapType(e.sons[0].typ)
+  let mt = mapType(p.config, e.sons[0].typ)
   if mt in {ctArray, ctPtrToArray} and not enforceDeref:
     # XXX the amount of hacks for C's arrays is incredible, maybe we should
     # simply wrap them in a struct? --> Losing auto vectorization then?
@@ -726,12 +726,12 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) =
     initLocExpr(p, e.sons[0], a)
     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):
+  elif mapType(p.config, 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, addrLoc(a), a.storage)
+    putIntoDest(p, d, e, addrLoc(p.config, a), a.storage)
 
 template inheritLocation(d: var TLoc, a: TLoc) =
   if d.k == locNone: d.storage = a.storage
@@ -828,7 +828,7 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
   if optFieldCheck in p.options:
     var a: TLoc
     genRecordFieldAux(p, e.sons[0], d, a)
-    let ty = skipTypes(a.t, abstractInst)
+    let ty = skipTypes(a.t, abstractInst + tyUserTypeClasses)
     var r = rdLoc(a)
     let f = e.sons[0].sons[1].sym
     let field = lookupFieldAgain(p, ty, f, r)
@@ -846,21 +846,21 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   initLocExpr(p, x, a)
   initLocExpr(p, y, b)
   var ty = skipTypes(a.t, abstractVarRange + abstractPtrs + tyUserTypeClasses)
-  var first = intLiteral(firstOrd(ty))
+  var first = intLiteral(firstOrd(p.config, ty))
   # emit range check:
   if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags:
     if not isConstExpr(y):
       # semantic pass has already checked for const index expressions
-      if firstOrd(ty) == 0:
-        if (firstOrd(b.t) < firstOrd(ty)) or (lastOrd(b.t) > lastOrd(ty)):
+      if firstOrd(p.config, ty) == 0:
+        if (firstOrd(p.config, b.t) < firstOrd(p.config, ty)) or (lastOrd(p.config, b.t) > lastOrd(p.config, ty)):
           linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)) #raiseIndexError();$n",
-                  rdCharLoc(b), intLiteral(lastOrd(ty)))
+                  rdCharLoc(b), intLiteral(lastOrd(p.config, ty)))
       else:
         linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError();$n",
-                rdCharLoc(b), first, intLiteral(lastOrd(ty)))
+                rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)))
     else:
       let idx = getOrdValue(y)
-      if idx < firstOrd(ty) or idx > lastOrd(ty):
+      if idx < firstOrd(p.config, ty) or idx > lastOrd(p.config, ty):
         localError(p.config, x.info, "index out of bounds")
   d.inheritLocation(a)
   putIntoDest(p, d, n,
@@ -884,12 +884,12 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
       "((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))) #raiseIndexError();$n",
       rdLoc(a), rdLoc(b), rdLoc(arr))
   of tyArray:
-    let first = intLiteral(firstOrd(ty))
+    let first = intLiteral(firstOrd(p.config, ty))
     if tfUncheckedArray notin ty.flags:
       linefmt(p, cpsStmts,
         "if ($2-$1 != -1 && " &
         "($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)) #raiseIndexError();$n",
-        rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(ty)))
+        rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)))
   of tySequence, tyString:
     linefmt(p, cpsStmts,
       "if ($2-$1 != -1 && " &
@@ -985,7 +985,7 @@ proc genEcho(p: BProc, n: PNode) =
   # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)``
   # is threadsafe.
   internalAssert p.config, n.kind == nkBracket
-  if platform.targetOS == osGenode:
+  if p.config.target.targetOS == osGenode:
     # bypass libc and print directly to the Genode LOG session
     var args: Rope = nil
     var a: TLoc
@@ -1007,7 +1007,7 @@ proc genEcho(p: BProc, n: PNode) =
     when false:
       p.module.includeHeader("<stdio.h>")
       linefmt(p, cpsStmts, "printf($1$2);$n",
-              makeCString(repeat("%s", n.len) & tnl), args)
+              makeCString(repeat("%s", n.len) & "\L"), args)
       linefmt(p, cpsStmts, "fflush(stdout);$n")
 
 proc gcUsage(conf: ConfigRef; n: PNode) =
@@ -1121,7 +1121,7 @@ proc genReset(p: BProc, n: PNode) =
   var a: TLoc
   initLocExpr(p, n.sons[1], a)
   linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
-          addrLoc(a),
+          addrLoc(p.config, a),
           genTypeInfo(p.module, skipTypes(a.t, {tyVar}), n.info))
 
 proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) =
@@ -1306,7 +1306,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
   if d.k == locNone:
     getTemp(p, n.typ, d)
   # generate call to newSeq before adding the elements per hand:
-  let L = int(lengthOrd(n.sons[1].typ))
+  let L = int(lengthOrd(p.config, n.sons[1].typ))
   genNewSeqAux(p, d, intLiteral(L))
   initLocExpr(p, n.sons[1], a)
   # bug #5007; do not produce excessive C source code:
@@ -1420,7 +1420,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
     putIntoDest(p, d, e, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.storage)
   of tySet:
     putIntoDest(p, d, e, ropecg(p.module, "#reprSet($1, $2)", [
-                addrLoc(a), genTypeInfo(p.module, t, e.info)]), a.storage)
+                addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]), a.storage)
   of tyOpenArray, tyVarargs:
     var b: TLoc
     case a.t.kind
@@ -1431,7 +1431,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
                   "$1->data, ($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)], a.storage)
     of tyArray:
       putIntoDest(p, b, e,
-                  "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.storage)
+                  "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))], a.storage)
     else: internalError(p.config, e.sons[0].info, "genRepr()")
     putIntoDest(p, d, e,
         ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b),
@@ -1444,7 +1444,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
     localError(p.config, e.info, "'repr' doesn't support 'void' type")
   else:
     putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)",
-                              [addrLoc(a), genTypeInfo(p.module, t, e.info)]),
+                              [addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]),
                                a.storage)
   gcUsage(p.config, e)
 
@@ -1498,8 +1498,8 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     putIntoDest(p, d, e, tmp.r)
   of tyArray:
     # YYY: length(sideeffect) is optimized away incorrectly?
-    if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(typ)))
-    else: putIntoDest(p, d, e, rope(lengthOrd(typ)))
+    if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(p.config, typ)))
+    else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ)))
   else: internalError(p.config, e.info, "genArrayLen()")
 
 proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
@@ -1537,19 +1537,19 @@ proc genSwap(p: BProc, e: PNode, d: var TLoc) =
   genAssignment(p, a, b, {})
   genAssignment(p, b, tmp, {})
 
-proc rdSetElemLoc(a: TLoc, setType: PType): Rope =
+proc rdSetElemLoc(conf: ConfigRef; a: TLoc, setType: PType): Rope =
   # read a location of an set element; it may need a subtraction operation
   # before the set operation
   result = rdCharLoc(a)
   assert(setType.kind == tySet)
-  if firstOrd(setType) != 0:
-    result = "($1- $2)" % [result, rope(firstOrd(setType))]
+  if firstOrd(conf, setType) != 0:
+    result = "($1- $2)" % [result, rope(firstOrd(conf, setType))]
 
-proc fewCmps(s: PNode): bool =
+proc fewCmps(conf: ConfigRef; s: PNode): bool =
   # this function estimates whether it is better to emit code
   # for constructing the set or generating a bunch of comparisons directly
   if s.kind != nkCurly: return false
-  if (getSize(s.typ) <= platform.intSize) and (nfAllConst in s.flags):
+  if (getSize(conf, s.typ) <= conf.target.intSize) and (nfAllConst in s.flags):
     result = false            # it is better to emit the set generation code
   elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}:
     result = true             # better not emit the set if int is basetype!
@@ -1557,10 +1557,10 @@ 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, frmt % [rdLoc(a), rdSetElemLoc(b, a.t)])
+  putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(p.config, b, a.t)])
 
 proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
-  case int(getSize(skipTypes(e.sons[1].typ, abstractVar)))
+  case int(getSize(p.config, skipTypes(e.sons[1].typ, abstractVar)))
   of 1: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&7U)))!=0)")
   of 2: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&15U)))!=0)")
   of 4: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&31U)))!=0)")
@@ -1572,11 +1572,11 @@ proc binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) =
   assert(d.k == locNone)
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
-  lineF(p, cpsStmts, frmt, [rdLoc(a), rdSetElemLoc(b, a.t)])
+  lineF(p, cpsStmts, frmt, [rdLoc(a), rdSetElemLoc(p.config, b, a.t)])
 
 proc genInOp(p: BProc, e: PNode, d: var TLoc) =
   var a, b, x, y: TLoc
-  if (e.sons[1].kind == nkCurly) and fewCmps(e.sons[1]):
+  if (e.sons[1].kind == nkCurly) and fewCmps(p.config, e.sons[1]):
     # a set constructor but not a constant set:
     # do not emit the set, but generate a bunch of comparisons; and if we do
     # so, we skip the unnecessary range check: This is a semantical extension
@@ -1620,7 +1620,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       "&", "|", "& ~", "^"]
   var a, b, i: TLoc
   var setType = skipTypes(e.sons[1].typ, abstractVar)
-  var size = int(getSize(setType))
+  var size = int(getSize(p.config, setType))
   case size
   of 1, 2, 4, 8:
     case op
@@ -1687,7 +1687,7 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
   let etyp = skipTypes(e.typ, abstractRange)
   if etyp.kind in ValueTypes and lfIndirect notin a.flags:
     putIntoDest(p, d, e, "(*($1*) ($2))" %
-        [getTypeDesc(p.module, e.typ), addrLoc(a)], a.storage)
+        [getTypeDesc(p.module, e.typ), addrLoc(p.config, a)], a.storage)
   elif etyp.kind == tyProc and etyp.callConv == ccClosure:
     putIntoDest(p, d, e, "(($1) ($2))" %
         [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage)
@@ -1924,7 +1924,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
     putIntoDest(p, d, e, genSetNode(p, e))
   else:
     if d.k == locNone: getTemp(p, e.typ, d)
-    if getSize(e.typ) > 8:
+    if getSize(p.config, e.typ) > 8:
       # big set:
       useStringh(p.module)
       lineF(p, cpsStmts, "memset($1, 0, sizeof($2));$n",
@@ -1936,14 +1936,14 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
           initLocExpr(p, it.sons[1], b)
           lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
               "$2[(NU)($1)>>3] |=(1U<<((NU)($1)&7U));$n", [rdLoc(idx), rdLoc(d),
-              rdSetElemLoc(a, e.typ), rdSetElemLoc(b, e.typ)])
+              rdSetElemLoc(p.config, a, e.typ), rdSetElemLoc(p.config, b, e.typ)])
         else:
           initLocExpr(p, it, a)
           lineF(p, cpsStmts, "$1[(NU)($2)>>3] |=(1U<<((NU)($2)&7U));$n",
-               [rdLoc(d), rdSetElemLoc(a, e.typ)])
+               [rdLoc(d), rdSetElemLoc(p.config, a, e.typ)])
     else:
       # small set
-      var ts = "NU" & $(getSize(e.typ) * 8)
+      var ts = "NU" & $(getSize(p.config, e.typ) * 8)
       lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)])
       for it in e.sons:
         if it.kind == nkRange:
@@ -1952,13 +1952,13 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
           initLocExpr(p, it.sons[1], b)
           lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" &
               "$2 |=((" & ts & ")(1)<<(($1)%(sizeof(" & ts & ")*8)));$n", [
-              rdLoc(idx), rdLoc(d), rdSetElemLoc(a, e.typ),
-              rdSetElemLoc(b, e.typ)])
+              rdLoc(idx), rdLoc(d), rdSetElemLoc(p.config, a, e.typ),
+              rdSetElemLoc(p.config, b, e.typ)])
         else:
           initLocExpr(p, it, a)
           lineF(p, cpsStmts,
                "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n",
-               [rdLoc(d), rdSetElemLoc(a, e.typ)])
+               [rdLoc(d), rdSetElemLoc(p.config, a, e.typ)])
 
 proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
   var rec: TLoc
@@ -2076,7 +2076,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
                 "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
   else:
     putIntoDest(p, d, n, "(*($1*) ($2))" %
-                        [getTypeDesc(p.module, dest), addrLoc(a)], a.storage)
+                        [getTypeDesc(p.module, dest), addrLoc(p.config, a)], a.storage)
 
 proc downConv(p: BProc, n: PNode, d: var TLoc) =
   if p.module.compileToCpp:
@@ -2297,7 +2297,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
         incl a.flags, lfSingleUse
         genCall(p, ex, a)
         if lfSingleUse notin a.flags:
-          line(p, cpsStmts, a.r & ";" & tnl)
+          line(p, cpsStmts, a.r & ";\L")
       else:
         initLocExpr(p, ex, a)
   of nkAsmStmt: genAsmStmt(p, n)
@@ -2313,7 +2313,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     genTypeSection(p.module, n)
   of nkCommentStmt, nkIteratorDef, nkIncludeStmt,
      nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
-     nkFromStmt, nkTemplateDef, nkMacroDef:
+     nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt:
     discard
   of nkPragma: genPragma(p, n)
   of nkPragmaBlock: expr(p, n.lastSon, d)
@@ -2367,7 +2367,7 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
     result.add "}"
   of tyArray: result = rope"{}"
   of tySet:
-    if mapType(t) == ctArray: result = rope"{}"
+    if mapType(p.config, t) == ctArray: result = rope"{}"
     else: result = rope"0"
   else:
     globalError(p.config, info, "cannot create null element for: " & $t.kind)
@@ -2436,7 +2436,7 @@ proc genConstSimpleList(p: BProc, n: PNode): Rope =
   addf(result, "}$n", [])
 
 proc genConstSeq(p: BProc, n: PNode, t: PType): Rope =
-  var data = "{{$1, $1}" % [n.len.rope]
+  var data = "{{$1, $1 | NIM_STRLIT_FLAG}" % [n.len.rope]
   if n.len > 0:
     # array part needs extra curlies:
     data.add(", {")
@@ -2464,8 +2464,8 @@ proc genConstExpr(p: BProc, n: PNode): Rope =
     result = genConstExpr(p, n.sons[1])
   of nkCurly:
     var cs: TBitSet
-    toBitSet(n, cs)
-    result = genRawSetData(cs, int(getSize(n.typ)))
+    toBitSet(p.config, n, cs)
+    result = genRawSetData(cs, int(getSize(p.config, n.typ)))
   of nkBracket, nkPar, nkTupleConstr, nkClosure:
     var t = skipTypes(n.typ, abstractInst)
     if t.kind == tySequence:
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
index 4b4f9c0e6..664f89b73 100644
--- a/compiler/ccgmerge.nim
+++ b/compiler/ccgmerge.nim
@@ -47,34 +47,32 @@ const
 
 proc genSectionStart*(fs: TCFileSection; conf: ConfigRef): Rope =
   if compilationCachePresent(conf):
-    result = rope(tnl)
-    add(result, "/*\t")
+    result = nil
+    add(result, "\n/*\t")
     add(result, CFileSectionNames[fs])
-    add(result, ":*/")
-    add(result, tnl)
+    add(result, ":*/\n")
 
 proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
   if compilationCachePresent(conf):
-    result = rope(NimMergeEndMark & tnl)
+    result = rope(NimMergeEndMark & "\n")
 
 proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
   if compilationCachePresent(conf):
-    result = rope(tnl)
-    add(result, "/*\t")
+    result = rope(nil)
+    add(result, "\n/*\t")
     add(result, CProcSectionNames[ps])
-    add(result, ":*/")
-    add(result, tnl)
+    add(result, ":*/\n")
 
 proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope =
   if compilationCachePresent(conf):
-    result = rope(NimMergeEndMark & tnl)
+    result = rope(NimMergeEndMark & "\n")
 
 proc writeTypeCache(a: TypeCache, s: var string) =
   var i = 0
   for id, value in pairs(a):
     if i == 10:
       i = 0
-      s.add(tnl)
+      s.add('\L')
     else:
       s.add(' ')
     encodeStr($id, s)
@@ -88,7 +86,7 @@ proc writeIntSet(a: IntSet, s: var string) =
   for x in items(a):
     if i == 10:
       i = 0
-      s.add(tnl)
+      s.add('\L')
     else:
       s.add(' ')
     encodeVInt(x, s)
@@ -97,8 +95,7 @@ proc writeIntSet(a: IntSet, s: var string) =
 
 proc genMergeInfo*(m: BModule): Rope =
   if not compilationCachePresent(m.config): return nil
-  var s = "/*\tNIM_merge_INFO:"
-  s.add(tnl)
+  var s = "/*\tNIM_merge_INFO:\n"
   s.add("typeCache:{")
   writeTypeCache(m.typeCache, s)
   s.add("declared:{")
@@ -110,8 +107,7 @@ proc genMergeInfo*(m: BModule): Rope =
   encodeVInt(m.labels, s)
   s.add(" flags:")
   encodeVInt(cast[int](m.flags), s)
-  s.add(tnl)
-  s.add("*/")
+  s.add("\n*/")
   result = s.rope
 
 template `^`(pos: int): untyped = L.buf[pos]
@@ -155,11 +151,11 @@ proc readVerbatimSection(L: var TBaseLexer): Rope =
     of CR:
       pos = nimlexbase.handleCR(L, pos)
       buf = L.buf
-      r.add(tnl)
+      r.add('\L')
     of LF:
       pos = nimlexbase.handleLF(L, pos)
       buf = L.buf
-      r.add(tnl)
+      r.add('\L')
     of '\0':
       doAssert(false, "ccgmerge: expected: " & NimMergeEndMark)
       break
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index f99ee9270..f9654bb1f 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -28,9 +28,9 @@ proc registerGcRoot(p: BProc, v: PSym) =
       appcg(p.module, p.module.initProc.procSec(cpsInit),
         "#nimRegisterGlobalMarker($1);$n", [prc])
 
-proc isAssignedImmediately(n: PNode): bool {.inline.} =
+proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} =
   if n.kind == nkEmpty: return false
-  if isInvalidReturnType(n.typ):
+  if isInvalidReturnType(conf, n.typ):
     # var v = f()
     # is transformed into: var v;  f(addr v)
     # where 'f' **does not** initialize the result!
@@ -65,7 +65,7 @@ proc genVarTuple(p: BProc, n: PNode) =
       registerGcRoot(p, v)
     else:
       assignLocalVar(p, vn)
-      initLocalVar(p, v, immediateAsgn=isAssignedImmediately(n[L-1]))
+      initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[L-1]))
     initLoc(field, locExpr, vn, tup.storage)
     if t.kind == tyTuple:
       field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
@@ -205,7 +205,7 @@ proc genGotoState(p: BProc, n: PNode) =
     howManyTrys    = p.nestedTryStmts.len,
     howManyExcepts = p.inExceptBlockLen)
   lineF(p, cpsStmts, " goto BeforeRet_;$n", [])
-  var statesCounter = lastOrd(n.sons[0].typ)
+  var statesCounter = lastOrd(p.config, n.sons[0].typ)
   if n.len >= 2 and n[1].kind == nkIntLit:
     statesCounter = n[1].intVal
   let prefix = if n.len == 3 and n[2].kind == nkStrLit: n[2].strVal.rope
@@ -264,7 +264,7 @@ proc genSingleVar(p: BProc, a: PNode) =
     registerGcRoot(p, v)
   else:
     let value = a.sons[2]
-    let imm = isAssignedImmediately(value)
+    let imm = isAssignedImmediately(p.config, value)
     if imm and p.module.compileToCpp and p.splitDecls == 0 and
         not containsHiddenPointer(v.typ):
       # C++ really doesn't like things like 'Foo f; f = x' as that invokes a
@@ -404,12 +404,12 @@ proc genComputedGoto(p: BProc; n: PNode) =
         localError(p.config, it.info,
             "case statement must be exhaustive for computed goto"); return
       casePos = i
-      let aSize = lengthOrd(it.sons[0].typ)
+      let aSize = lengthOrd(p.config, it.sons[0].typ)
       if aSize > 10_000:
         localError(p.config, it.info,
             "case statement has too many cases for computed goto"); return
       arraySize = aSize.int
-      if firstOrd(it.sons[0].typ) != 0:
+      if firstOrd(p.config, it.sons[0].typ) != 0:
         localError(p.config, it.info,
             "case statement has to start at 0 for computed goto"); return
   if casePos < 0:
@@ -480,7 +480,7 @@ proc genWhileStmt(p: BProc, t: PNode) =
       lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), label])
     var loopBody = t.sons[1]
     if loopBody.stmtsContainPragma(wComputedGoto) and
-        hasComputedGoto in CC[cCompiler].props:
+        hasComputedGoto in CC[p.config.cCompiler].props:
       # for closure support weird loop bodies are generated:
       if loopBody.len == 2 and loopBody.sons[0].kind == nkEmpty:
         loopBody = loopBody.sons[1]
@@ -653,7 +653,7 @@ proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
     assert(b.sons[i].kind != nkRange)
     initLocExpr(p, b.sons[i], x)
     assert(b.sons[i].kind in {nkStrLit..nkTripleStrLit})
-    var j = int(hashString(b.sons[i].strVal) and high(branches))
+    var j = int(hashString(p.config, b.sons[i].strVal) and high(branches))
     appcg(p.module, branches[j], "if (#eqStrings($1, $2)) goto $3;$n",
          [rdLoc(e), rdLoc(x), labl])
 
@@ -706,7 +706,7 @@ proc ifSwitchSplitPoint(p: BProc, n: PNode): int =
     var stmtBlock = lastSon(branch)
     if stmtBlock.stmtsContainPragma(wLinearScanEnd):
       result = i
-    elif hasSwitchRange notin CC[cCompiler].props:
+    elif hasSwitchRange notin CC[p.config.cCompiler].props:
       if branch.kind == nkOfBranch and branchHasTooBigRange(branch):
         result = i
 
@@ -714,7 +714,7 @@ proc genCaseRange(p: BProc, branch: PNode) =
   var length = branch.len
   for j in 0 .. length-2:
     if branch[j].kind == nkRange:
-      if hasSwitchRange in CC[cCompiler].props:
+      if hasSwitchRange in CC[p.config.cCompiler].props:
         lineF(p, cpsStmts, "case $1 ... $2:$n", [
             genLiteral(p, branch[j][0]),
             genLiteral(p, branch[j][1])])
@@ -754,7 +754,7 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
         hasDefault = true
       exprBlock(p, branch.lastSon, d)
       lineF(p, cpsStmts, "break;$n", [])
-    if (hasAssume in CC[cCompiler].props) and not hasDefault:
+    if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault:
       lineF(p, cpsStmts, "default: __assume(0);$n", [])
     lineF(p, cpsStmts, "}$n", [])
   if lend != nil: fixLabel(p, lend)
@@ -975,21 +975,22 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
       initLocExpr(p, it, a)
       res.add($a.rdLoc)
 
-  if isAsmStmt and hasGnuAsm in CC[cCompiler].props:
+  if isAsmStmt and hasGnuAsm in CC[p.config.cCompiler].props:
     for x in splitLines(res):
       var j = 0
-      while x[j] in {' ', '\t'}: inc(j)
-      if x[j] in {'"', ':'}:
-        # don't modify the line if already in quotes or
-        # some clobber register list:
-        add(result, x); add(result, tnl)
-      elif x[j] != '\0':
-        # ignore empty lines
-        add(result, "\"")
-        add(result, x)
-        add(result, "\\n\"\n")
+      while j < x.len and x[j] in {' ', '\t'}: inc(j)
+      if j < x.len:
+        if x[j] in {'"', ':'}:
+          # don't modify the line if already in quotes or
+          # some clobber register list:
+          add(result, x); add(result, "\L")
+        else:
+          # ignore empty lines
+          add(result, "\"")
+          add(result, x)
+          add(result, "\\n\"\n")
   else:
-    res.add(tnl)
+    res.add("\L")
     result = res.rope
 
 proc genAsmStmt(p: BProc, t: PNode) =
@@ -1001,9 +1002,9 @@ proc genAsmStmt(p: BProc, t: PNode) =
   # work:
   if p.prc == nil:
     # top level asm statement?
-    addf(p.module.s[cfsProcHeaders], CC[cCompiler].asmStmtFrmt, [s])
+    addf(p.module.s[cfsProcHeaders], CC[p.config.cCompiler].asmStmtFrmt, [s])
   else:
-    lineF(p, cpsStmts, CC[cCompiler].asmStmtFrmt, [s])
+    lineF(p, cpsStmts, CC[p.config.cCompiler].asmStmtFrmt, [s])
 
 proc determineSection(n: PNode): TCFileSection =
   result = cfsProcHeaders
@@ -1036,7 +1037,7 @@ proc genBreakPoint(p: BProc, t: PNode) =
     genLineDir(p, t)          # BUGFIX
     appcg(p.module, p.module.g.breakpoints,
          "#dbgRegisterBreakpoint($1, (NCSTRING)$2, (NCSTRING)$3);$n", [
-        rope(toLinenumber(t.info)), makeCString(toFilename(t.info)),
+        rope(toLinenumber(t.info)), makeCString(toFilename(p.config, t.info)),
         makeCString(name)])
 
 proc genWatchpoint(p: BProc, n: PNode) =
@@ -1045,7 +1046,7 @@ proc genWatchpoint(p: BProc, n: PNode) =
   initLocExpr(p, n.sons[1], a)
   let typ = skipTypes(n.sons[1].typ, abstractVarRange)
   lineCg(p, cpsStmts, "#dbgRegisterWatchpoint($1, (NCSTRING)$2, $3);$n",
-        [a.addrLoc, makeCString(renderTree(n.sons[1])),
+        [addrLoc(p.config, a), makeCString(renderTree(n.sons[1])),
         genTypeInfo(p.module, typ, n.info)])
 
 proc genPragma(p: BProc, n: PNode) =
@@ -1076,7 +1077,7 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
   var t = skipTypes(objtype, abstractVar)
   assert t.kind == tyObject
   discard genTypeInfo(p.module, t, a.lode.info)
-  var L = lengthOrd(field.typ)
+  var L = lengthOrd(p.config, field.typ)
   if not containsOrIncl(p.module.declaredThings, field.id):
     appcg(p.module, cfsVars, "extern $1",
           discriminatorTableDecl(p.module, t, field))
diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim
index da5c624b7..3e8a87041 100644
--- a/compiler/ccgthreadvars.nim
+++ b/compiler/ccgthreadvars.nim
@@ -23,27 +23,14 @@ proc accessThreadLocalVar(p: BProc, s: PSym) =
     add(p.procSec(cpsInit),
       ropecg(p.module, "\tNimTV_ = (NimThreadVars*) #GetThreadLocalVars();$n"))
 
-var
-  nimtv: Rope                 # Nim thread vars; the struct body
-  nimtvDeps: seq[PType] = @[]  # type deps: every module needs whole struct
-  nimtvDeclared = initIntSet() # so that every var/field exists only once
-                               # in the struct
-
-# 'nimtv' is incredibly hard to modularize! Best effort is to store all thread
-# vars in a ROD section and with their type deps and load them
-# unconditionally...
-
-# nimtvDeps is VERY hard to cache because it's not a list of IDs nor can it be
-# made to be one.
-
 proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
   if emulatedThreadVars(m.config):
     # we gather all thread locals var into a struct; we need to allocate
     # storage for that somehow, can't use the thread local storage
     # allocator for it :-(
-    if not containsOrIncl(nimtvDeclared, s.id):
-      nimtvDeps.add(s.loc.t)
-      addf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
+    if not containsOrIncl(m.g.nimtvDeclared, s.id):
+      m.g.nimtvDeps.add(s.loc.t)
+      addf(m.g.nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
   else:
     if isExtern: add(m.s[cfsVars], "extern ")
     if optThreads in m.config.globalOptions: add(m.s[cfsVars], "NIM_THREADVAR ")
@@ -51,12 +38,12 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
     addf(m.s[cfsVars], " $1;$n", [s.loc.r])
 
 proc generateThreadLocalStorage(m: BModule) =
-  if nimtv != nil and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
-    for t in items(nimtvDeps): discard getTypeDesc(m, t)
-    addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv])
+  if m.g.nimtv != nil and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
+    for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t)
+    addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
 
 proc generateThreadVarsSize(m: BModule) =
-  if nimtv != nil:
+  if m.g.nimtv != nil:
     let externc = if m.config.cmd == cmdCompileToCpp or
                        sfCompileToCpp in m.module.flags: "extern \"C\" "
                   else: ""
diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim
index c265064a1..4514ce7dc 100644
--- a/compiler/ccgtrav.nim
+++ b/compiler/ccgtrav.nim
@@ -70,7 +70,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
      tySink:
     genTraverseProc(c, accessor, lastSon(typ))
   of tyArray:
-    let arraySize = lengthOrd(typ.sons[0])
+    let arraySize = lengthOrd(c.p.config, typ.sons[0])
     var i: TLoc
     getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i)
     let oldCode = p.s(cpsStmts)
@@ -122,7 +122,6 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
   var p = newProc(nil, m)
   result = "Marker_" & getTypeName(m, origTyp, sig)
   var typ = origTyp.skipTypes(abstractInst)
-  if typ.kind == tyOpt: typ = optLowering(typ)
 
   let header = "static N_NIMCALL(void, $1)(void* p, NI op)" % [result]
 
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 7b44cddad..82508e37e 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -50,7 +50,7 @@ proc mangleName(m: BModule; s: PSym): Rope =
     result = s.name.s.mangle.rope
     add(result, idOrSig(s, m.module.name.s, m.sigConflicts))
     s.loc.r = result
-    writeMangledName(m.ndi, s)
+    writeMangledName(m.ndi, s, m.config)
 
 proc mangleParamName(m: BModule; s: PSym): Rope =
   ## we cannot use 'sigConflicts' here since we have a BModule, not a BProc.
@@ -63,7 +63,7 @@ proc mangleParamName(m: BModule; s: PSym): Rope =
       res.add "_0"
     result = res.rope
     s.loc.r = result
-    writeMangledName(m.ndi, s)
+    writeMangledName(m.ndi, s, m.config)
 
 proc mangleLocalName(p: BProc; s: PSym): Rope =
   assert s.kind in skLocalVars+{skTemp}
@@ -81,7 +81,7 @@ proc mangleLocalName(p: BProc; s: PSym): Rope =
       result.add "_" & rope(counter+1)
     p.sigConflicts.inc(key)
     s.loc.r = result
-    if s.kind != skTemp: writeMangledName(p.module.ndi, s)
+    if s.kind != skTemp: writeMangledName(p.module.ndi, s, p.config)
 
 proc scopeMangledParam(p: BProc; param: PSym) =
   ## parameter generation only takes BModule, not a BProc, so we have to
@@ -124,40 +124,40 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
   result = typ.loc.r
   if result == nil: internalError(m.config, "getTypeName: " & $typ.kind)
 
-proc mapSetType(typ: PType): TCTypeKind =
-  case int(getSize(typ))
+proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
+  case int(getSize(conf, typ))
   of 1: result = ctInt8
   of 2: result = ctInt16
   of 4: result = ctInt32
   of 8: result = ctInt64
   else: result = ctArray
 
-proc mapType(typ: PType): TCTypeKind =
+proc mapType(conf: ConfigRef; typ: PType): TCTypeKind =
   ## Maps a Nim type to a C type
   case typ.kind
   of tyNone, tyStmt: result = ctVoid
   of tyBool: result = ctBool
   of tyChar: result = ctChar
-  of tySet: result = mapSetType(typ)
+  of tySet: result = mapSetType(conf, typ)
   of tyOpenArray, tyArray, tyVarargs: result = ctArray
   of tyObject, tyTuple: result = ctStruct
   of tyUserTypeClasses:
     doAssert typ.isResolvedUserTypeClass
-    return mapType(typ.lastSon)
+    return mapType(conf, typ.lastSon)
   of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
      tyTypeDesc, tyAlias, tySink, tyInferred:
-    result = mapType(lastSon(typ))
+    result = mapType(conf, lastSon(typ))
   of tyEnum:
-    if firstOrd(typ) < 0:
+    if firstOrd(conf, typ) < 0:
       result = ctInt32
     else:
-      case int(getSize(typ))
+      case int(getSize(conf, typ))
       of 1: result = ctUInt8
       of 2: result = ctUInt16
       of 4: result = ctInt32
       of 8: result = ctInt64
       else: result = ctInt32
-  of tyRange: result = mapType(typ.sons[0])
+  of tyRange: result = mapType(conf, typ.sons[0])
   of tyPtr, tyVar, tyLent, tyRef, tyOptAsRef:
     var base = skipTypes(typ.lastSon, typedescInst)
     case base.kind
@@ -169,27 +169,20 @@ 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
   of tyInt..tyUInt64:
     result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt))
   of tyStatic:
-    if typ.n != nil: result = mapType(lastSon typ)
+    if typ.n != nil: result = mapType(conf, lastSon typ)
     else: doAssert(false, "mapType")
   else: doAssert(false, "mapType")
 
-proc mapReturnType(typ: PType): TCTypeKind =
+proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind =
   #if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr
   #else:
-  result = mapType(typ)
+  result = mapType(conf, typ)
 
 proc isImportedType(t: PType): bool =
   result = t.sym != nil and sfImportc in t.sym.flags
@@ -207,14 +200,14 @@ proc isObjLackingTypeField(typ: PType): bool {.inline.} =
   result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and
       (typ.sons[0] == nil) or isPureObject(typ))
 
-proc isInvalidReturnType(rettype: PType): bool =
+proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool =
   # Arrays and sets cannot be returned by a C procedure, because C is
   # such a poor programming language.
   # We exclude records with refs too. This enhances efficiency and
   # is necessary for proper code generation of assignments.
   if rettype == nil: result = true
   else:
-    case mapType(rettype)
+    case mapType(conf, rettype)
     of ctArray:
       result = not (skipTypes(rettype, typedescInst).kind in
           {tyVar, tyLent, tyRef, tyPtr})
@@ -240,9 +233,9 @@ proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
 
 proc addAbiCheck(m: BModule, t: PType, name: Rope) =
   if isDefined(m.config, "checkabi"):
-    addf(m.s[cfsTypeInfo], "NIM_CHECK_SIZE($1, $2);$n", [name, rope(getSize(t))])
+    addf(m.s[cfsTypeInfo], "NIM_CHECK_SIZE($1, $2);$n", [name, rope(getSize(m.config, t))])
 
-proc ccgIntroducedPtr(s: PSym): bool =
+proc ccgIntroducedPtr(conf: ConfigRef; s: PSym): bool =
   var pt = skipTypes(s.typ, typedescInst)
   assert skResult != s.kind
   if tfByRef in pt.flags: return true
@@ -252,7 +245,7 @@ proc ccgIntroducedPtr(s: PSym): bool =
     if s.typ.sym != nil and sfForward in s.typ.sym.flags:
       # forwarded objects are *always* passed by pointers for consistency!
       result = true
-    elif (optByRef in s.options) or (getSize(pt) > platform.floatSize * 3):
+    elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3):
       result = true           # requested anyway
     elif (tfFinal in pt.flags) and (pt.sons[0] == nil):
       result = false          # no need, because no subtyping possible
@@ -260,14 +253,14 @@ proc ccgIntroducedPtr(s: PSym): bool =
       result = true           # ordinary objects are always passed by reference,
                               # otherwise casting doesn't work
   of tyTuple:
-    result = (getSize(pt) > platform.floatSize*3) or (optByRef in s.options)
+    result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options)
   else: result = false
 
-proc fillResult(param: PNode) =
+proc fillResult(conf: ConfigRef; param: PNode) =
   fillLoc(param.sym.loc, locParam, param, ~"Result",
           OnStack)
   let t = param.sym.typ
-  if mapReturnType(t) != ctArray and isInvalidReturnType(t):
+  if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, t):
     incl(param.sym.loc.flags, lfIndirect)
     param.sym.loc.storage = OnUnknown
 
@@ -364,12 +357,6 @@ 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)
 
@@ -384,7 +371,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
                    check: var IntSet, declareEnvironment=true;
                    weakDep=false) =
   params = nil
-  if t.sons[0] == nil or isInvalidReturnType(t.sons[0]):
+  if t.sons[0] == nil or isInvalidReturnType(m.config, t.sons[0]):
     rettype = ~"void"
   else:
     rettype = getTypeDescAux(m, t.sons[0], check)
@@ -395,7 +382,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
     if params != nil: add(params, ~", ")
     fillLoc(param.loc, locParam, t.n.sons[i], mangleParamName(m, param),
             param.paramStorageLoc)
-    if ccgIntroducedPtr(param):
+    if ccgIntroducedPtr(m.config, param):
       add(params, getTypeDescWeak(m, param.typ, check))
       add(params, ~"*")
       incl(param.loc.flags, lfIndirect)
@@ -417,10 +404,10 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
       addf(params, ", NI $1Len_$2", [param.loc.r, j.rope])
       inc(j)
       arr = arr.sons[0]
-  if t.sons[0] != nil and isInvalidReturnType(t.sons[0]):
+  if t.sons[0] != nil and isInvalidReturnType(m.config, t.sons[0]):
     var arr = t.sons[0]
     if params != nil: add(params, ", ")
-    if mapReturnType(t.sons[0]) != ctArray:
+    if mapReturnType(m.config, t.sons[0]) != ctArray:
       add(params, getTypeDescWeak(m, arr, check))
       add(params, "*")
     else:
@@ -471,13 +458,13 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
             if tfPacked notin rectype.flags:
               add(unionBody, "struct {")
             else:
-              if hasAttribute in CC[cCompiler].props:
+              if hasAttribute in CC[m.config.cCompiler].props:
                 add(unionBody, "struct __attribute__((__packed__)){" )
               else:
                 addf(unionBody, "#pragma pack(push, 1)$nstruct{", [])
             add(unionBody, a)
             addf(unionBody, "} $1;$n", [sname])
-            if tfPacked in rectype.flags and hasAttribute notin CC[cCompiler].props:
+            if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
               addf(unionBody, "#pragma pack(pop)$n", [])
         else:
           add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check))
@@ -526,10 +513,10 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
   var hasField = false
 
   if tfPacked in typ.flags:
-    if hasAttribute in CC[cCompiler].props:
+    if hasAttribute in CC[m.config.cCompiler].props:
       result = structOrUnion(typ) & " __attribute__((__packed__))"
     else:
-      result = "#pragma pack(push, 1)" & tnl & structOrUnion(typ)
+      result = "#pragma pack(push, 1)\L" & structOrUnion(typ)
   else:
     result = structOrUnion(typ)
 
@@ -556,7 +543,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
           # proper request to generate popCurrentExceptionEx not possible for 2 reasons:
           # generated function will be below declared Exception type and circular dependency
           # between Exception and popCurrentExceptionEx function
-          result = genProcHeader(m, magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")) & ";" & rnl & result
+          result = genProcHeader(m, magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")) & ";" & result
       hasField = true
     else:
       appcg(m, result, " {$n  $1 Sup;$n",
@@ -570,9 +557,9 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
     addf(result, "char dummy;$n", [])
   else:
     add(result, desc)
-  add(result, "};" & tnl)
-  if tfPacked in typ.flags and hasAttribute notin CC[cCompiler].props:
-    result.add "#pragma pack(pop)" & tnl
+  add(result, "};\L")
+  if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
+    result.add "#pragma pack(pop)\L"
 
 proc getTupleDesc(m: BModule, typ: PType, name: Rope,
                   check: var IntSet): Rope =
@@ -581,9 +568,9 @@ proc getTupleDesc(m: BModule, typ: PType, name: Rope,
   for i in countup(0, sonsLen(typ) - 1):
     addf(desc, "$1 Field$2;$n",
          [getTypeDescAux(m, typ.sons[i], check), rope(i)])
-  if desc == nil: add(result, "char dummy;" & tnl)
+  if desc == nil: add(result, "char dummy;\L")
   else: add(result, desc)
-  add(result, "};" & tnl)
+  add(result, "};\L")
 
 proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool =
   # A helper proc for handling cppimport patterns, involving numeric
@@ -657,21 +644,6 @@ 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
@@ -687,11 +659,11 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
           (sfImportc in t.sym.flags and t.sym.magic == mNone)):
         m.typeCache[sig] = result
         var size: int
-        if firstOrd(t) < 0:
+        if firstOrd(m.config, t) < 0:
           addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
           size = 4
         else:
-          size = int(getSize(t))
+          size = int(getSize(m.config, t))
           case size
           of 1: addf(m.s[cfsTypes], "typedef NU8 $1;$n", [result])
           of 2: addf(m.s[cfsTypes], "typedef NU16 $1;$n", [result])
@@ -747,40 +719,8 @@ 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)
+    var n: BiggestInt = lengthOrd(m.config, t)
     if n <= 0: n = 1   # make an array of at least one element
     result = getTypeName(m, origTyp, sig)
     m.typeCache[sig] = result
@@ -791,8 +731,6 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
     else: addAbiCheck(m, t, result)
   of tyObject, tyTuple:
     if isImportedCppType(t) and origTyp.kind == tyGenericInst:
-      # for instantiated templates we do not go through the type cache as the
-      # the type cache is not aware of 'tyGenericInst'.
       let cppName = getTypeName(m, t, sig)
       var i = 0
       var chunkStart = 0
@@ -866,11 +804,11 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
     result = $t.kind & '_' & getTypeName(m, t.lastSon, hashType t.lastSon)
     m.typeCache[sig] = result
     if not isImportedType(t):
-      let s = int(getSize(t))
+      let s = int(getSize(m.config, t))
       case s
       of 1, 2, 4, 8: addf(m.s[cfsTypes], "typedef NU$2 $1;$n", [result, rope(s*8)])
       else: addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n",
-             [result, rope(getSize(t))])
+             [result, rope(getSize(m.config, t))])
   of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink,
      tyUserTypeClass, tyUserTypeClassInst, tyInferred:
     result = getTypeDescAux(m, lastSon(t), check)
@@ -972,7 +910,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
     var typename = typeToString(if origType.typeInst != nil: origType.typeInst
                                 else: origType, preferName)
     if typename == "ref object" and origType.skipTypes(skipPtrs).sym != nil:
-      typename = "anon ref object from " & $origType.skipTypes(skipPtrs).sym.info
+      typename = "anon ref object from " & m.config$origType.skipTypes(skipPtrs).sym.info
     addf(m.s[cfsTypeInit3], "$1.name = $2;$n",
         [name, makeCstring typename])
     discard cgsym(m, "nimTypeRoot")
@@ -1006,7 +944,7 @@ proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope =
 proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope =
   discard cgsym(m, "TNimNode")
   var tmp = discriminatorTableName(m, objtype, d)
-  result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(d.typ)+1)]
+  result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(m.config, d.typ)+1)]
 
 proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
                      info: TLineInfo) =
@@ -1030,7 +968,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
     assert(n.sons[0].kind == nkSym)
     var field = n.sons[0].sym
     var tmp = discriminatorTableName(m, typ, field)
-    var L = lengthOrd(field.typ)
+    var L = lengthOrd(m.config, field.typ)
     assert L > 0
     if field.loc.r == nil: fillObjectFields(m, typ)
     if field.loc.t == nil:
@@ -1140,7 +1078,7 @@ proc genEnumInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
       add(enumNames, makeCString(field.name.s))
     else:
       add(enumNames, makeCString(field.ast.strVal))
-    if i < length - 1: add(enumNames, ", " & tnl)
+    if i < length - 1: add(enumNames, ", \L")
     if field.position != i or tfEnumHasHoles in typ.flags:
       addf(specialCases, "$1.offset = $2;$n", [elemNode, rope(field.position)])
       hasHoles = true
@@ -1166,7 +1104,7 @@ proc genSetInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
   genTypeInfoAux(m, typ, typ, name, info)
   var tmp = getNimNode(m)
   addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n",
-       [tmp, rope(firstOrd(typ)), name])
+       [tmp, rope(firstOrd(m.config, typ)), name])
 
 proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
   genTypeInfoAuxBase(m, typ, typ, name, genTypeInfo(m, typ.sons[1], info), info)
@@ -1190,8 +1128,6 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
 proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
   let origType = t
   var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
-  if t.kind == tyOpt:
-    return genTypeInfo(m, optLowering(t), info)
 
   let sig = hashType(origType)
   result = m.typeInfoMarker.getOrDefault(sig)
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index a6080a808..75cd3d35d 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -27,9 +27,9 @@ proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
 proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool =
   result = getPragmaStmt(n, w) != nil
 
-proc hashString*(s: string): BiggestInt =
+proc hashString*(conf: ConfigRef; s: string): BiggestInt =
   # has to be the same algorithm as system.hashString!
-  if CPU[targetCPU].bit == 64:
+  if CPU[conf.target.targetCPU].bit == 64:
     # we have to use the same bitwidth
     # as the target CPU
     var b = 0'i64
@@ -52,117 +52,12 @@ proc hashString*(s: string): BiggestInt =
     a = a +% `shl`(a, 15'i32)
     result = a
 
-var
-  gTypeTable: array[TTypeKind, TIdTable]  # XXX globals here
-  gCanonicalTypes: array[TTypeKind, PType]
-
-proc initTypeTables() =
-  for i in countup(low(TTypeKind), high(TTypeKind)): initIdTable(gTypeTable[i])
-
-proc resetCaches* =
-  ## XXX: fix that more properly
-  initTypeTables()
-  for i in low(gCanonicalTypes)..high(gCanonicalTypes):
-    gCanonicalTypes[i] = nil
-
-when false:
-  proc echoStats*() =
-    for i in countup(low(TTypeKind), high(TTypeKind)):
-      echo i, " ", gTypeTable[i].counter
-
-proc slowSearch(key: PType; k: TTypeKind): PType =
-  # tuples are quite horrible as C does not support them directly and
-  # tuple[string, string] is a (strange) subtype of
-  # tuple[nameA, nameB: string]. This bites us here, so we
-  # use 'sameBackendType' instead of 'sameType'.
-  if idTableHasObjectAsKey(gTypeTable[k], key): return key
-  for h in countup(0, high(gTypeTable[k].data)):
-    var t = PType(gTypeTable[k].data[h].key)
-    if t != nil and sameBackendType(t, key):
-      return t
-  idTablePut(gTypeTable[k], key, key)
-  result = key
-
-proc getUniqueType*(key: PType): PType =
-  # this is a hotspot in the compiler!
-  result = key
-  when false:
-    if key == nil: return
-    var k = key.kind
-    case k
-    of tyBool, tyChar, tyInt..tyUInt64:
-      # no canonicalization for integral types, so that e.g. ``pid_t`` is
-      # produced instead of ``NI``.
-      result = key
-    of  tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString,
-        tyCString, tyNone, tyVoid:
-      result = gCanonicalTypes[k]
-      if result == nil:
-        gCanonicalTypes[k] = key
-        result = key
-    of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr:
-      if key.isResolvedUserTypeClass:
-        return getUniqueType(lastSon(key))
-      if key.sym != nil:
-        internalError(key.sym.info, "metatype not eliminated")
-      else:
-        internalError("metatype not eliminated")
-    of tyDistinct:
-      if key.deepCopy != nil: result = key
-      else: result = getUniqueType(lastSon(key))
-    of tyGenericInst, tyOrdinal, tyStatic, tyAlias, tySink, tyInferred:
-      result = getUniqueType(lastSon(key))
-      #let obj = lastSon(key)
-      #if obj.sym != nil and obj.sym.name.s == "TOption":
-      #  echo "for ", typeToString(key), " I returned "
-      #  debug result
-    of tyPtr, tyRef, tyVar, tyLent:
-      let elemType = lastSon(key)
-      if elemType.kind in {tyBool, tyChar, tyInt..tyUInt64}:
-        # no canonicalization for integral types, so that e.g. ``ptr pid_t`` is
-        # produced instead of ``ptr NI``.
-        result = key
-      else:
-        result = slowSearch(key, k)
-    of tyGenericInvocation, tyGenericBody,
-       tyOpenArray, tyArray, tySet, tyRange, tyTuple,
-       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)
-    of tyObject:
-      if tfFromGeneric notin key.flags:
-        # fast case; lookup per id suffices:
-        result = PType(idTableGet(gTypeTable[k], key))
-        if result == nil:
-          idTablePut(gTypeTable[k], key, key)
-          result = key
-      else:
-        # ugly slow case: need to compare by structure
-        if idTableHasObjectAsKey(gTypeTable[k], key): return key
-        for h in countup(0, high(gTypeTable[k].data)):
-          var t = PType(gTypeTable[k].data[h].key)
-          if t != nil and sameBackendType(t, key):
-            return t
-        idTablePut(gTypeTable[k], key, key)
-        result = key
-    of tyEnum:
-      result = PType(idTableGet(gTypeTable[k], key))
-      if result == nil:
-        idTablePut(gTypeTable[k], key, key)
-        result = key
-    of tyProc:
-      if key.callConv != ccClosure:
-        result = key
-      else:
-        # ugh, we need the canon here:
-        result = slowSearch(key, k)
-    of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("getUniqueType")
+template getUniqueType*(key: PType): PType = key
 
 proc makeSingleLineCString*(s: string): string =
   result = "\""
   for c in items(s):
-    result.add(c.toCChar)
+    c.toCChar(result)
   result.add('\"')
 
 proc mangle*(name: string): string =
@@ -210,5 +105,3 @@ proc mangle*(name: string): string =
       requiresUnderscore = true
   if requiresUnderscore:
     result.add "_"
-
-initTypeTables()
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index e749c78db..cd344f096 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -12,14 +12,15 @@
 import
   ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
   nversion, nimsets, msgs, std / sha1, bitsets, idents, types,
-  ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
+  ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth,
   condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
-  lowerings, semparallel, tables, sets, ndi
+  lowerings, semparallel, tables, sets, ndi, lineinfos
 
 import strutils except `%` # collides with ropes.`%`
 
 from modulegraphs import ModuleGraph
-from configuration import warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated
+from lineinfos import
+  warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated, errCannotOpenFile
 import dynlib
 
 when not declared(dynlib.libCandidates):
@@ -120,10 +121,10 @@ proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope =
           internalError(m.config, "ropes: invalid format string $" & $j)
         add(result, args[j-1])
       of 'n':
-        if optLineDir notin m.config.options: add(result, rnl)
+        if optLineDir notin m.config.options: add(result, "\L")
         inc(i)
       of 'N':
-        add(result, rnl)
+        add(result, "\L")
         inc(i)
       else: internalError(m.config, "ropes: invalid format string $" & frmt[i])
     elif frmt[i] == '#' and frmt[i+1] in IdentStartChars:
@@ -193,7 +194,7 @@ proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) =
         [rope(makeSingleLineCString(filename)), rope(line)])
 
 proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) =
-  genCLineDir(r, info.toFullPath, info.safeLineNm, conf)
+  genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf)
 
 proc freshLineInfo(p: BProc; info: TLineInfo): bool =
   if p.lastLineInfo.line != info.line or
@@ -203,27 +204,22 @@ proc freshLineInfo(p: BProc; info: TLineInfo): bool =
     result = true
 
 proc genLineDir(p: BProc, t: PNode) =
-  var tt = t
-  #while tt.kind in {nkStmtListExpr}+nkCallKinds:
-  #  tt = tt.lastSon
-  if tt.kind in nkCallKinds and tt.len > 1:
-    tt = tt.sons[1]
-  let line = tt.info.safeLineNm
+  let line = t.info.safeLineNm
 
   if optEmbedOrigSrc in p.config.globalOptions:
-    add(p.s(cpsStmts), ~"//" & sourceLine(p.config, tt.info) & rnl)
-  genCLineDir(p.s(cpsStmts), tt.info.toFullPath, line, p.config)
+    add(p.s(cpsStmts), ~"//" & sourceLine(p.config, t.info) & "\L")
+  genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p.config)
   if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and
       (p.prc == nil or sfPure notin p.prc.flags):
-    if freshLineInfo(p, tt.info):
+    if freshLineInfo(p, t.info):
       linefmt(p, cpsStmts, "#endb($1, $2);$N",
-              line.rope, makeCString(toFilename(tt.info)))
+              line.rope, makeCString(toFilename(p.config, t.info)))
   elif ({optLineTrace, optStackTrace} * p.options ==
       {optLineTrace, optStackTrace}) and
-      (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex != InvalidFileIDX:
-    if freshLineInfo(p, tt.info):
+      (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIDX:
+    if freshLineInfo(p, t.info):
       linefmt(p, cpsStmts, "nimln_($1, $2);$n",
-              line.rope, quotedFilename(p.config, tt.info))
+              line.rope, quotedFilename(p.config, t.info))
 
 proc postStmtActions(p: BProc) {.inline.} =
   add(p.s(cpsStmts), p.module.injectStmt)
@@ -249,9 +245,9 @@ proc rdLoc(a: TLoc): Rope =
   result = a.r
   if lfIndirect in a.flags: result = "(*$1)" % [result]
 
-proc addrLoc(a: TLoc): Rope =
+proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
   result = a.r
-  if lfIndirect notin a.flags and mapType(a.t) != ctArray:
+  if lfIndirect notin a.flags and mapType(conf, a.t) != ctArray:
     result = "(&" & result & ")"
 
 proc rdCharLoc(a: TLoc): Rope =
@@ -281,7 +277,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
     linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t, a.lode.info))
   of frEmbedded:
     # worst case for performance:
-    var r = if takeAddr: addrLoc(a) else: rdLoc(a)
+    var r = if takeAddr: addrLoc(p.config, a) else: rdLoc(a)
     linefmt(p, section, "#objectInit($1, $2);$n", r, genTypeInfo(p.module, t, a.lode.info))
 
 type
@@ -310,10 +306,10 @@ proc resetLoc(p: BProc, loc: var TLoc) =
       linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc))
   else:
     if optNilCheck in p.options:
-      linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc))
+      linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(p.config, loc))
     if loc.storage != OnStack:
       linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
-              addrLoc(loc), genTypeInfo(p.module, loc.t, loc.lode.info))
+              addrLoc(p.config, loc), genTypeInfo(p.module, loc.t, loc.lode.info))
       # XXX: generated reset procs should not touch the m_type
       # field, so disabling this should be safe:
       genObjectInit(p, cpsStmts, loc.t, loc, true)
@@ -322,7 +318,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
       # array passed as argument decayed into pointer, bug #7332
       # so we use getTypeDesc here rather than rdLoc(loc)
       linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
-              addrLoc(loc), getTypeDesc(p.module, loc.t))
+              addrLoc(p.config, loc), getTypeDesc(p.module, loc.t))
       # XXX: We can be extra clever here and call memset only
       # on the bytes following the m_type field?
       genObjectInit(p, cpsStmts, loc.t, loc, true)
@@ -339,7 +335,7 @@ proc constructLoc(p: BProc, loc: TLoc, isTemp = false) =
       if not isImportedCppType(typ):
         useStringh(p.module)
         linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
-                addrLoc(loc), getTypeDesc(p.module, typ))
+                addrLoc(p.config, loc), getTypeDesc(p.module, typ))
     genObjectInit(p, cpsStmts, loc.t, loc, true)
 
 proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
@@ -386,7 +382,7 @@ proc localDebugInfo(p: BProc, s: PSym) =
   # XXX work around a bug: No type information for open arrays possible:
   if skipTypes(s.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: return
   var a = "&" & s.loc.r
-  if s.kind == skParam and ccgIntroducedPtr(s): a = s.loc.r
+  if s.kind == skParam and ccgIntroducedPtr(p.config, s): a = s.loc.r
   lineF(p, cpsInit,
        "FR_.s[$1].address = (void*)$3; FR_.s[$1].typ = $4; FR_.s[$1].name = $2;$n",
        [p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a,
@@ -414,7 +410,7 @@ 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 p.config.options: "" else: tnl
+  let nl = if optLineDir in p.config.options: "" else: "\L"
   let decl = localVarDecl(p, n) & ";" & nl
   line(p, cpsLocals, decl)
   localDebugInfo(p, n.sym)
@@ -651,33 +647,33 @@ proc cgsym(m: BModule, name: string): Rope =
   result = sym.loc.r
 
 proc generateHeaders(m: BModule) =
-  add(m.s[cfsHeaders], tnl & "#include \"nimbase.h\"" & tnl)
+  add(m.s[cfsHeaders], "\L#include \"nimbase.h\"\L")
 
   for it in m.headerFiles:
     if it[0] == '#':
-      add(m.s[cfsHeaders], rope(it.replace('`', '"') & tnl))
+      add(m.s[cfsHeaders], rope(it.replace('`', '"') & "\L"))
     elif it[0] notin {'\"', '<'}:
       addf(m.s[cfsHeaders], "#include \"$1\"$N", [rope(it)])
     else:
       addf(m.s[cfsHeaders], "#include $1$N", [rope(it)])
-  add(m.s[cfsHeaders], "#undef LANGUAGE_C" & tnl)
-  add(m.s[cfsHeaders], "#undef MIPSEB" & tnl)
-  add(m.s[cfsHeaders], "#undef MIPSEL" & tnl)
-  add(m.s[cfsHeaders], "#undef PPC" & tnl)
-  add(m.s[cfsHeaders], "#undef R3000" & tnl)
-  add(m.s[cfsHeaders], "#undef R4000" & tnl)
-  add(m.s[cfsHeaders], "#undef i386" & tnl)
-  add(m.s[cfsHeaders], "#undef linux" & tnl)
-  add(m.s[cfsHeaders], "#undef mips" & tnl)
-  add(m.s[cfsHeaders], "#undef near" & tnl)
-  add(m.s[cfsHeaders], "#undef powerpc" & tnl)
-  add(m.s[cfsHeaders], "#undef unix" & tnl)
+  add(m.s[cfsHeaders], "#undef LANGUAGE_C\L")
+  add(m.s[cfsHeaders], "#undef MIPSEB\L")
+  add(m.s[cfsHeaders], "#undef MIPSEL\L")
+  add(m.s[cfsHeaders], "#undef PPC\L")
+  add(m.s[cfsHeaders], "#undef R3000\L")
+  add(m.s[cfsHeaders], "#undef R4000\L")
+  add(m.s[cfsHeaders], "#undef i386\L")
+  add(m.s[cfsHeaders], "#undef linux\L")
+  add(m.s[cfsHeaders], "#undef mips\L")
+  add(m.s[cfsHeaders], "#undef near\L")
+  add(m.s[cfsHeaders], "#undef powerpc\L")
+  add(m.s[cfsHeaders], "#undef unix\L")
 
 proc openNamespaceNim(): Rope =
-  result.add("namespace Nim {" & tnl)
+  result.add("namespace Nim {\L")
 
 proc closeNamespaceNim(): Rope =
-  result.add("}" & tnl)
+  result.add("}\L")
 
 proc closureSetup(p: BProc, prc: PSym) =
   if tfCapturesEnv notin prc.typ.flags: return
@@ -727,7 +723,7 @@ proc genProcAux(m: BModule, prc: PSym) =
       internalError(m.config, prc.info, "proc has no result symbol")
     let resNode = prc.ast.sons[resultPos]
     let res = resNode.sym # get result symbol
-    if not isInvalidReturnType(prc.typ.sons[0]):
+    if not isInvalidReturnType(m.config, 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, resNode)
@@ -741,7 +737,7 @@ proc genProcAux(m: BModule, prc: PSym) =
         initLocalVar(p, res, immediateAsgn=false)
       returnStmt = ropecg(p.module, "\treturn $1;$n", rdLoc(res.loc))
     else:
-      fillResult(resNode)
+      fillResult(p.config, resNode)
       assignParam(p, res)
       if sfNoInit notin prc.flags: resetLoc(p, res.loc)
       if skipTypes(res.typ, abstractInst).kind == tyArray:
@@ -756,10 +752,10 @@ proc genProcAux(m: BModule, prc: PSym) =
   genStmts(p, prc.getBody) # modifies p.locals, p.init, etc.
   var generatedProc: Rope
   if sfNoReturn in prc.flags:
-    if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
+    if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
       header = "__declspec(noreturn) " & header
   if sfPure in prc.flags:
-    if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
+    if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
       header = "__declspec(naked) " & header
     generatedProc = ropecg(p.module, "$N$1 {$n$2$3$4}$N$N",
                          header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts))
@@ -803,13 +799,13 @@ proc genProcPrototype(m: BModule, sym: PSym) =
                         getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)))
   elif not containsOrIncl(m.declaredProtos, sym.id):
     var header = genProcHeader(m, sym)
-    if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[cCompiler].props:
+    if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
       header = "__declspec(noreturn) " & header
     if sym.typ.callConv != ccInline and requiresExternC(m, sym):
       header = "extern \"C\" " & header
-    if sfPure in sym.flags and hasAttribute in CC[cCompiler].props:
+    if sfPure in sym.flags and hasAttribute in CC[m.config.cCompiler].props:
       header.add(" __attribute__((naked))")
-    if sfNoReturn in sym.flags and hasAttribute in CC[cCompiler].props:
+    if sfNoReturn in sym.flags and hasAttribute in CC[m.config.cCompiler].props:
       header.add(" __attribute__((noreturn))")
     add(m.s[cfsProcHeaders], ropecg(m, "$1;$n", header))
 
@@ -918,10 +914,10 @@ proc genVarPrototype(m: BModule, n: PNode) =
       addf(m.s[cfsVars], " $1;$n", [sym.loc.r])
 
 proc addIntTypes(result: var Rope; conf: ConfigRef) {.inline.} =
-  addf(result, "#define NIM_NEW_MANGLING_RULES" & tnl &
-               "#define NIM_INTBITS $1" & tnl, [
-    platform.CPU[targetCPU].intSize.rope])
-  if optUseNimNamespace in conf.globalOptions: result.add("#define USE_NIM_NAMESPACE" & tnl)
+  addf(result, "#define NIM_NEW_MANGLING_RULES\L" &
+               "#define NIM_INTBITS $1\L", [
+    platform.CPU[conf.target.targetCPU].intSize.rope])
+  if optUseNimNamespace in conf.globalOptions: result.add("#define USE_NIM_NAMESPACE\L")
 
 proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope =
   if optCompileOnly in conf.globalOptions:
@@ -936,9 +932,9 @@ proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope =
         "/* Compiled for: $2, $3, $4 */$N" &
         "/* Command for C compiler:$n   $5 */$N") %
         [rope(VersionAsString),
-        rope(platform.OS[targetOS].name),
-        rope(platform.CPU[targetCPU].name),
-        rope(extccomp.CC[extccomp.cCompiler].name),
+        rope(platform.OS[conf.target.targetOS].name),
+        rope(platform.CPU[conf.target.targetCPU].name),
+        rope(extccomp.CC[conf.cCompiler].name),
         rope(getCompileCFileCmd(conf, cfile))]
 
 proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
@@ -948,8 +944,8 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
 proc genFilenames(m: BModule): Rope =
   discard cgsym(m, "dbgRegisterFilename")
   result = nil
-  for i in 0..<fileInfos.len:
-    result.addf("dbgRegisterFilename($1);$N", [fileInfos[i].projPath.makeCString])
+  for i in 0..<m.config.m.fileInfos.len:
+    result.addf("dbgRegisterFilename($1);$N", [m.config.m.fileInfos[i].projPath.makeCString])
 
 proc genMainProc(m: BModule) =
   const
@@ -1052,7 +1048,7 @@ proc genMainProc(m: BModule) =
       "}$N$N"
 
   var nimMain, otherMain: FormatStr
-  if platform.targetOS == osWindows and
+  if m.config.target.targetOS == osWindows and
       m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:
     if optGenGuiApp in m.config.globalOptions:
       nimMain = WinNimMain
@@ -1061,14 +1057,14 @@ proc genMainProc(m: BModule) =
       nimMain = WinNimDllMain
       otherMain = WinCDllMain
     m.includeHeader("<windows.h>")
-  elif platform.targetOS == osGenode:
+  elif m.config.target.targetOS == osGenode:
     nimMain = GenodeNimMain
     otherMain = ComponentConstruct
     m.includeHeader("<libc/component.h>")
   elif optGenDynLib in m.config.globalOptions:
     nimMain = PosixNimDllMain
     otherMain = PosixCDllMain
-  elif platform.targetOS == osStandalone:
+  elif m.config.target.targetOS == osStandalone:
     nimMain = PosixNimMain
     otherMain = StandaloneCMain
   else:
@@ -1079,12 +1075,12 @@ proc genMainProc(m: BModule) =
     m.g.breakpoints.add(m.genFilenames)
 
   let initStackBottomCall =
-    if platform.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
+    if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
     else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
   inc(m.labels)
   appcg(m, m.s[cfsProcs], PreMainBody, [
     m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit,
-     if emulatedThreadVars(m.config) and platform.targetOS != osStandalone:
+     if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
        ropecg(m, "\t#initThreadVarsEmulation();$N")
      else:
        "".rope,
@@ -1094,7 +1090,7 @@ proc genMainProc(m: BModule) =
         [m.g.mainModInit, initStackBottomCall, rope(m.labels)])
   if optNoMain notin m.config.globalOptions:
     if optUseNimNamespace in m.config.globalOptions:
-      m.s[cfsProcs].add closeNamespaceNim() & "using namespace Nim;" & tnl
+      m.s[cfsProcs].add closeNamespaceNim() & "using namespace Nim;\L"
 
     appcg(m, m.s[cfsProcs], otherMain, [])
     if optUseNimNamespace in m.config.globalOptions: m.s[cfsProcs].add openNamespaceNim()
@@ -1255,7 +1251,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
     excl(result.postInitProc.options, optStackTrace)
   let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi")
                 else: ""
-  open(result.ndi, ndiName)
+  open(result.ndi, ndiName, g.config)
 
 proc nullify[T](arr: var T) =
   for i in low(arr)..high(arr):
@@ -1305,13 +1301,15 @@ proc resetModule*(m: BModule) =
 proc resetCgenModules*(g: BModuleList) =
   for m in cgenModules(g): resetModule(m)
 
-proc rawNewModule(g: BModuleList; module: PSym): BModule =
-  result = rawNewModule(g, module, module.position.FileIndex.toFullPath)
+proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
+  result = rawNewModule(g, module, toFullPath(conf, module.position.FileIndex))
 
-proc newModule(g: BModuleList; module: PSym): BModule =
+proc newModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
   # we should create only one cgen module for each module sym
-  result = rawNewModule(g, module)
-  growCache g.modules, module.position
+  result = rawNewModule(g, module, conf)
+  if module.position >= g.modules.len:
+    setLen(g.modules, module.position + 1)
+  #growCache g.modules, module.position
   g.modules[module.position] = result
 
 template injectG() {.dirty.} =
@@ -1319,9 +1317,9 @@ template injectG() {.dirty.} =
     graph.backend = newModuleList(graph)
   let g = BModuleList(graph.backend)
 
-proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
+proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
   injectG()
-  result = newModule(g, module)
+  result = newModule(g, module, graph.config)
   if optGenIndex in graph.config.globalOptions and g.generatedHeader == nil:
     let f = if graph.config.headerFile.len > 0: graph.config.headerFile
             else: graph.config.projectFull
@@ -1353,7 +1351,8 @@ proc writeHeader(m: BModule) =
   result.addf("N_CDECL(void, NimMain)(void);$n", [])
   if optUseNimNamespace in m.config.globalOptions: result.add closeNamespaceNim()
   result.addf("#endif /* $1 */$n", [guard])
-  writeRope(result, m.filename)
+  if not writeRope(result, m.filename):
+    rawMessage(m.config, errCannotOpenFile, m.filename)
 
 proc getCFile(m: BModule): string =
   let ext =
@@ -1362,11 +1361,12 @@ proc getCFile(m: BModule): string =
       else: ".c"
   result = changeFileExt(completeCFilePath(m.config, withPackageName(m.config, m.cfilename)), ext)
 
-proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext =
-  injectG()
-  var m = newModule(g, module)
-  readMergeInfo(getCFile(m), m)
-  result = m
+when false:
+  proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext =
+    injectG()
+    var m = newModule(g, module, graph.config)
+    readMergeInfo(getCFile(m), m)
+    result = m
 
 proc myProcess(b: PPassContext, n: PNode): PNode =
   result = n
@@ -1374,7 +1374,8 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
   var m = BModule(b)
   if passes.skipCodegen(m.config, n): return
   m.initProc.options = initProcOptions(m)
-  softRnl = if optLineDir in m.config.options: noRnl else: rnl
+  #softRnl = if optLineDir in m.config.options: noRnl else: rnl
+  # XXX replicate this logic!
   genStmts(m.initProc, n)
 
 proc finishModule(m: BModule) =
@@ -1401,12 +1402,14 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
           echo "diff ", cfile.cname, ".backup ", cfile.cname
         else:
           echo "new file ", cfile.cname
-      writeRope(code, cfile.cname)
+      if not writeRope(code, cfile.cname):
+        rawMessage(m.config, errCannotOpenFile, cfile.cname)
       return
     if existsFile(cfile.obj) and os.fileNewer(cfile.obj, cfile.cname):
       result = false
   else:
-    writeRope(code, cfile.cname)
+    if not writeRope(code, cfile.cname):
+      rawMessage(m.config, errCannotOpenFile, cfile.cname)
 
 # We need 2 different logics here: pending modules (including
 # 'nim__dat') may require file merging for the combination of dead code
@@ -1418,7 +1421,7 @@ proc writeModule(m: BModule, pending: bool) =
   # generate code for the init statements of the module:
   let cfile = getCFile(m)
 
-  if m.rd == nil or optForceFullMake in m.config.globalOptions:
+  if true or optForceFullMake in m.config.globalOptions:
     genInitCode(m)
     finishTypeDescriptions(m)
     if sfMainModule in m.module.flags:
@@ -1441,7 +1444,8 @@ proc writeModule(m: BModule, pending: bool) =
     genInitCode(m)
     finishTypeDescriptions(m)
     var code = genModule(m, cf)
-    writeRope(code, cfile)
+    if not writeRope(code, cfile):
+      rawMessage(m.config, errCannotOpenFile, cfile)
     addFileToCompile(m.config, cf)
   else:
     # Consider: first compilation compiles ``system.nim`` and produces
@@ -1462,7 +1466,8 @@ proc updateCachedModule(m: BModule) =
     finishTypeDescriptions(m)
 
     var code = genModule(m, cf)
-    writeRope(code, cfile)
+    if not writeRope(code, cfile):
+      rawMessage(m.config, errCannotOpenFile, cfile)
   else:
     cf.flags = {CfileFlag.Cached}
   addFileToCompile(m.config, cf)
@@ -1475,7 +1480,6 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
   # if the module is cached, we don't regenerate the main proc
   # nor the dispatchers? But if the dispatchers changed?
   # XXX emit the dispatchers into its own .c file?
-  if b.rd != nil: return
   if n != nil:
     m.initProc.options = initProcOptions(m)
     genStmts(m.initProc, n)
@@ -1498,14 +1502,10 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
   if g.generatedHeader != nil: finishModule(g.generatedHeader)
   while g.forwardedProcsCounter > 0:
     for m in cgenModules(g):
-      if m.rd == nil:
-        finishModule(m)
+      finishModule(m)
   for m in cgenModules(g):
-    if m.rd != nil:
-      m.updateCachedModule
-    else:
-      m.writeModule(pending=true)
+    m.writeModule(pending=true)
   writeMapping(config, g.mapping)
   if g.generatedHeader != nil: writeHeader(g.generatedHeader)
 
-const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
+const cgenPass* = makePass(myOpen, myProcess, myClose)
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index 843677654..a526a0f00 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -11,9 +11,8 @@
 
 import
   ast, astalgo, ropes, passes, options, intsets, platform, sighashes,
-  tables, ndi
+  tables, ndi, lineinfos
 
-from msgs import TLineInfo
 from modulegraphs import ModuleGraph
 
 type
@@ -122,6 +121,17 @@ type
     graph*: ModuleGraph
     strVersion*, seqVersion*: int # version of the string/seq implementation to use
 
+    nimtv*: Rope            # Nim thread vars; the struct body
+    nimtvDeps*: seq[PType]  # type deps: every module needs whole struct
+    nimtvDeclared*: IntSet  # so that every var/field exists only once
+                            # in the struct
+                            # 'nimtv' is incredibly hard to modularize! Best
+                            # effort is to store all thread vars in a ROD
+                            # section and with their type deps and load them
+                            # unconditionally...
+                            # nimtvDeps is VERY hard to cache because it's
+                            # not a list of IDs nor can it be made to be one.
+
   TCGen = object of TPassContext # represents a C source file
     s*: TCFileSections        # sections of the C file
     flags*: set[Codegenflag]
@@ -180,7 +190,7 @@ proc newProc*(prc: PSym, module: BModule): BProc =
 
 proc newModuleList*(g: ModuleGraph): BModuleList =
   BModuleList(modules: @[], typeInfoMarker: initTable[SigHash, Rope](), config: g.config,
-    graph: g)
+    graph: g, nimtvDeps: @[], nimtvDeclared: initIntSet())
 
 iterator cgenModules*(g: BModuleList): BModule =
   for i in 0..high(g.modules):
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index 1d72952e2..5b58e6498 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -11,7 +11,7 @@
 
 import
   intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
-  sempass2, strutils, modulegraphs, configuration
+  sempass2, strutils, modulegraphs, lineinfos
 
 proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
   var dest = skipTypes(d, abstractPtrs)
@@ -115,7 +115,7 @@ proc createDispatcher(s: PSym): PSym =
   # we can't inline the dispatcher itself (for now):
   if disp.typ.callConv == ccInline: disp.typ.callConv = ccDefault
   disp.ast = copyTree(s.ast)
-  disp.ast.sons[bodyPos] = ast.emptyNode
+  disp.ast.sons[bodyPos] = newNodeI(nkEmpty, s.info)
   disp.loc.r = nil
   if s.typ.sons[0] != nil:
     if disp.ast.sonsLen > resultPos:
@@ -124,7 +124,7 @@ proc createDispatcher(s: PSym): PSym =
       # We've encountered a method prototype without a filled-in
       # resultPos slot. We put a placeholder in there that will
       # be updated in fixupDispatcher().
-      disp.ast.addSon(ast.emptyNode)
+      disp.ast.addSon(newNodeI(nkEmpty, s.info))
   attachDispatcher(s, newSymNode(disp))
   # attach to itself to prevent bugs:
   attachDispatcher(disp, newSymNode(disp))
@@ -137,7 +137,7 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
   # the lock level of the dispatcher needs to be updated/checked
   # against that of the method.
   if disp.ast.sonsLen > resultPos and meth.ast.sonsLen > resultPos and
-     disp.ast.sons[resultPos] == ast.emptyNode:
+     disp.ast.sons[resultPos].kind == nkEmpty:
     disp.ast.sons[resultPos] = copyTree(meth.ast.sons[resultPos])
 
   # The following code works only with lock levels, so we disable
@@ -184,7 +184,7 @@ proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) =
   #  internalError(s.info, "no method dispatcher found")
   if witness != nil:
     localError(g.config, s.info, "invalid declaration order; cannot attach '" & s.name.s &
-                       "' to method defined here: " & $witness.info)
+                       "' to method defined here: " & g.config$witness.info)
   elif sfBase notin s.flags:
     message(g.config, s.info, warnUseBase)
 
diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim
index 5568fd37b..6fa856b2f 100644
--- a/compiler/closureiters.nim
+++ b/compiler/closureiters.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nim Compiler
-#        (c) Copyright 2018 Andreas Rumpf
+#        (c) Copyright 2018 Nim Contributors
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -132,7 +132,7 @@
 
 import
   intsets, strutils, options, ast, astalgo, trees, treetab, msgs, idents,
-  renderer, types, magicsys, rodread, lowerings, lambdalifting, modulegraphs
+  renderer, types, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos
 
 type
   Ctx = object
@@ -177,7 +177,7 @@ proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode =
   ctx.newStateAssgn(newIntTypeNode(nkIntLit, stateNo, ctx.g.getSysType(TLineInfo(), tyInt)))
 
 proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
-  result = newSym(skVar, getIdent(name), ctx.fn, ctx.fn.info)
+  result = newSym(skVar, getIdent(ctx.g.cache, name), ctx.fn, ctx.fn.info)
   result.typ = typ
   assert(not typ.isNil)
 
@@ -190,7 +190,7 @@ proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
   else:
     let envParam = getEnvParam(ctx.fn)
     # let obj = envParam.typ.lastSon
-    result = addUniqueField(envParam.typ.lastSon, result)
+    result = addUniqueField(envParam.typ.lastSon, result, ctx.g.cache)
 
 proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode =
   if ctx.stateVarSym.isNil:
@@ -210,7 +210,7 @@ proc newUnrollFinallyAccess(ctx: var Ctx, info: TLineInfo): PNode =
 
 proc newCurExcAccess(ctx: var Ctx): PNode =
   if ctx.curExcSym.isNil:
-    ctx.curExcSym = ctx.newEnvVar(":curExc", ctx.g.callCodegenProc("getCurrentException", emptyNode).typ)
+    ctx.curExcSym = ctx.newEnvVar(":curExc", ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode).typ)
   ctx.newEnvVarAccess(ctx.curExcSym)
 
 proc newState(ctx: var Ctx, n, gotoOut: PNode): int =
@@ -324,7 +324,7 @@ proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} =
           assert(c[i].kind == nkType)
           let nextCond = newTree(nkCall,
             newSymNode(g.getSysMagic(c.info, "of", mOf)),
-            g.callCodegenProc("getCurrentException", emptyNode),
+            g.callCodegenProc("getCurrentException", ctx.g.emptyNode),
             c[i])
           nextCond.typ = ctx.g.getSysType(c.info, tyBool)
           nextCond.info = c.info
@@ -349,7 +349,7 @@ proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} =
   if ifStmt.len != 0:
     result = newTree(nkStmtList, ctx.newNullifyCurExc(n.info), ifStmt)
   else:
-    result = emptyNode
+    result = ctx.g.emptyNode
 
 proc addElseToExcept(ctx: var Ctx, n: PNode) =
   if n.kind == nkStmtList and n[1].kind == nkIfStmt and n[1][^1].kind != nkElse:
@@ -364,7 +364,7 @@ proc addElseToExcept(ctx: var Ctx, n: PNode) =
     block: # :curExc = getCurrentException()
       branchBody.add(newTree(nkAsgn,
         ctx.newCurExcAccess(),
-        ctx.g.callCodegenProc("getCurrentException", emptyNode)))
+        ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode)))
 
     block: # goto nearestFinally
       branchBody.add(newTree(nkGotoState, ctx.g.newIntLit(n.info, ctx.nearestFinally)))
@@ -372,12 +372,12 @@ proc addElseToExcept(ctx: var Ctx, n: PNode) =
     let elseBranch = newTree(nkElse, branchBody)
     n[1].add(elseBranch)
 
-proc getFinallyNode(n: PNode): PNode =
+proc getFinallyNode(ctx: var Ctx, n: PNode): PNode =
   result = n[^1]
   if result.kind == nkFinally:
     result = result[0]
   else:
-    result = emptyNode
+    result = ctx.g.emptyNode
 
 proc hasYieldsInExpressions(n: PNode): bool =
   case n.kind
@@ -725,7 +725,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
 
       if condNeedsSplit:
         let (st, ex) = exprToStmtList(n[0])
-        let brk = newTree(nkBreakStmt, emptyNode)
+        let brk = newTree(nkBreakStmt, ctx.g.emptyNode)
         let branch = newTree(nkElifBranch, ctx.g.newNotCall(ex), brk)
         let check = newTree(nkIfStmt, branch)
         let newBody = newTree(nkStmtList, st, check, n[1])
@@ -777,14 +777,14 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
   cmp.typ = ctx.g.getSysType(info, tyBool)
 
   let asgn = newTree(nkFastAsgn,
-    newSymNode(getClosureIterResult(ctx.fn), info),
+    newSymNode(getClosureIterResult(ctx.g, ctx.fn), info),
     ctx.newTmpResultAccess())
 
   let retStmt = newTree(nkReturnStmt, asgn)
   let branch = newTree(nkElifBranch, cmp, retStmt)
 
   # The C++ backend requires `getCurrentException` here.
-  let raiseStmt = newTree(nkRaiseStmt, ctx.g.callCodegenProc("getCurrentException", emptyNode))
+  let raiseStmt = newTree(nkRaiseStmt, ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode))
   raiseStmt.info = info
   let elseBranch = newTree(nkElse, raiseStmt)
 
@@ -922,7 +922,7 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
       result = newNodeI(nkGotoState, n.info)
       var tryBody = toStmtList(n[0])
       var exceptBody = ctx.collectExceptState(n)
-      var finallyBody = newTree(nkStmtList, getFinallyNode(n))
+      var finallyBody = newTree(nkStmtList, getFinallyNode(ctx, n))
       finallyBody = ctx.transformReturnsInTry(finallyBody)
       finallyBody.add(ctx.newEndFinallyNode(finallyBody.info))
 
@@ -1023,11 +1023,11 @@ proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode =
       if n[0].sons[0].kind != nkEmpty:
         var a = newNodeI(nkAsgn, n[0].sons[0].info)
         var retVal = n[0].sons[0] #liftCapturedVars(n.sons[0], owner, d, c)
-        addSon(a, newSymNode(getClosureIterResult(ctx.fn)))
+        addSon(a, newSymNode(getClosureIterResult(ctx.g, ctx.fn)))
         addSon(a, retVal)
         retStmt.add(a)
       else:
-        retStmt.add(emptyNode)
+        retStmt.add(ctx.g.emptyNode)
 
       result.add(retStmt)
     else:
@@ -1054,10 +1054,10 @@ proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode =
     for i in 0 ..< n.len:
       n[i] = ctx.tranformStateAssignments(n[i])
 
-proc skipStmtList(n: PNode): PNode =
+proc skipStmtList(ctx: Ctx; n: PNode): PNode =
   result = n
   while result.kind in {nkStmtList}:
-    if result.len == 0: return emptyNode
+    if result.len == 0: return ctx.g.emptyNode
     result = result[0]
 
 proc skipEmptyStates(ctx: Ctx, stateIdx: int): int =
@@ -1072,7 +1072,7 @@ proc skipEmptyStates(ctx: Ctx, stateIdx: int): int =
     if label == -1:
       newLabel = ctx.exitStateIdx
     else:
-      let fs = ctx.states[label][1].skipStmtList()
+      let fs = skipStmtList(ctx, ctx.states[label][1])
       if fs.kind == nkGotoState:
         newLabel = fs[0].intVal.int
     if label == newLabel: break
@@ -1148,7 +1148,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
       newIntTypeNode(nkIntLit, 0, intTyp))
     cond.typ = boolTyp
 
-    let raiseStmt = newTree(nkRaiseStmt, emptyNode)
+    let raiseStmt = newTree(nkRaiseStmt, ctx.g.emptyNode)
     let ifBranch = newTree(nkElifBranch, cond, raiseStmt)
     let ifStmt = newTree(nkIfStmt, ifBranch)
     result.add(ifStmt)
@@ -1185,7 +1185,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
   block:
     result.add(newTree(nkAsgn,
       ctx.newCurExcAccess(),
-      ctx.g.callCodegenProc("getCurrentException", emptyNode)))
+      ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode)))
 
 proc wrapIntoTryExcept(ctx: var Ctx, n: PNode): PNode {.inline.} =
   let setupExc = newTree(nkCall,
@@ -1235,7 +1235,7 @@ proc deleteEmptyStates(ctx: var Ctx) =
   # Apply new state indexes and mark unused states with -1
   var iValid = 0
   for i, s in ctx.states:
-    let body = s[1].skipStmtList()
+    let body = skipStmtList(ctx, s[1])
     if body.kind == nkGotoState and i != ctx.states.len - 1 and i != 0:
       # This is an empty state. Mark with -1.
       s[0].intVal = -1
@@ -1244,7 +1244,7 @@ proc deleteEmptyStates(ctx: var Ctx) =
       inc iValid
 
   for i, s in ctx.states:
-    let body = s[1].skipStmtList()
+    let body = skipStmtList(ctx, s[1])
     if body.kind != nkGotoState or i == 0:
       discard ctx.skipThroughEmptyStates(s)
       let excHandlState = ctx.exceptionTable[i]
@@ -1255,7 +1255,7 @@ proc deleteEmptyStates(ctx: var Ctx) =
 
   var i = 0
   while i < ctx.states.len - 1:
-    let fs = ctx.states[i][1].skipStmtList()
+    let fs = skipStmtList(ctx, ctx.states[i][1])
     if fs.kind == nkGotoState and i != 0:
       ctx.states.delete(i)
       ctx.exceptionTable.delete(i)
@@ -1271,10 +1271,10 @@ proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode =
     # Lambda lifting was not done yet. Use temporary :state sym, which
     # be handled specially by lambda lifting. Local temp vars (if needed)
     # should folllow the same logic.
-    ctx.stateVarSym = newSym(skVar, getIdent(":state"), fn, fn.info)
+    ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), fn, fn.info)
     ctx.stateVarSym.typ = g.createClosureIterStateType(fn)
 
-  ctx.stateLoopLabel = newSym(skLabel, getIdent(":stateLoop"), fn, fn.info)
+  ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), fn, fn.info)
   let n = n.toStmtList
 
   discard ctx.newState(n, nil)
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 09f63f0f5..866405f9f 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -26,7 +26,7 @@ bootSwitch(usedNoGC, defined(nogc), "--gc:none")
 
 import
   os, msgs, options, nversion, condsyms, strutils, extccomp, platform,
-  wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils, configuration
+  wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils, lineinfos
 
 # but some have deps to imported modules. Yay.
 bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
@@ -56,21 +56,21 @@ const
     x
   AdvancedUsage = slurp"../doc/advopt.txt".replace("//", "") % FeatureDesc
 
-proc getCommandLineDesc(): string =
-  result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name,
-                           CPU[platform.hostCPU].name, CompileDate]) &
+proc getCommandLineDesc(conf: ConfigRef): string =
+  result = (HelpMessage % [VersionAsString, platform.OS[conf.target.hostOS].name,
+                           CPU[conf.target.hostCPU].name, CompileDate]) &
                            Usage
 
 proc helpOnError(conf: ConfigRef; pass: TCmdLinePass) =
   if pass == passCmd1:
-    msgWriteln(conf, getCommandLineDesc(), {msgStdout})
+    msgWriteln(conf, getCommandLineDesc(conf), {msgStdout})
     msgQuit(0)
 
 proc writeAdvancedUsage(conf: ConfigRef; pass: TCmdLinePass) =
   if pass == passCmd1:
     msgWriteln(conf, (HelpMessage % [VersionAsString,
-                                 platform.OS[platform.hostOS].name,
-                                 CPU[platform.hostCPU].name, CompileDate]) &
+                                 platform.OS[conf.target.hostOS].name,
+                                 CPU[conf.target.hostCPU].name, CompileDate]) &
                                  AdvancedUsage,
                {msgStdout})
     msgQuit(0)
@@ -78,8 +78,8 @@ proc writeAdvancedUsage(conf: ConfigRef; pass: TCmdLinePass) =
 proc writeFullhelp(conf: ConfigRef; pass: TCmdLinePass) =
   if pass == passCmd1:
     msgWriteln(conf, `%`(HelpMessage, [VersionAsString,
-                                 platform.OS[platform.hostOS].name,
-                                 CPU[platform.hostCPU].name, CompileDate]) &
+                                 platform.OS[conf.target.hostOS].name,
+                                 CPU[conf.target.hostCPU].name, CompileDate]) &
                                  Usage & AdvancedUsage,
                {msgStdout})
     msgQuit(0)
@@ -87,8 +87,8 @@ proc writeFullhelp(conf: ConfigRef; pass: TCmdLinePass) =
 proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) =
   if pass == passCmd1:
     msgWriteln(conf, `%`(HelpMessage, [VersionAsString,
-                                 platform.OS[platform.hostOS].name,
-                                 CPU[platform.hostCPU].name, CompileDate]),
+                                 platform.OS[conf.target.hostOS].name,
+                                 CPU[conf.target.hostCPU].name, CompileDate]),
                {msgStdout})
 
     const gitHash = gorge("git log -n 1 --format=%H").strip
@@ -103,7 +103,7 @@ proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) =
 
 proc writeCommandLineUsage*(conf: ConfigRef; helpWritten: var bool) =
   if not helpWritten:
-    msgWriteln(conf, getCommandLineDesc(), {msgStdout})
+    msgWriteln(conf, getCommandLineDesc(conf), {msgStdout})
     helpWritten = true
 
 proc addPrefix(switch: string): string =
@@ -178,11 +178,11 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
   if i < len(arg) and (arg[i] in {':', '='}): inc(i)
   else: invalidCmdLineOption(conf, pass, orig, info)
   if state == wHint:
-    let x = findStr(configuration.HintsToStr, id)
+    let x = findStr(lineinfos.HintsToStr, id)
     if x >= 0: n = TNoteKind(x + ord(hintMin))
     else: localError(conf, info, "unknown hint: " & id)
   else:
-    let x = findStr(configuration.WarningsToStr, id)
+    let x = findStr(lineinfos.WarningsToStr, id)
     if x >= 0: n = TNoteKind(x + ord(warnMin))
     else: localError(conf, info, "unknown warning: " & id)
   case substr(arg, i).normalize
@@ -289,14 +289,14 @@ proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
           else:
             conf.projectPath / path
   try:
-    result = pathSubs(conf, p, info.toFullPath().splitFile().dir)
+    result = pathSubs(conf, p, toFullPath(conf, info).splitFile().dir)
   except ValueError:
     localError(conf, info, "invalid path: " & p)
     result = p
 
 proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): string =
   let path = if path[0] == '"': strutils.unescape(path) else: path
-  let basedir = info.toFullPath().splitFile().dir
+  let basedir = toFullPath(conf, info).splitFile().dir
   let p = if os.isAbsolute(path) or '$' in path:
             path
           else:
@@ -322,9 +322,9 @@ proc trackDirty(conf: ConfigRef; arg: string, info: TLineInfo) =
 
   let dirtyOriginalIdx = fileInfoIdx(conf, a[1])
   if dirtyOriginalIdx.int32 >= 0:
-    msgs.setDirtyFile(dirtyOriginalIdx, a[0])
+    msgs.setDirtyFile(conf, dirtyOriginalIdx, a[0])
 
-  gTrackPos = newLineInfo(dirtyOriginalIdx, line, column)
+  conf.m.trackPos = newLineInfo(dirtyOriginalIdx, line, column)
 
 proc track(conf: ConfigRef; arg: string, info: TLineInfo) =
   var a = arg.split(',')
@@ -334,7 +334,7 @@ proc track(conf: ConfigRef; arg: string, info: TLineInfo) =
     localError(conf, info, errInvalidNumber % a[1])
   if parseUtils.parseInt(a[2], column) <= 0:
     localError(conf, info, errInvalidNumber % a[2])
-  gTrackPos = newLineInfo(conf, a[0], line, column)
+  conf.m.trackPos = newLineInfo(conf, a[0], line, column)
 
 proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) =
   if pass in {passCmd2, passPP}:
@@ -344,8 +344,6 @@ proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, in
 proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
                     conf: ConfigRef) =
   var
-    theOS: TSystemOS
-    cpu: TSystemCPU
     key, val: string
   case switch.normalize
   of "path", "p":
@@ -565,13 +563,13 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     if pass in {passCmd2, passPP}: extccomp.addLinkOptionCmd(conf, arg)
   of "cincludes":
     expectArg(conf, switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: cIncludes.add processPath(conf, arg, info)
+    if pass in {passCmd2, passPP}: conf.cIncludes.add processPath(conf, arg, info)
   of "clibdir":
     expectArg(conf, switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: cLibs.add processPath(conf, arg, info)
+    if pass in {passCmd2, passPP}: conf.cLibs.add processPath(conf, arg, info)
   of "clib":
     expectArg(conf, switch, arg, pass, info)
-    if pass in {passCmd2, passPP}: cLinkedLibs.add processPath(conf, arg, info)
+    if pass in {passCmd2, passPP}: conf.cLinkedLibs.add processPath(conf, arg, info)
   of "header":
     if conf != nil: conf.headerFile = arg
     incl(conf.globalOptions, optGenIndex)
@@ -592,23 +590,26 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
   of "os":
     expectArg(conf, switch, arg, pass, info)
     if pass in {passCmd1, passPP}:
-      theOS = platform.nameToOS(arg)
+      let theOS = platform.nameToOS(arg)
       if theOS == osNone: localError(conf, info, "unknown OS: '$1'" % arg)
-      elif theOS != platform.hostOS:
-        setTarget(theOS, targetCPU)
+      elif theOS != conf.target.hostOS:
+        setTarget(conf.target, theOS, conf.target.targetCPU)
   of "cpu":
     expectArg(conf, switch, arg, pass, info)
     if pass in {passCmd1, passPP}:
-      cpu = platform.nameToCPU(arg)
+      let cpu = platform.nameToCPU(arg)
       if cpu == cpuNone: localError(conf, info, "unknown CPU: '$1'" % arg)
-      elif cpu != platform.hostCPU:
-        setTarget(targetOS, cpu)
+      elif cpu != conf.target.hostCPU:
+        setTarget(conf.target, conf.target.targetOS, cpu)
   of "run", "r":
     expectNoArg(conf, switch, arg, pass, info)
     incl(conf.globalOptions, optRun)
   of "verbosity":
     expectArg(conf, switch, arg, pass, info)
-    conf.verbosity = parseInt(arg)
+    let verbosity = parseInt(arg)
+    if verbosity notin {0..3}:
+      localError(conf, info, "invalid verbosity level: '$1'" % arg)
+    conf.verbosity = verbosity
     conf.notes = NotesVerbosity[conf.verbosity]
     incl(conf.notes, conf.enableNotes)
     excl(conf.notes, conf.disableNotes)
@@ -628,9 +629,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
   of "help", "h":
     expectNoArg(conf, switch, arg, pass, info)
     helpOnError(conf, pass)
-  of "symbolfiles":
+  of "symbolfiles", "incremental":
     case arg.normalize
-    of "on": conf.symbolFiles = enabledSf
+    of "on": conf.symbolFiles = v2Sf
     of "off": conf.symbolFiles = disabledSf
     of "writeonly": conf.symbolFiles = writeOnlySf
     of "readonly": conf.symbolFiles = readOnlySf
@@ -719,6 +720,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     doAssert(conf != nil)
     incl(conf.features, destructor)
     defineSymbol(conf.symbols, "nimNewRuntime")
+  of "nep1":
+    processOnOffSwitchG(conf, {optCheckNep1}, arg, pass, info)
   of "cppcompiletonamespace":
     expectNoArg(conf, switch, arg, pass, info)
     incl conf.globalOptions, optUseNimNamespace
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 773c0faf9..8b2d36722 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -44,6 +44,7 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimcomputedgoto")
   defineSymbol("nimunion")
   defineSymbol("nimnewshared")
+  defineSymbol("nimNewTypedesc")
   defineSymbol("nimrequiresnimframe")
   defineSymbol("nimparsebiggestfloatmagic")
   defineSymbol("nimalias")
@@ -70,3 +71,4 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimNoNil")
   defineSymbol("nimNoZeroTerminator")
   defineSymbol("nimNotNil")
+  defineSymbol("nimVmExportFixed")
diff --git a/compiler/configuration.nim b/compiler/configuration.nim
index f9f0e623c..22e0b834e 100644
--- a/compiler/configuration.nim
+++ b/compiler/configuration.nim
@@ -1,360 +1,6 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2018 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
+## Use the module 'lineinfos' instead!
 
-## This module contains the rather excessive configuration object that
-## needs to be passed around to everything so that the compiler becomes
-## more useful as a library.
+{.deprecated.}
 
-import tables
-
-const
-  explanationsBaseUrl* = "https://nim-lang.org/docs/manual"
-
-type
-  TMsgKind* = enum
-    errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile,
-    errXExpected,
-    errGridTableNotImplemented,
-    errGeneralParseError,
-    errNewSectionExpected,
-    errInvalidDirectiveX,
-    errGenerated,
-    errUser,
-    warnCannotOpenFile,
-    warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
-    warnDeprecated, warnConfigDeprecated,
-    warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
-    warnUnknownSubstitutionX, warnLanguageXNotSupported,
-    warnFieldXNotSupported, warnCommentXIgnored,
-    warnTypelessParam,
-    warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
-    warnEachIdentIsTuple, warnShadowIdent,
-    warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
-    warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
-    warnInconsistentSpacing, warnUser,
-    hintSuccess, hintSuccessX,
-    hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
-    hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
-    hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
-    hintConditionAlwaysTrue, hintName, hintPattern,
-    hintExecuting, hintLinking, hintDependency,
-    hintSource, hintPerformance, hintStackTrace, hintGCStats,
-    hintUser, hintUserRaw
-
-const
-  MsgKindToStr*: array[TMsgKind, string] = [
-    errUnknown: "unknown error",
-    errInternal: "internal error: $1",
-    errIllFormedAstX: "illformed AST: $1",
-    errCannotOpenFile: "cannot open '$1'",
-    errXExpected: "'$1' expected",
-    errGridTableNotImplemented: "grid table is not implemented",
-    errGeneralParseError: "general parse error",
-    errNewSectionExpected: "new section expected",
-    errInvalidDirectiveX: "invalid directive: '$1'",
-    errGenerated: "$1",
-    errUser: "$1",
-    warnCannotOpenFile: "cannot open '$1'",
-    warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
-    warnXIsNeverRead: "'$1' is never read",
-    warnXmightNotBeenInit: "'$1' might not have been initialized",
-    warnDeprecated: "$1 is deprecated",
-    warnConfigDeprecated: "config file '$1' is deprecated",
-    warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
-    warnUnknownMagic: "unknown magic '$1' might crash the compiler",
-    warnRedefinitionOfLabel: "redefinition of label '$1'",
-    warnUnknownSubstitutionX: "unknown substitution '$1'",
-    warnLanguageXNotSupported: "language '$1' not supported",
-    warnFieldXNotSupported: "field '$1' not supported",
-    warnCommentXIgnored: "comment '$1' ignored",
-    warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
-    warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
-    warnWriteToForeignHeap: "write to foreign heap",
-    warnUnsafeCode: "unsafe code: '$1'",
-    warnEachIdentIsTuple: "each identifier is a tuple",
-    warnShadowIdent: "shadowed identifier: '$1'",
-    warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.",
-    warnProveField: "cannot prove that field '$1' is accessible",
-    warnProveIndex: "cannot prove index '$1' is valid",
-    warnGcUnsafe: "not GC-safe: '$1'",
-    warnGcUnsafe2: "$1",
-    warnUninit: "'$1' might not have been initialized",
-    warnGcMem: "'$1' uses GC'ed memory",
-    warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
-    warnLockLevel: "$1",
-    warnResultShadowed: "Special variable 'result' is shadowed.",
-    warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
-    warnUser: "$1",
-    hintSuccess: "operation successful",
-    hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
-    hintLineTooLong: "line too long",
-    hintXDeclaredButNotUsed: "'$1' is declared but not used",
-    hintConvToBaseNotNeeded: "conversion to base object is not needed",
-    hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless",
-    hintExprAlwaysX: "expression evaluates always to '$1'",
-    hintQuitCalled: "quit() called",
-    hintProcessing: "$1",
-    hintCodeBegin: "generated code listing:",
-    hintCodeEnd: "end of listing",
-    hintConf: "used config file '$1'",
-    hintPath: "added path: '$1'",
-    hintConditionAlwaysTrue: "condition is always true: '$1'",
-    hintName: "name should be: '$1'",
-    hintPattern: "$1",
-    hintExecuting: "$1",
-    hintLinking: "",
-    hintDependency: "$1",
-    hintSource: "$1",
-    hintPerformance: "$1",
-    hintStackTrace: "$1",
-    hintGCStats: "$1",
-    hintUser: "$1",
-    hintUserRaw: "$1"]
-
-const
-  WarningsToStr* = ["CannotOpenFile", "OctalEscape",
-    "XIsNeverRead", "XmightNotBeenInit",
-    "Deprecated", "ConfigDeprecated",
-    "SmallLshouldNotBeUsed", "UnknownMagic",
-    "RedefinitionOfLabel", "UnknownSubstitutionX",
-    "LanguageXNotSupported", "FieldXNotSupported",
-    "CommentXIgnored",
-    "TypelessParam", "UseBase", "WriteToForeignHeap",
-    "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
-    "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
-    "GcMem", "Destructor", "LockLevel", "ResultShadowed",
-    "Spacing", "User"]
-
-  HintsToStr* = ["Success", "SuccessX", "LineTooLong",
-    "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
-    "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
-    "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
-    "Source", "Performance", "StackTrace", "GCStats",
-    "User", "UserRaw"]
-
-const
-  fatalMin* = errUnknown
-  fatalMax* = errInternal
-  errMin* = errUnknown
-  errMax* = errUser
-  warnMin* = warnCannotOpenFile
-  warnMax* = pred(hintSuccess)
-  hintMin* = hintSuccess
-  hintMax* = high(TMsgKind)
-
-static:
-  doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1
-  doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1
-
-type
-  TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
-  TNoteKinds* = set[TNoteKind]
-
-const
-  NotesVerbosity*: array[0..3, TNoteKinds] = [
-    {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
-                                         warnProveField, warnProveIndex,
-                                         warnGcUnsafe,
-                                         hintSuccessX, hintPath, hintConf,
-                                         hintProcessing, hintPattern,
-                                         hintDependency,
-                                         hintExecuting, hintLinking,
-                                         hintCodeBegin, hintCodeEnd,
-                                         hintSource, hintStackTrace,
-                                         hintGCStats},
-    {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
-                                         warnProveField, warnProveIndex,
-                                         warnGcUnsafe,
-                                         hintPath,
-                                         hintDependency,
-                                         hintCodeBegin, hintCodeEnd,
-                                         hintSource, hintStackTrace,
-                                         hintGCStats},
-    {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit},
-    {low(TNoteKind)..high(TNoteKind)}]
-
-const
-  errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
-  errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
-
-#[
-errStringLiteralExpected: "string literal expected",
-errIntLiteralExpected: "integer literal expected",
-errIdentifierExpected: "identifier expected, but found '$1'",
-errNewlineExpected: "newline expected, but found '$1'",
-errInvalidModuleName: "invalid module name: '$1'",
-errOnOrOffExpected: "'on' or 'off' expected",
-errNoneSpeedOrSizeExpected: "'none', 'speed' or 'size' expected",
-errInvalidPragma: "invalid pragma",
-errUnknownPragma: "unknown pragma: '$1'",
-errAtPopWithoutPush: "'pop' without a 'push' pragma",
-errEmptyAsm: "empty asm statement",
-errInvalidIndentation: "invalid indentation",
-
-errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type",
-errAttemptToRedefine: ,
-errStmtInvalidAfterReturn: "statement not allowed after 'return', 'break', 'raise', 'continue' or proc call with noreturn pragma",
-errStmtExpected: "statement expected",
-errInvalidLabel: "'$1' is no label",
-errInvalidCmdLineOption: "invalid command line option: '$1'",
-errCmdLineArgExpected: "argument for command line option expected: '$1'",
-errCmdLineNoArgExpected: "invalid argument for command line option: '$1'",
-errInvalidVarSubstitution: "invalid variable substitution in '$1'",
-errUnknownVar: "unknown variable: '$1'",
-errUnknownCcompiler: "unknown C compiler: '$1'",
-errOnOrOffExpectedButXFound: "'on' or 'off' expected, but '$1' found",
-errOnOffOrListExpectedButXFound: "'on', 'off' or 'list' expected, but '$1' found",
-errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found",
-,
-errInvalidMultipleAsgn: "multiple assignment is not allowed",
-errColonOrEqualsExpected: "':' or '=' expected, but found '$1'",
-errUndeclaredField: "undeclared field: '$1'",
-errUndeclaredRoutine: "attempting to call undeclared routine: '$1'",
-errUseQualifier: "ambiguous identifier: '$1' -- use a qualifier",
-errTypeExpected: "type expected",
-errSystemNeeds: "system module needs '$1'",
-errExecutionOfProgramFailed: "execution of an external program failed: '$1'",
-errNotOverloadable: ,
-errInvalidArgForX: "invalid argument for '$1'",
-errStmtHasNoEffect: "statement has no effect",
-,
-errXExpectsArrayType: "'$1' expects an array type",
-errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet",
-errExprXAmbiguous: "expression '$1' ambiguous in this context",
-errConstantDivisionByZero: ,
-errOrdinalOrFloatTypeExpected: "ordinal or float type expected",
-errOverOrUnderflow: ,
-errCannotEvalXBecauseIncompletelyDefined: ,
-errChrExpectsRange0_255: "'chr' expects an int in the range 0..255",
-errDynlibRequiresExportc: "'dynlib' requires 'exportc'",
-errNilAccess: "attempt to access a nil address",
-errIndexOutOfBounds: "index out of bounds",
-errIndexTypesDoNotMatch: "index types do not match",
-errBracketsInvalidForType: "'[]' operator invalid for this type",
-errValueOutOfSetBounds: "value out of set bounds",
-errFieldNotInit: "field '$1' not initialized",
-errExprXCannotBeCalled: "expression '$1' cannot be called",
-errExprHasNoType: "expression has no type",
-errExprXHasNoType:,
-errCastNotInSafeMode: "'cast' not allowed in safe mode",
-errExprCannotBeCastToX: ,
-errCommaOrParRiExpected: "',' or ')' expected",
-errCurlyLeOrParLeExpected: "'{' or '(' expected",
-errSectionExpected: "section ('type', 'proc', etc.) expected",
-errRangeExpected: "range expected",
-errMagicOnlyInSystem: "'magic' only allowed in system module",
-errPowerOfTwoExpected: "power of two expected",
-errStringMayNotBeEmpty: "string literal may not be empty",
-errCallConvExpected: "calling convention expected",
-errProcOnlyOneCallConv: "a proc can only have one calling convention",
-errSymbolMustBeImported: "symbol must be imported if 'lib' pragma is used",
-errExprMustBeBool: "expression must be of type 'bool'",
-errConstExprExpected: "constant expression expected",
-errDuplicateCaseLabel: "duplicate case label",
-errRangeIsEmpty: "range is empty",
-,
-errSelectorMustBeOrdinal: "selector must be of an ordinal type",
-errOrdXMustNotBeNegative: "ord($1) must not be negative",
-errLenXinvalid: "len($1) must be less than 32768",
-errTypeXhasUnknownSize: "type '$1' has unknown size",
-errConstNeedsConstExpr: "a constant can only be initialized with a constant expression",
-errConstNeedsValue: "a constant needs a value",
-errResultCannotBeOpenArray: "the result type cannot be on open array",
-errSizeTooBig: "computing the type's size produced an overflow",
-errInheritanceOnlyWithEnums: "inheritance only works with an enum",
-errIllegalRecursionInTypeX:,
-errCannotInstantiateX: "cannot instantiate: '$1'",
-errTypeMismatch: "type mismatch: got <",
-errButExpected: "but expected one of: ",
-errButExpectedX: "but expected '$1'",
-errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
-errWrongNumberOfArguments: "wrong number of arguments",
-errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
-errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters",
-errXCannotBePassedToProcVar: ,
-,
-errImplOfXexpected: ,
-
-errIllegalConvFromXtoY: ,
-errCannotBindXTwice: "cannot bind parameter '$1' twice",
-errInvalidOrderInArrayConstructor: ,
-errInvalidOrderInEnumX: "invalid order in enum '$1'",
-errEnumXHasHoles: "enum '$1' has holes",
-errExceptExpected: "'except' or 'finally' expected",
-errInvalidTry: "after catch all 'except' or 'finally' no section may follow",
-errOptionExpected: ,
-errXisNoLabel: "'$1' is not a label",
-errNotAllCasesCovered: "not all cases are covered",
-errUnknownSubstitionVar: "unknown substitution variable: '$1'",
-errComplexStmtRequiresInd: "complex statement requires indentation",
-errXisNotCallable: "'$1' is not callable",
-errNoPragmasAllowedForX: "no pragmas allowed for $1",
-,
-errInvalidParamKindX: "invalid param kind: '$1'",
-errDefaultArgumentInvalid: "default argument invalid",
-errNamedParamHasToBeIdent: "named parameter has to be an identifier",
-errNoReturnTypeForX: "no return type allowed for $1",
-errConvNeedsOneArg: "a type conversion needs exactly one argument",
-errInvalidPragmaX: ,
-errXNotAllowedHere: "$1 not allowed here",
-errXisNoType: "invalid type: '$1'",
-errCircumNeedsPointer: "'[]' needs a pointer or reference type",
-errInvalidExpression: "invalid expression",
-errInvalidExpressionX: "invalid expression: '$1'",
-errEnumHasNoValueX: "enum has no value '$1'",
-,
-errNoCommand: "no command given",
-errInvalidCommandX: "invalid command: '$1'",
-errXNeedsParamObjectType: ,
-errTemplateInstantiationTooNested: "template instantiation too nested, try --evalTemplateLimit:N",
-errMacroInstantiationTooNested: "macro instantiation too nested, try --evalMacroLimit:N",
-errInstantiationFrom: "template/generic instantiation from here",
-errInvalidIndexValueForTuple: "invalid index value for tuple subscript",
-errCommandExpectsFilename: "command expects a filename argument",
-errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
-errXExpected: "'$1' expected",
-,
-errCastToANonConcreteType: "cannot cast to a non concrete type: '$1'",
-errInvalidSectionStart: "invalid section start",
-errGridTableNotImplemented: "grid table is not implemented",
-errGeneralParseError: "general parse error",
-errNewSectionExpected: "new section expected",
-errWhitespaceExpected: "whitespace expected, got '$1'",
-errXisNoValidIndexFile: "'$1' is no valid index file",
-errCannotRenderX: "cannot render reStructuredText element '$1'",
-errVarVarTypeNotAllowed: ,
-errInstantiateXExplicitly: "instantiate '$1' explicitly",
-errOnlyACallOpCanBeDelegator: ,
-errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
-errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " &
-                                   "because the parameter '$1' has a generic type",
-errDestructorNotGenericEnough: "Destructor signature is too specific. " &
-                               "A destructor must be associated will all instantiations of a generic type",
-errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " &
-                                "templates, macros and other inline iterators",
-errXExpectsTwoArguments: "'$1' expects two arguments",
-errXExpectsObjectTypes: "'$1' expects object types",
-errXcanNeverBeOfThisSubtype: "'$1' can never be of this subtype",
-errTooManyIterations: "interpretation requires too many iterations; " &
-  "if you are sure this is not a bug in your code edit " &
-  "compiler/vmdef.MaxLoopIterations and rebuild the compiler",
-errFieldXNotFound: "field '$1' cannot be found",
-errInvalidConversionFromTypeX: "invalid conversion from type '$1'",
-errAssertionFailed: "assertion failed",
-errCannotGenerateCodeForX: "cannot generate code for '$1'",
-errXRequiresOneArgument: "$1 requires one parameter",
-errUnhandledExceptionX: "unhandled exception: $1",
-errCyclicTree: "macro returned a cyclic abstract syntax tree",
-errXisNoMacroOrTemplate: "'$1' is no macro or template",
-errXhasSideEffects: "'$1' can have side effects",
-errWrongSymbolX:,
-errIllegalCaptureX: "illegal capture '$1'",
-errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
-,
-]#
+import lineinfos
+export lineinfos
diff --git a/compiler/depends.nim b/compiler/depends.nim
index 732404232..d0a1139ef 100644
--- a/compiler/depends.nim
+++ b/compiler/depends.nim
@@ -14,46 +14,51 @@ import
 
 from modulegraphs import ModuleGraph
 
-proc generateDot*(project: string)
-
 type
   TGen = object of TPassContext
-    module*: PSym
+    module: PSym
     config: ConfigRef
+    graph: ModuleGraph
   PGen = ref TGen
 
-var gDotGraph: Rope # the generated DOT file; we need a global variable
+  Backend = ref object of RootRef
+    dotGraph: Rope
 
-proc addDependencyAux(importing, imported: string) =
-  addf(gDotGraph, "$1 -> \"$2\";$n", [rope(importing), rope(imported)])
+proc addDependencyAux(b: Backend; importing, imported: string) =
+  addf(b.dotGraph, "$1 -> \"$2\";$n", [rope(importing), rope(imported)])
   # s1 -> s2_4[label="[0-9]"];
 
 proc addDotDependency(c: PPassContext, n: PNode): PNode =
   result = n
-  var g = PGen(c)
+  let g = PGen(c)
+  let b = Backend(g.graph.backend)
   case n.kind
   of nkImportStmt:
     for i in countup(0, sonsLen(n) - 1):
       var imported = getModuleName(g.config, n.sons[i])
-      addDependencyAux(g.module.name.s, imported)
+      addDependencyAux(b, g.module.name.s, imported)
   of nkFromStmt, nkImportExceptStmt:
     var imported = getModuleName(g.config, n.sons[0])
-    addDependencyAux(g.module.name.s, imported)
+    addDependencyAux(b, g.module.name.s, imported)
   of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:
     for i in countup(0, sonsLen(n) - 1): discard addDotDependency(c, n.sons[i])
   else:
     discard
 
-proc generateDot(project: string) =
-  writeRope("digraph $1 {$n$2}$n" % [
-      rope(changeFileExt(extractFilename(project), "")), gDotGraph],
+proc generateDot*(graph: ModuleGraph; project: string) =
+  let b = Backend(graph.backend)
+  discard writeRope("digraph $1 {$n$2}$n" % [
+      rope(changeFileExt(extractFilename(project), "")), b.dotGraph],
             changeFileExt(project, "dot"))
 
-proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
+proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
   var g: PGen
   new(g)
   g.module = module
   g.config = graph.config
+  g.graph = graph
+  if graph.backend == nil:
+    graph.backend = Backend(dotGraph: nil)
   result = g
 
 const gendependPass* = makePass(open = myOpen, process = addDotDependency)
diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim
index 31c735794..0395728c2 100644
--- a/compiler/destroyer.nim
+++ b/compiler/destroyer.nim
@@ -116,8 +116,8 @@ Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently
 
 import
   intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
-  strutils, options, dfa, lowerings, rodread, tables, modulegraphs,
-  configuration
+  strutils, options, dfa, lowerings, tables, modulegraphs,
+  lineinfos
 
 const
   InterestingSyms = {skVar, skResult, skLet}
@@ -132,10 +132,11 @@ type
     destroys, topLevelVars: PNode
     toDropBit: Table[int, PSym]
     graph: ModuleGraph
+    emptyNode: PNode
 
 proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode =
   # XXX why are temps fields in an object here?
-  let f = newSym(skField, getIdent(":d" & $c.tmpObj.n.len), c.owner, info)
+  let f = newSym(skField, getIdent(c.graph.cache, ":d" & $c.tmpObj.n.len), c.owner, info)
   f.typ = typ
   rawAddField c.tmpObj, f
   result = rawDirectAccess(c.tmp, f)
@@ -243,17 +244,17 @@ proc genDestroy(c: Con; t: PType; dest: PNode): PNode =
   genOp(t.destructor, "=destroy")
 
 proc addTopVar(c: var Con; v: PNode) =
-  c.topLevelVars.add newTree(nkIdentDefs, v, emptyNode, emptyNode)
+  c.topLevelVars.add newTree(nkIdentDefs, v, c.emptyNode, c.emptyNode)
 
 proc dropBit(c: var Con; s: PSym): PSym =
   result = c.toDropBit.getOrDefault(s.id)
   assert result != nil
 
 proc registerDropBit(c: var Con; s: PSym) =
-  let result = newSym(skTemp, getIdent(s.name.s & "_AliveBit"), c.owner, s.info)
+  let result = newSym(skTemp, getIdent(c.graph.cache, s.name.s & "_AliveBit"), c.owner, s.info)
   result.typ = getSysType(c.graph, s.info, tyBool)
   let trueVal = newIntTypeNode(nkIntLit, 1, result.typ)
-  c.topLevelVars.add newTree(nkIdentDefs, newSymNode result, emptyNode, trueVal)
+  c.topLevelVars.add newTree(nkIdentDefs, newSymNode result, c.emptyNode, trueVal)
   c.toDropBit[s.id] = result
   # generate:
   #  if not sinkParam_AliveBit: `=destroy`(sinkParam)
@@ -322,13 +323,13 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode =
   # generate: (let tmp = v; reset(v); tmp)
   result = newNodeIT(nkStmtListExpr, n.info, n.typ)
 
-  var temp = newSym(skLet, getIdent("blitTmp"), c.owner, n.info)
+  var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.owner, n.info)
   var v = newNodeI(nkLetSection, n.info)
   let tempAsNode = newSymNode(temp)
 
   var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
   vpart.sons[0] = tempAsNode
-  vpart.sons[1] = ast.emptyNode
+  vpart.sons[1] = c.emptyNode
   vpart.sons[2] = n
   add(v, vpart)
 
@@ -427,13 +428,14 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
     echo "injecting into ", n
   var c: Con
   c.owner = owner
-  c.tmp = newSym(skTemp, getIdent":d", owner, n.info)
+  c.tmp = newSym(skTemp, getIdent(g.cache, ":d"), owner, n.info)
   c.tmpObj = createObj(g, owner, n.info)
   c.tmp.typ = c.tmpObj
   c.destroys = newNodeI(nkStmtList, n.info)
   c.topLevelVars = newNodeI(nkVarSection, n.info)
   c.toDropBit = initTable[int, PSym]()
   c.graph = g
+  c.emptyNode = newNodeI(nkEmpty, n.info)
   let cfg = constructCfg(owner, n)
   shallowCopy(c.g, cfg)
   c.jumpTargets = initIntSet()
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index 0fd706178..013242f62 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -23,7 +23,7 @@
 ## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen.
 ## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
 
-import ast, astalgo, types, intsets, tables, msgs, options
+import ast, astalgo, types, intsets, tables, msgs, options, lineinfos
 
 type
   InstrKind* = enum
@@ -54,7 +54,7 @@ type
     blocks: seq[TBlock]
 
 proc debugInfo(info: TLineInfo): string =
-  result = info.toFilename & ":" & $info.line
+  result = $info.line #info.toFilename & ":" & $info.line
 
 proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) =
   # for debugging purposes
@@ -87,7 +87,8 @@ proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} =
   ## echos the ControlFlowGraph for debugging purposes.
   var buf = ""
   codeListing(c, buf, start, last)
-  echo buf
+  when declared(echo):
+    echo buf
 
 proc forkI(c: var Con; n: PNode): TPosition =
   result = TPosition(c.code.len)
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 7ab2f0eee..db4e301d4 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -16,7 +16,7 @@ import
   wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast,
   packages/docutils/rst, packages/docutils/rstgen, times,
   packages/docutils/highlite, sempass2, json, xmltree, cgi,
-  typesrenderer, astalgo, modulepaths, configuration
+  typesrenderer, astalgo, modulepaths, lineinfos
 
 type
   TSections = array[TSymKind, Rope]
@@ -30,6 +30,7 @@ type
     types: TStrTable
     isPureRst: bool
     conf*: ConfigRef
+    cache*: IdentCache
 
   PDoc* = ref TDocumentor ## Alias to type less.
 
@@ -86,10 +87,11 @@ proc parseRst(text, filename: string,
   result = rstParse(text, filename, line, column, hasToc, rstOptions,
                     docgenFindFile, compilerMsgHandler)
 
-proc newDocumentor*(filename: string, conf: ConfigRef): PDoc =
+proc newDocumentor*(filename: string; cache: IdentCache; conf: ConfigRef): PDoc =
   declareClosures()
   new(result)
   result.conf = conf
+  result.cache = cache
   initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex),
                    conf.configVars, filename, {roSupportRawDirective},
                    docgenFindFile, compilerMsgHandler)
@@ -190,7 +192,7 @@ proc genComment(d: PDoc, n: PNode): string =
   result = ""
   var dummyHasToc: bool
   if n.comment != nil:
-    renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
+    renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info),
                                toLinenumber(n.info), toColumn(n.info),
                                dummyHasToc, d.options, d.conf), result)
 
@@ -307,13 +309,13 @@ when false:
       result = findDocComment(n.sons[i])
       if result != nil: return
 
-  proc extractDocComment*(s: PSym, d: PDoc = nil): string =
+  proc extractDocComment*(s: PSym, d: PDoc): string =
     let n = findDocComment(s.ast)
     result = ""
     if not n.isNil:
       if not d.isNil:
         var dummyHasToc: bool
-        renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
+        renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info),
                                      toLinenumber(n.info), toColumn(n.info),
                                      dummyHasToc, d.options + {roSkipPounds}),
                        result)
@@ -349,18 +351,18 @@ proc getName(d: PDoc, n: PNode, splitAfter = -1): string =
   else:
     result = ""
 
-proc getNameIdent(n: PNode): PIdent =
+proc getNameIdent(cache: IdentCache; n: PNode): PIdent =
   case n.kind
-  of nkPostfix: result = getNameIdent(n.sons[1])
-  of nkPragmaExpr: result = getNameIdent(n.sons[0])
+  of nkPostfix: result = getNameIdent(cache, n.sons[1])
+  of nkPragmaExpr: result = getNameIdent(cache, n.sons[0])
   of nkSym: result = n.sym.name
   of nkIdent: result = n.ident
   of nkAccQuoted:
     var r = ""
-    for i in 0..<n.len: r.add(getNameIdent(n[i]).s)
-    result = getIdent(r)
+    for i in 0..<n.len: r.add(getNameIdent(cache, n[i]).s)
+    result = getIdent(cache, r)
   of nkOpenSymChoice, nkClosedSymChoice:
-    result = getNameIdent(n[0])
+    result = getNameIdent(cache, n[0])
   else:
     result = nil
 
@@ -502,7 +504,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
   let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc")
   if docItemSeeSrc.len > 0:
     let cwd = canonicalizePath(d.conf, getCurrentDir())
-    var path = n.info.toFullPath
+    var path = toFullPath(d.conf, n.info)
     if path.startsWith(cwd):
       path = path[cwd.len+1 .. ^1].replace('\\', '/')
     let gitUrl = getConfigVar(d.conf, "git.url")
@@ -580,27 +582,27 @@ proc traceDeps(d: PDoc, it: PNode) =
     if d.section[k] != nil: add(d.section[k], ", ")
     dispA(d.conf, d.section[k],
           "<a class=\"reference external\" href=\"$1.html\">$1</a>",
-          "$1", [rope(getModuleName(d.conf, it))])
+          "$1", [rope(splitFile(getModuleName(d.conf, it)).name)])
 
 proc generateDoc*(d: PDoc, n: PNode) =
   case n.kind
   of nkCommentStmt: add(d.modDesc, genComment(d, n))
   of nkProcDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     genItem(d, n, n.sons[namePos], skProc)
   of nkFuncDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     genItem(d, n, n.sons[namePos], skFunc)
   of nkMethodDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     genItem(d, n, n.sons[namePos], skMethod)
   of nkIteratorDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     genItem(d, n, n.sons[namePos], skIterator)
   of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro)
   of nkTemplateDef: genItem(d, n, n.sons[namePos], skTemplate)
   of nkConverterDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     genItem(d, n, n.sons[namePos], skConverter)
   of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
     for i in countup(0, sonsLen(n) - 1):
@@ -630,23 +632,23 @@ proc generateJson*(d: PDoc, n: PNode) =
       d.add %{ "comment": %stripped, "line": %n.info.line.int,
                "col": %n.info.col }
   of nkProcDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     d.add genJsonItem(d, n, n.sons[namePos], skProc)
   of nkFuncDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     d.add genJsonItem(d, n, n.sons[namePos], skFunc)
   of nkMethodDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     d.add genJsonItem(d, n, n.sons[namePos], skMethod)
   of nkIteratorDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     d.add genJsonItem(d, n, n.sons[namePos], skIterator)
   of nkMacroDef:
     d.add genJsonItem(d, n, n.sons[namePos], skMacro)
   of nkTemplateDef:
     d.add genJsonItem(d, n, n.sons[namePos], skTemplate)
   of nkConverterDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     d.add genJsonItem(d, n, n.sons[namePos], skConverter)
   of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
     for i in countup(0, sonsLen(n) - 1):
@@ -673,23 +675,23 @@ proc generateTags*(d: PDoc, n: PNode, r: var Rope) =
       let stripped = n.comment.substr(2).strip
       r.add stripped
   of nkProcDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     r.add genTagsItem(d, n, n.sons[namePos], skProc)
   of nkFuncDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     r.add genTagsItem(d, n, n.sons[namePos], skFunc)
   of nkMethodDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     r.add genTagsItem(d, n, n.sons[namePos], skMethod)
   of nkIteratorDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     r.add genTagsItem(d, n, n.sons[namePos], skIterator)
   of nkMacroDef:
     r.add genTagsItem(d, n, n.sons[namePos], skMacro)
   of nkTemplateDef:
     r.add genTagsItem(d, n, n.sons[namePos], skTemplate)
   of nkConverterDef:
-    when useEffectSystem: documentRaises(n)
+    when useEffectSystem: documentRaises(d.cache, n)
     r.add genTagsItem(d, n, n.sons[namePos], skConverter)
   of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
     for i in countup(0, sonsLen(n) - 1):
@@ -777,10 +779,14 @@ proc getOutFile2(conf: ConfigRef; filename, ext, dir: string): string =
 
 proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) =
   var content = genOutFile(d)
+  var success = true
   if optStdout in d.conf.globalOptions:
     writeRope(stdout, content)
   else:
-    writeRope(content, getOutFile2(d.conf, filename, outExt, "htmldocs"), useWarning)
+    let outfile = getOutFile2(d.conf, filename, outExt, "htmldocs")
+    success = writeRope(content, outfile)
+  if not success:
+    rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile, filename)
 
 proc writeOutputJson*(d: PDoc, filename, outExt: string,
                       useWarning = false) =
@@ -798,18 +804,18 @@ proc writeOutputJson*(d: PDoc, filename, outExt: string,
     else:
       discard "fixme: error report"
 
-proc commandDoc*(conf: ConfigRef) =
-  var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf)
+proc commandDoc*(cache: IdentCache, conf: ConfigRef) =
+  var ast = parseFile(conf.projectMainIdx, cache, conf)
   if ast == nil: return
-  var d = newDocumentor(conf.projectFull, conf)
+  var d = newDocumentor(conf.projectFull, cache, conf)
   d.hasToc = true
   generateDoc(d, ast)
   writeOutput(d, conf.projectFull, HtmlExt)
   generateIndex(d)
 
-proc commandRstAux(conf: ConfigRef; filename, outExt: string) =
+proc commandRstAux(cache: IdentCache, conf: ConfigRef; filename, outExt: string) =
   var filen = addFileExt(filename, "txt")
-  var d = newDocumentor(filen, conf)
+  var d = newDocumentor(filen, cache, conf)
   d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string;
                           status: int; content: string) =
     var outp: string
@@ -841,17 +847,16 @@ proc commandRstAux(conf: ConfigRef; filename, outExt: string) =
   writeOutput(d, filename, outExt)
   generateIndex(d)
 
-proc commandRst2Html*(conf: ConfigRef) =
-  commandRstAux(conf, conf.projectFull, HtmlExt)
+proc commandRst2Html*(cache: IdentCache, conf: ConfigRef) =
+  commandRstAux(cache, conf, conf.projectFull, HtmlExt)
 
-proc commandRst2TeX*(conf: ConfigRef) =
-  splitter = "\\-"
-  commandRstAux(conf, conf.projectFull, TexExt)
+proc commandRst2TeX*(cache: IdentCache, conf: ConfigRef) =
+  commandRstAux(cache, conf, conf.projectFull, TexExt)
 
-proc commandJson*(conf: ConfigRef) =
-  var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf)
+proc commandJson*(cache: IdentCache, conf: ConfigRef) =
+  var ast = parseFile(conf.projectMainIdx, cache, conf)
   if ast == nil: return
-  var d = newDocumentor(conf.projectFull, conf)
+  var d = newDocumentor(conf.projectFull, cache, conf)
   d.hasToc = true
   generateJson(d, ast)
   let json = d.jArray
@@ -861,12 +866,14 @@ proc commandJson*(conf: ConfigRef) =
     writeRope(stdout, content)
   else:
     #echo getOutFile(gProjectFull, JsonExt)
-    writeRope(content, getOutFile(conf, conf.projectFull, JsonExt), useWarning = false)
+    let filename = getOutFile(conf, conf.projectFull, JsonExt)
+    if not writeRope(content, filename):
+      rawMessage(conf, errCannotOpenFile, filename)
 
-proc commandTags*(conf: ConfigRef) =
-  var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf)
+proc commandTags*(cache: IdentCache, conf: ConfigRef) =
+  var ast = parseFile(conf.projectMainIdx, cache, conf)
   if ast == nil: return
-  var d = newDocumentor(conf.projectFull, conf)
+  var d = newDocumentor(conf.projectFull, cache, conf)
   d.hasToc = true
   var
     content: Rope
@@ -876,9 +883,11 @@ proc commandTags*(conf: ConfigRef) =
     writeRope(stdout, content)
   else:
     #echo getOutFile(gProjectFull, TagsExt)
-    writeRope(content, getOutFile(conf, conf.projectFull, TagsExt), useWarning = false)
+    let filename = getOutFile(conf, conf.projectFull, TagsExt)
+    if not writeRope(content, filename):
+      rawMessage(conf, errCannotOpenFile, filename)
 
-proc commandBuildIndex*(conf: ConfigRef) =
+proc commandBuildIndex*(cache: IdentCache, conf: ConfigRef) =
   var content = mergeIndexes(conf.projectFull).rope
 
   let code = ropeFormatNamedVars(conf, getConfigVar(conf, "doc.file"), ["title",
@@ -887,4 +896,6 @@ proc commandBuildIndex*(conf: ConfigRef) =
       ["Index".rope, nil, nil, rope(getDateStr()),
                    rope(getClockStr()), content, nil, nil, nil])
   # no analytics because context is not available
-  writeRope(code, getOutFile(conf, "theindex", HtmlExt))
+  let filename = getOutFile(conf, "theindex", HtmlExt)
+  if not writeRope(code, filename):
+    rawMessage(conf, errCannotOpenFile, filename)
diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim
index d9a73e1cd..068c47bb3 100644
--- a/compiler/docgen2.nim
+++ b/compiler/docgen2.nim
@@ -11,7 +11,7 @@
 # semantic checking.
 
 import
-  os, options, ast, astalgo, msgs, ropes, idents, passes, docgen
+  os, options, ast, astalgo, msgs, ropes, idents, passes, docgen, lineinfos
 
 from modulegraphs import ModuleGraph
 
@@ -25,7 +25,7 @@ template closeImpl(body: untyped) {.dirty.} =
   var g = PGen(p)
   let useWarning = sfMainModule notin g.module.flags
   #echo g.module.name.s, " ", g.module.owner.id, " ", gMainPackageId
-  if (g.module.owner.id == gMainPackageId and optWholeProject in g.doc.conf.globalOptions) or
+  if (g.module.owner.id == g.doc.conf.mainPackageId and optWholeProject in g.doc.conf.globalOptions) or
       sfMainModule in g.module.flags:
     body
     try:
@@ -35,11 +35,11 @@ template closeImpl(body: untyped) {.dirty.} =
 
 proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
   closeImpl:
-    writeOutput(g.doc, g.module.filename, HtmlExt, useWarning)
+    writeOutput(g.doc, toFilename(graph.config, FileIndex g.module.position), HtmlExt, useWarning)
 
 proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
   closeImpl:
-    writeOutputJson(g.doc, g.module.filename, ".json", useWarning)
+    writeOutputJson(g.doc, toFilename(graph.config, FileIndex g.module.position), ".json", useWarning)
 
 proc processNode(c: PPassContext, n: PNode): PNode =
   result = n
@@ -51,11 +51,11 @@ proc processNodeJson(c: PPassContext, n: PNode): PNode =
   var g = PGen(c)
   generateJson(g.doc, n)
 
-proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
+proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
   var g: PGen
   new(g)
   g.module = module
-  var d = newDocumentor(module.filename, graph.config)
+  var d = newDocumentor(toFilename(graph.config, FileIndex module.position), graph.cache, graph.config)
   d.hasToc = true
   g.doc = d
   result = g
diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim
index 0e3d0609d..e863c8995 100644
--- a/compiler/evalffi.nim
+++ b/compiler/evalffi.nim
@@ -442,7 +442,7 @@ proc callForeignFunction*(call: PNode): PNode =
   libffi.call(cif, fn, retVal, args)
 
   if retVal.isNil:
-    result = emptyNode
+    result = newNode(nkEmpty)
   else:
     result = unpack(retVal, typ.sons[0], nil)
     result.info = call.info
@@ -484,7 +484,7 @@ proc callForeignFunction*(fn: PNode, fntyp: PType,
   libffi.call(cif, fn, retVal, cargs)
 
   if retVal.isNil:
-    result = emptyNode
+    result = newNode(nkEmpty)
   else:
     result = unpack(retVal, fntyp.sons[0], nil)
     result.info = info
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index 01c56ec9c..d6c630e79 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -11,7 +11,7 @@
 
 import
   strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer,
-  rodread
+  lineinfos
 
 type
   TemplCtx = object
@@ -104,7 +104,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode
     let default = s.typ.n.sons[i].sym.ast
     if default.isNil or default.kind == nkEmpty:
       localError(conf, n.info, errWrongNumberOfArguments)
-      addSon(result, ast.emptyNode)
+      addSon(result, newNodeI(nkEmpty, n.info))
     else:
       addSon(result, default.copyTree)
 
@@ -114,7 +114,6 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode
 
 # to prevent endless recursion in template instantiation
 const evalTemplateLimit* = 1000
-var evalTemplateCounter* = 0 # XXX remove this global
 
 proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
   when true:
@@ -140,8 +139,8 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
 
 proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
                    conf: ConfigRef; fromHlo=false): PNode =
-  inc(evalTemplateCounter)
-  if evalTemplateCounter > evalTemplateLimit:
+  inc(conf.evalTemplateCounter)
+  if conf.evalTemplateCounter > evalTemplateLimit:
     globalError(conf, n.info, errTemplateInstantiationTooNested)
     result = n
 
@@ -170,5 +169,5 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
       evalTemplateAux(body.sons[i], args, ctx, result)
   result.flags.incl nfFromTemplate
   result = wrapInComesFrom(n.info, tmpl, result)
-  dec(evalTemplateCounter)
+  dec(conf.evalTemplateCounter)
 
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 3f0e6f611..17133624b 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -9,19 +9,14 @@
 
 # Module providing functions for calling the different external C compilers
 # Uses some hard-wired facts about each C/C++ compiler, plus options read
-# from a configuration file, to provide generalized procedures to compile
+# from a lineinfos file, to provide generalized procedures to compile
 # nim files.
 
 import
   ropes, os, strutils, osproc, platform, condsyms, options, msgs,
-  configuration, std / sha1, streams
-
-#from debuginfo import writeDebugInfo
+  lineinfos, std / sha1, streams
 
 type
-  TSystemCC* = enum
-    ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
-    ccTcc, ccPcc, ccUcc, ccIcl, ccIcc
   TInfoCCProp* = enum         # properties of the C compiler:
     hasSwitchRange,           # CC allows ranges in switch statements (GNU C)
     hasComputedGoto,          # CC has computed goto (GNU C extension)
@@ -336,37 +331,8 @@ const
 
   hExt* = ".h"
 
-var
-  cCompiler* = ccGcc # the used compiler
-  gMixedMode*: bool  # true if some module triggered C++ codegen
-  cIncludes*: seq[string] = @[]   # directories to search for included files
-  cLibs*: seq[string] = @[]       # directories to search for lib files
-  cLinkedLibs*: seq[string] = @[] # libraries to link
-
-# implementation
-
-proc libNameTmpl(): string {.inline.} =
-  result = if targetOS == osWindows: "$1.lib" else: "lib$1.a"
-
-type
-  CfileFlag* {.pure.} = enum
-    Cached,    ## no need to recompile this time
-    External   ## file was introduced via .compile pragma
-
-  Cfile* = object
-    cname*, obj*: string
-    flags*: set[CFileFlag]
-  CfileList = seq[Cfile]
-
-var
-  externalToLink: seq[string] = @[] # files to link in addition to the file
-                                    # we compiled
-  linkOptionsCmd: string = ""
-  compileOptionsCmd: seq[string] = @[]
-  linkOptions: string = ""
-  compileOptions: string = ""
-  ccompilerpath: string = ""
-  toCompile: CfileList = @[]
+proc libNameTmpl(conf: ConfigRef): string {.inline.} =
+  result = if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a"
 
 proc nameToCC*(name: string): TSystemCC =
   ## Returns the kind of compiler referred to by `name`, or ccNone
@@ -389,10 +355,10 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
     else:
       suffix
 
-  if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and
+  if (conf.target.hostOS != conf.target.targetOS or conf.target.hostCPU != conf.target.targetCPU) and
       optCompileOnly notin conf.globalOptions:
-    let fullCCname = platform.CPU[targetCPU].name & '.' &
-                     platform.OS[targetOS].name & '.' &
+    let fullCCname = platform.CPU[conf.target.targetCPU].name & '.' &
+                     platform.OS[conf.target.targetOS].name & '.' &
                      CC[c].name & fullSuffix
     result = getConfigVar(conf, fullCCname)
     if result.len == 0:
@@ -402,40 +368,40 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
     result = getConfigVar(conf, CC[c].name & fullSuffix)
 
 proc setCC*(conf: ConfigRef; ccname: string; info: TLineInfo) =
-  cCompiler = nameToCC(ccname)
-  if cCompiler == ccNone:
+  conf.cCompiler = nameToCC(ccname)
+  if conf.cCompiler == ccNone:
     localError(conf, info, "unknown C compiler: '$1'" % ccname)
-  compileOptions = getConfigVar(conf, cCompiler, ".options.always")
-  linkOptions = ""
-  ccompilerpath = getConfigVar(conf, cCompiler, ".path")
+  conf.compileOptions = getConfigVar(conf, conf.cCompiler, ".options.always")
+  conf.linkOptions = ""
+  conf.ccompilerpath = getConfigVar(conf, conf.cCompiler, ".path")
   for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name)
-  defineSymbol(conf.symbols, CC[cCompiler].name)
+  defineSymbol(conf.symbols, CC[conf.cCompiler].name)
 
 proc addOpt(dest: var string, src: string) =
   if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ")
   add(dest, src)
 
 proc addLinkOption*(conf: ConfigRef; option: string) =
-  addOpt(linkOptions, option)
+  addOpt(conf.linkOptions, option)
 
 proc addCompileOption*(conf: ConfigRef; option: string) =
-  if strutils.find(compileOptions, option, 0) < 0:
-    addOpt(compileOptions, option)
+  if strutils.find(conf.compileOptions, option, 0) < 0:
+    addOpt(conf.compileOptions, option)
 
 proc addLinkOptionCmd*(conf: ConfigRef; option: string) =
-  addOpt(linkOptionsCmd, option)
+  addOpt(conf.linkOptionsCmd, option)
 
 proc addCompileOptionCmd*(conf: ConfigRef; option: string) =
-  compileOptionsCmd.add(option)
+  conf.compileOptionsCmd.add(option)
 
 proc initVars*(conf: ConfigRef) =
   # we need to define the symbol here, because ``CC`` may have never been set!
   for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name)
-  defineSymbol(conf.symbols, CC[cCompiler].name)
-  addCompileOption(conf, getConfigVar(conf, cCompiler, ".options.always"))
+  defineSymbol(conf.symbols, CC[conf.cCompiler].name)
+  addCompileOption(conf, getConfigVar(conf, conf.cCompiler, ".options.always"))
   #addLinkOption(getConfigVar(cCompiler, ".options.linker"))
-  if len(ccompilerpath) == 0:
-    ccompilerpath = getConfigVar(conf, cCompiler, ".path")
+  if len(conf.ccompilerpath) == 0:
+    conf.ccompilerpath = getConfigVar(conf, conf.cCompiler, ".path")
 
 proc completeCFilePath*(conf: ConfigRef; cfile: string, createSubDir: bool = true): string =
   result = completeGeneratedFilePath(conf, cfile, createSubDir)
@@ -445,21 +411,21 @@ proc toObjFile*(conf: ConfigRef; filename: string): string =
   #if filename.endsWith(".cpp"):
   #  result = changeFileExt(filename, "cpp." & CC[cCompiler].objExt)
   #else:
-  result = changeFileExt(filename, CC[cCompiler].objExt)
+  result = changeFileExt(filename, CC[conf.cCompiler].objExt)
 
 proc addFileToCompile*(conf: ConfigRef; cf: Cfile) =
-  toCompile.add(cf)
+  conf.toCompile.add(cf)
 
 proc resetCompilationLists*(conf: ConfigRef) =
-  toCompile.setLen 0
+  conf.toCompile.setLen 0
   ## XXX: we must associate these with their originating module
   # when the module is loaded/unloaded it adds/removes its items
   # That's because we still need to hash check the external files
   # Maybe we can do that in checkDep on the other hand?
-  externalToLink.setLen 0
+  conf.externalToLink.setLen 0
 
 proc addExternalFileToLink*(conf: ConfigRef; filename: string) =
-  externalToLink.insert(filename, 0)
+  conf.externalToLink.insert(filename, 0)
 
 proc execWithEcho(conf: ConfigRef; cmd: string, msg = hintExecuting): int =
   rawMessage(conf, msg, cmd)
@@ -472,9 +438,12 @@ proc execExternalProgram*(conf: ConfigRef; cmd: string, msg = hintExecuting) =
 
 proc generateScript(conf: ConfigRef; projectFile: string, script: Rope) =
   let (dir, name, ext) = splitFile(projectFile)
-  writeRope(script, getNimcacheDir(conf) / addFileExt("compile_" & name,
-                                     platform.OS[targetOS].scriptExt))
-  copyFile(conf.libpath / "nimbase.h", getNimcacheDir(conf) / "nimbase.h")
+  let filename = getNimcacheDir(conf) / addFileExt("compile_" & name,
+                                     platform.OS[conf.target.targetOS].scriptExt)
+  if writeRope(script, filename):
+    copyFile(conf.libpath / "nimbase.h", getNimcacheDir(conf) / "nimbase.h")
+  else:
+    rawMessage(conf, errGenerated, "could not write to file: " & filename)
 
 proc getOptSpeed(conf: ConfigRef; c: TSystemCC): string =
   result = getConfigVar(conf, c, ".options.speed")
@@ -499,8 +468,8 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
   result = conf.globalOptions * {optGenScript, optGenMapping} != {}
 
 proc cFileSpecificOptions(conf: ConfigRef; cfilename: string): string =
-  result = compileOptions
-  for option in compileOptionsCmd:
+  result = conf.compileOptions
+  for option in conf.compileOptionsCmd:
     if strutils.find(result, option, 0) < 0:
       addOpt(result, option)
 
@@ -508,15 +477,15 @@ proc cFileSpecificOptions(conf: ConfigRef; cfilename: string): string =
   if optCDebug in conf.globalOptions:
     let key = trunk & ".debug"
     if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
-    else: addOpt(result, getDebug(conf, cCompiler))
+    else: addOpt(result, getDebug(conf, conf.cCompiler))
   if optOptimizeSpeed in conf.options:
     let key = trunk & ".speed"
     if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
-    else: addOpt(result, getOptSpeed(conf, cCompiler))
+    else: addOpt(result, getOptSpeed(conf, conf.cCompiler))
   elif optOptimizeSize in conf.options:
     let key = trunk & ".size"
     if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
-    else: addOpt(result, getOptSize(conf, cCompiler))
+    else: addOpt(result, getOptSize(conf, conf.cCompiler))
   let key = trunk & ".always"
   if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
 
@@ -524,15 +493,15 @@ proc getCompileOptions(conf: ConfigRef): string =
   result = cFileSpecificOptions(conf, "__dummy__")
 
 proc getLinkOptions(conf: ConfigRef): string =
-  result = linkOptions & " " & linkOptionsCmd & " "
-  for linkedLib in items(cLinkedLibs):
-    result.add(CC[cCompiler].linkLibCmd % linkedLib.quoteShell)
-  for libDir in items(cLibs):
-    result.add(join([CC[cCompiler].linkDirCmd, libDir.quoteShell]))
+  result = conf.linkOptions & " " & conf.linkOptionsCmd & " "
+  for linkedLib in items(conf.cLinkedLibs):
+    result.add(CC[conf.cCompiler].linkLibCmd % linkedLib.quoteShell)
+  for libDir in items(conf.cLibs):
+    result.add(join([CC[conf.cCompiler].linkDirCmd, libDir.quoteShell]))
 
 proc needsExeExt(conf: ConfigRef): bool {.inline.} =
-  result = (optGenScript in conf.globalOptions and targetOS == osWindows) or
-           (platform.hostOS == osWindows)
+  result = (optGenScript in conf.globalOptions and conf.target.targetOS == osWindows) or
+           (conf.target.hostOS == osWindows)
 
 proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string =
   result = if conf.cmd == cmdCompileToCpp and not cfile.endsWith(".c"):
@@ -546,18 +515,18 @@ proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string
 
 proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string =
   result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
-           elif gMixedMode and conf.cmd != cmdCompileToCpp: CC[compiler].cppCompiler
+           elif optMixedMode in conf.globalOptions and conf.cmd != cmdCompileToCpp: CC[compiler].cppCompiler
            else: getCompilerExe(conf, compiler, "")
 
 proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
-  var c = cCompiler
+  var c = conf.cCompiler
   var options = cFileSpecificOptions(conf, cfile.cname)
   var exe = getConfigVar(conf, c, ".exe")
   if exe.len == 0: exe = getCompilerExe(conf, c, cfile.cname)
 
   if needsExeExt(conf): exe = addFileExt(exe, "exe")
   if optGenDynLib in conf.globalOptions and
-      ospNeedsPIC in platform.OS[targetOS].props:
+      ospNeedsPIC in platform.OS[conf.target.targetOS].props:
     add(options, ' ' & CC[c].pic)
 
   var includeCmd, compilePattern: string
@@ -565,10 +534,10 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
     # compute include paths:
     includeCmd = CC[c].includeCmd & quoteShell(conf.libpath)
 
-    for includeDir in items(cIncludes):
+    for includeDir in items(conf.cIncludes):
       includeCmd.add(join([CC[c].includeCmd, includeDir.quoteShell]))
 
-    compilePattern = joinPath(ccompilerpath, exe)
+    compilePattern = joinPath(conf.ccompilerpath, exe)
   else:
     includeCmd = ""
     compilePattern = getCompilerExe(conf, c, cfile.cname)
@@ -604,9 +573,9 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
 proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash =
   result = secureHash(
     $secureHashFile(cfile.cname) &
-    platform.OS[targetOS].name &
-    platform.CPU[targetCPU].name &
-    extccomp.CC[extccomp.cCompiler].name &
+    platform.OS[conf.target.targetOS].name &
+    platform.CPU[conf.target.targetCPU].name &
+    extccomp.CC[conf.cCompiler].name &
     getCompileCFileCmd(conf, cfile))
 
 proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
@@ -630,11 +599,11 @@ proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
 proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) =
   if optForceFullMake notin conf.globalOptions and not externalFileChanged(conf, c):
     c.flags.incl CfileFlag.Cached
-  toCompile.add(c)
+  conf.toCompile.add(c)
 
 proc addExternalFileToCompile*(conf: ConfigRef; filename: string) =
   var c = Cfile(cname: filename,
-    obj: toObjFile(conf, completeCFilePath(conf, changeFileExt(filename, ""), false)),
+    obj: toObjFile(conf, completeCFilePath(conf, filename, false)),
     flags: {CfileFlag.External})
   addExternalFileToCompile(conf, c)
 
@@ -650,7 +619,7 @@ proc compileCFile(conf: ConfigRef; list: CFileList, script: var Rope, cmds: var
       add(prettyCmds, "CC: " & name)
     if optGenScript in conf.globalOptions:
       add(script, compileCmd)
-      add(script, tnl)
+      add(script, "\n")
 
 proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
   if optGenStaticLib in conf.globalOptions:
@@ -660,24 +629,24 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
       if not libname.isAbsolute():
         libname = getCurrentDir() / libname
     else:
-      libname = (libNameTmpl() % splitFile(conf.projectName).name)
-    result = CC[cCompiler].buildLib % ["libfile", libname,
+      libname = (libNameTmpl(conf) % splitFile(conf.projectName).name)
+    result = CC[conf.cCompiler].buildLib % ["libfile", libname,
                                        "objfiles", objfiles]
   else:
-    var linkerExe = getConfigVar(conf, cCompiler, ".linkerexe")
-    if len(linkerExe) == 0: linkerExe = getLinkerExe(conf, cCompiler)
+    var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe")
+    if len(linkerExe) == 0: linkerExe = getLinkerExe(conf, conf.cCompiler)
     # bug #6452: We must not use ``quoteShell`` here for ``linkerExe``
     if needsExeExt(conf): linkerExe = addFileExt(linkerExe, "exe")
     if noAbsolutePaths(conf): result = linkerExe
-    else: result = joinPath(ccompilerpath, linkerExe)
-    let buildgui = if optGenGuiApp in conf.globalOptions: CC[cCompiler].buildGui
+    else: result = joinPath(conf.cCompilerpath, linkerExe)
+    let buildgui = if optGenGuiApp in conf.globalOptions: CC[conf.cCompiler].buildGui
                    else: ""
     var exefile, builddll: string
     if optGenDynLib in conf.globalOptions:
-      exefile = platform.OS[targetOS].dllFrmt % splitFile(projectfile).name
-      builddll = CC[cCompiler].buildDll
+      exefile = platform.OS[conf.target.targetOS].dllFrmt % splitFile(projectfile).name
+      builddll = CC[conf.cCompiler].buildDll
     else:
-      exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt
+      exefile = splitFile(projectfile).name & platform.OS[conf.target.targetOS].exeExt
       builddll = ""
     if conf.outFile.len > 0:
       exefile = conf.outFile.expandTilde
@@ -691,10 +660,10 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
         writeDebugInfo(exefile.changeFileExt("ndb"))
     exefile = quoteShell(exefile)
     let linkOptions = getLinkOptions(conf) & " " &
-                      getConfigVar(conf, cCompiler, ".options.linker")
-    var linkTmpl = getConfigVar(conf, cCompiler, ".linkTmpl")
+                      getConfigVar(conf, conf.cCompiler, ".options.linker")
+    var linkTmpl = getConfigVar(conf, conf.cCompiler, ".linkTmpl")
     if linkTmpl.len == 0:
-      linkTmpl = CC[cCompiler].linkTmpl
+      linkTmpl = CC[conf.cCompiler].linkTmpl
     result = quoteShell(result % ["builddll", builddll,
         "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
         "exefile", exefile, "nim", getPrefixDir(conf), "lib", conf.libpath])
@@ -765,19 +734,20 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) =
   var cmds: TStringSeq = @[]
   var prettyCmds: TStringSeq = @[]
   let prettyCb = proc (idx: int) =
-    echo prettyCmds[idx]
-  compileCFile(conf, toCompile, script, cmds, prettyCmds)
+    when declared(echo):
+      echo prettyCmds[idx]
+  compileCFile(conf, conf.toCompile, script, cmds, prettyCmds)
   if optCompileOnly notin conf.globalOptions:
     execCmdsInParallel(conf, cmds, prettyCb)
   if optNoLinking notin conf.globalOptions:
     # call the linker:
     var objfiles = ""
-    for it in externalToLink:
+    for it in conf.externalToLink:
       let objFile = if noAbsolutePaths(conf): it.extractFilename else: it
       add(objfiles, ' ')
       add(objfiles, quoteShell(
-          addFileExt(objFile, CC[cCompiler].objExt)))
-    for x in toCompile:
+          addFileExt(objFile, CC[conf.cCompiler].objExt)))
+    for x in conf.toCompile:
       let objFile = if noAbsolutePaths(conf): x.obj.extractFilename else: x.obj
       add(objfiles, ' ')
       add(objfiles, quoteShell(objFile))
@@ -789,7 +759,7 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) =
     linkCmd = ""
   if optGenScript in conf.globalOptions:
     add(script, linkCmd)
-    add(script, tnl)
+    add(script, "\n")
     generateScript(conf, projectfile, script)
 
 #from json import escapeJson
@@ -824,7 +794,7 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
     for it in llist:
       let objfile = if noAbsolutePaths(conf): it.extractFilename
                     else: it
-      let objstr = addFileExt(objfile, CC[cCompiler].objExt)
+      let objstr = addFileExt(objfile, CC[conf.cCompiler].objExt)
       add(objfiles, ' ')
       add(objfiles, objstr)
       if pastStart: lit ",\L"
@@ -848,11 +818,11 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
   var f: File
   if open(f, jsonFile, fmWrite):
     lit "{\"compile\":[\L"
-    cfiles(conf, f, buf, toCompile, false)
+    cfiles(conf, f, buf, conf.toCompile, false)
     lit "],\L\"link\":[\L"
     var objfiles = ""
     # XXX add every file here that is to link
-    linkfiles(conf, f, buf, objfiles, toCompile, externalToLink)
+    linkfiles(conf, f, buf, objfiles, conf.toCompile, conf.externalToLink)
 
     lit "],\L\"linkcmd\": "
     str getLinkCmd(conf, projectfile, objfiles)
@@ -877,14 +847,16 @@ proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
       add(prettyCmds, "CC: " & name)
 
     let prettyCb = proc (idx: int) =
-      echo prettyCmds[idx]
+      when declared(echo):
+        echo prettyCmds[idx]
     execCmdsInParallel(conf, cmds, prettyCb)
 
     let linkCmd = data["linkcmd"]
     doAssert linkCmd.kind == JString
     execLinkCmd(conf, linkCmd.getStr)
   except:
-    echo getCurrentException().getStackTrace()
+    when declared(echo):
+      echo getCurrentException().getStackTrace()
     quit "error evaluating JSON file: " & jsonFile
 
 proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope =
@@ -894,16 +866,18 @@ proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope =
 proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) =
   if optGenMapping notin conf.globalOptions: return
   var code = rope("[C_Files]\n")
-  add(code, genMappingFiles(conf, toCompile))
+  add(code, genMappingFiles(conf, conf.toCompile))
   add(code, "\n[C_Compiler]\nFlags=")
   add(code, strutils.escape(getCompileOptions(conf)))
 
   add(code, "\n[Linker]\nFlags=")
   add(code, strutils.escape(getLinkOptions(conf) & " " &
-                            getConfigVar(conf, cCompiler, ".options.linker")))
+                            getConfigVar(conf, conf.cCompiler, ".options.linker")))
 
   add(code, "\n[Environment]\nlibpath=")
   add(code, strutils.escape(conf.libpath))
 
   addf(code, "\n[Symbols]$n$1", [symbolMapping])
-  writeRope(code, joinPath(conf.projectPath, "mapping.txt"))
+  let filename = joinPath(conf.projectPath, "mapping.txt")
+  if not writeRope(code, filename):
+    rawMessage(conf, errGenerated, "could not write to file: " & filename)
diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim
index 6c16a0b4e..09455ced7 100644
--- a/compiler/filter_tmpl.nim
+++ b/compiler/filter_tmpl.nim
@@ -11,7 +11,7 @@
 
 import
   llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
-  renderer, filters
+  renderer, filters, lineinfos
 
 type
   TParseState = enum
diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim
index 44c7651bc..44ad46136 100644
--- a/compiler/gorgeimpl.nim
+++ b/compiler/gorgeimpl.nim
@@ -9,7 +9,8 @@
 
 ## Module that implements ``gorge`` for the compiler.
 
-import msgs, std / sha1, os, osproc, streams, strutils, options
+import msgs, std / sha1, os, osproc, streams, strutils, options,
+  lineinfos
 
 proc readOutput(p: Process): (string, int) =
   result[0] = ""
@@ -22,7 +23,7 @@ proc readOutput(p: Process): (string, int) =
   result[1] = p.waitForExit
 
 proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) =
-  let workingDir = parentDir(info.toFullPath)
+  let workingDir = parentDir(toFullPath(conf, info))
   if cache.len > 0:# and optForceFullMake notin gGlobalOptions:
     let h = secureHash(cmd & "\t" & input & "\t" & cache)
     let filename = options.toGeneratedFile(conf, "gorge_" & $h, "txt")
diff --git a/compiler/guards.nim b/compiler/guards.nim
index d39ea799b..99bb51fce 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -10,7 +10,7 @@
 ## This module implements the 'implies' relation for guards.
 
 import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
-  saturate, modulegraphs, options, configuration
+  saturate, modulegraphs, options, lineinfos
 
 const
   someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
@@ -131,8 +131,8 @@ proc neg(n: PNode; o: Operators): PNode =
         let eAsNode = newIntNode(nkIntLit, e.sym.position)
         if not inSet(n.sons[1], eAsNode): s.add eAsNode
       result.sons[1] = s
-    elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000:
-      result.sons[1] = complement(n.sons[1])
+    #elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000:
+    #  result.sons[1] = complement(n.sons[1])
     else:
       # not ({2, 3, 4}.contains(x))   x != 2 and x != 3 and x != 4
       # XXX todo
@@ -208,14 +208,14 @@ proc zero(): PNode = nkIntLit.newIntNode(0)
 proc one(): PNode = nkIntLit.newIntNode(1)
 proc minusOne(): PNode = nkIntLit.newIntNode(-1)
 
-proc lowBound*(x: PNode): PNode =
-  result = nkIntLit.newIntNode(firstOrd(x.typ))
+proc lowBound*(conf: ConfigRef; x: PNode): PNode =
+  result = nkIntLit.newIntNode(firstOrd(conf, x.typ))
   result.info = x.info
 
-proc highBound*(x: PNode; o: Operators): PNode =
+proc highBound*(conf: ConfigRef; x: PNode; o: Operators): PNode =
   let typ = x.typ.skipTypes(abstractInst)
   result = if typ.kind == tyArray:
-             nkIntLit.newIntNode(lastOrd(typ))
+             nkIntLit.newIntNode(lastOrd(conf, typ))
            elif typ.kind == tySequence and x.kind == nkSym and
                x.sym.kind == skConst:
              nkIntLit.newIntNode(x.sym.ast.len-1)
@@ -503,7 +503,7 @@ proc leImpliesIn(x, c, aSet: PNode): TImplication =
     # fact:  x <= 4;  question x in {56}?
     # --> true if every value <= 4 is in the set {56}
     #
-    var value = newIntNode(c.kind, firstOrd(x.typ))
+    var value = newIntNode(c.kind, firstOrd(nil, x.typ))
     # don't iterate too often:
     if c.intVal - value.intVal < 1000:
       var i, pos, neg: int
@@ -520,7 +520,7 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication =
     # --> true iff every value >= 4 is in the set {56}
     #
     var value = newIntNode(c.kind, c.intVal)
-    let max = lastOrd(x.typ)
+    let max = lastOrd(nil, x.typ)
     # don't iterate too often:
     if max - value.intVal < 1000:
       var i, pos, neg: int
@@ -532,8 +532,8 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication =
       elif neg == i: result = impNo
 
 proc compareSets(a, b: PNode): TImplication =
-  if equalSets(a, b): result = impYes
-  elif intersectSets(a, b).len == 0: result = impNo
+  if equalSets(nil, a, b): result = impYes
+  elif intersectSets(nil, a, b).len == 0: result = impNo
 
 proc impliesIn(fact, loc, aSet: PNode): TImplication =
   case fact.sons[0].sym.magic
@@ -799,10 +799,10 @@ proc ple(m: TModel; a, b: PNode): TImplication =
 
   # use type information too:  x <= 4  iff  high(x) <= 4
   if b.isValue and a.typ != nil and a.typ.isOrdinalType:
-    if lastOrd(a.typ) <= b.intVal: return impYes
+    if lastOrd(nil, a.typ) <= b.intVal: return impYes
   # 3 <= x   iff  low(x) <= 3
   if a.isValue and b.typ != nil and b.typ.isOrdinalType:
-    if firstOrd(b.typ) <= a.intVal: return impYes
+    if firstOrd(nil, b.typ) <= a.intVal: return impYes
 
   # x <= x
   if sameTree(a, b): return impYes
diff --git a/compiler/hlo.nim b/compiler/hlo.nim
index 8251e3179..bbbcb4e56 100644
--- a/compiler/hlo.nim
+++ b/compiler/hlo.nim
@@ -43,8 +43,8 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
       if not isNil(x):
         assert x.kind in {nkStmtList, nkCall}
         # better be safe than sorry, so check evalTemplateCounter too:
-        inc(evalTemplateCounter)
-        if evalTemplateCounter > evalTemplateLimit:
+        inc(c.config.evalTemplateCounter)
+        if c.config.evalTemplateCounter > evalTemplateLimit:
           globalError(c.config, n.info, "template instantiation too nested")
         # deactivate this pattern:
         c.patterns[i] = nil
@@ -54,7 +54,7 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
           result = flattenStmts(x)
         else:
           result = evalPattern(c, x, result)
-        dec(evalTemplateCounter)
+        dec(c.config.evalTemplateCounter)
         # activate this pattern again:
         c.patterns[i] = pattern
 
diff --git a/compiler/idents.nim b/compiler/idents.nim
index 34cf5bf1e..0a2f2d5cf 100644
--- a/compiler/idents.nim
+++ b/compiler/idents.nim
@@ -30,12 +30,14 @@ type
     wordCounter: int
     idAnon*, idDelegator*, emptyIdent*: PIdent
 
-var
-  legacy: IdentCache
+when false:
+  var
+    legacy: IdentCache
 
 proc resetIdentCache*() =
-  for i in low(legacy.buckets)..high(legacy.buckets):
-    legacy.buckets[i] = nil
+  when false:
+    for i in low(legacy.buckets)..high(legacy.buckets):
+      legacy.buckets[i] = nil
 
 proc cmpIgnoreStyle*(a, b: cstring, blen: int): int =
   if a[0] != b[0]: return 1
@@ -111,25 +113,15 @@ proc getIdent*(self: IdentCache; identifier: string, h: Hash): PIdent =
   result = getIdent(cstring(identifier), len(identifier), h)
 
 proc newIdentCache*(): IdentCache =
-  if legacy.isNil:
-    result = IdentCache()
-    result.idAnon = result.getIdent":anonymous"
-    result.wordCounter = 1
-    result.idDelegator = result.getIdent":delegator"
-    result.emptyIdent = result.getIdent("")
-    # initialize the keywords:
-    for s in countup(succ(low(specialWords)), high(specialWords)):
-      result.getIdent(specialWords[s], hashIgnoreStyle(specialWords[s])).id = ord(s)
-    legacy = result
-  else:
-    result = legacy
+  result = IdentCache()
+  result.idAnon = result.getIdent":anonymous"
+  result.wordCounter = 1
+  result.idDelegator = result.getIdent":delegator"
+  result.emptyIdent = result.getIdent("")
+  # initialize the keywords:
+  for s in countup(succ(low(specialWords)), high(specialWords)):
+    result.getIdent(specialWords[s], hashIgnoreStyle(specialWords[s])).id = ord(s)
 
 proc whichKeyword*(id: PIdent): TSpecialWord =
   if id.id < 0: result = wInvalid
   else: result = TSpecialWord(id.id)
-
-proc getIdent*(identifier: string): PIdent =
-  ## for backwards compatibility.
-  if legacy.isNil:
-    discard newIdentCache()
-  legacy.getIdent identifier
diff --git a/compiler/importer.nim b/compiler/importer.nim
index 90e774a50..c013b93ab 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -10,8 +10,8 @@
 # This module implements the symbol importing mechanism.
 
 import
-  intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups,
-  semdata, passes, renderer, modulepaths, sigmatch, configuration
+  intsets, strutils, os, ast, astalgo, msgs, options, idents, lookups,
+  semdata, passes, renderer, modulepaths, sigmatch, lineinfos
 
 proc evalImport*(c: PContext, n: PNode): PNode
 proc evalFrom*(c: PContext, n: PNode): PNode
@@ -20,7 +20,7 @@ proc readExceptSet*(c: PContext, n: PNode): IntSet =
   assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
   result = initIntSet()
   for i in 1 ..< n.len:
-    let ident = lookups.considerQuotedIdent(c.config, n[i])
+    let ident = lookups.considerQuotedIdent(c, n[i])
     result.incl(ident.id)
 
 proc importPureEnumField*(c: PContext; s: PSym) =
@@ -69,12 +69,13 @@ proc rawImportSymbol(c: PContext, s: PSym) =
     if hasPattern(s): addPattern(c, s)
 
 proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
-  let ident = lookups.considerQuotedIdent(c.config, n)
+  let ident = lookups.considerQuotedIdent(c, n)
   let s = strTableGet(fromMod.tab, ident)
   if s == nil:
     errorUndeclaredIdentifier(c, n.info, ident.s)
   else:
-    if s.kind == skStub: loadStub(s)
+    when false:
+      if s.kind == skStub: loadStub(s)
     if s.kind notin ExportableSymKinds:
       internalError(c.config, n.info, "importSymbol: 2")
     # for an enumeration we have to add all identifiers
@@ -132,7 +133,7 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym =
     result = createModuleAlias(realModule, n.sons[1].ident, realModule.info,
                                c.config.options)
 
-proc myImportModule(c: PContext, n: PNode): PSym =
+proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
   var f = checkModuleName(c.config, n)
   if f != InvalidFileIDX:
     let L = c.graph.importStack.len
@@ -143,10 +144,10 @@ proc myImportModule(c: PContext, n: PNode): PSym =
       var err = ""
       for i in countup(recursion, L-1):
         if i > recursion: err.add "\n"
-        err.add toFullPath(c.graph.importStack[i]) & " imports " &
-                toFullPath(c.graph.importStack[i+1])
+        err.add toFullPath(c.config, c.graph.importStack[i]) & " imports " &
+                toFullPath(c.config, c.graph.importStack[i+1])
       c.recursiveDep = err
-    result = importModuleAs(c, n, gImportModule(c.graph, c.module, f, c.cache))
+    result = importModuleAs(c, n, c.graph.importModuleCallback(c.graph, c.module, f))
     #echo "set back to ", L
     c.graph.importStack.setLen(L)
     # we cannot perform this check reliably because of
@@ -161,9 +162,10 @@ proc myImportModule(c: PContext, n: PNode): PSym =
       else:
         message(c.config, n.info, warnDeprecated, result.name.s)
     suggestSym(c.config, n.info, result, c.graph.usageSym, false)
+    importStmtResult.add newStrNode(toFullPath(c.config, f), n.info)
 
-proc impMod(c: PContext; it: PNode) =
-  let m = myImportModule(c, it)
+proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
+  let m = myImportModule(c, it, importStmtResult)
   if m != nil:
     var emptySet: IntSet
     # ``addDecl`` needs to be done before ``importAllSymbols``!
@@ -172,7 +174,8 @@ proc impMod(c: PContext; it: PNode) =
     #importForwarded(c, m.ast, emptySet)
 
 proc evalImport(c: PContext, n: PNode): PNode =
-  result = n
+  #result = n
+  result = newNodeI(nkImportStmt, n.info)
   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:
@@ -184,14 +187,14 @@ proc evalImport(c: PContext, n: PNode): PNode =
       a.add sep # dummy entry, replaced in the loop
       for x in it[2]:
         a.sons[2] = x
-        impMod(c, a)
+        impMod(c, a, result)
     else:
-      impMod(c, it)
+      impMod(c, it, result)
 
 proc evalFrom(c: PContext, n: PNode): PNode =
-  result = n
+  result = newNodeI(nkImportStmt, n.info)
   checkMinSonsLen(n, 2, c.config)
-  var m = myImportModule(c, n.sons[0])
+  var m = myImportModule(c, n.sons[0], result)
   if m != nil:
     n.sons[0] = newSymNode(m)
     addDecl(c, m, n.info)               # add symbol to symbol table of module
@@ -200,9 +203,9 @@ proc evalFrom(c: PContext, n: PNode): PNode =
         importSymbol(c, n.sons[i], m)
 
 proc evalImportExcept*(c: PContext, n: PNode): PNode =
-  result = n
+  result = newNodeI(nkImportStmt, n.info)
   checkMinSonsLen(n, 2, c.config)
-  var m = myImportModule(c, n.sons[0])
+  var m = myImportModule(c, n.sons[0], result)
   if m != nil:
     n.sons[0] = newSymNode(m)
     addDecl(c, m, n.info)               # add symbol to symbol table of module
diff --git a/compiler/incremental.nim b/compiler/incremental.nim
new file mode 100644
index 000000000..2008d35de
--- /dev/null
+++ b/compiler/incremental.nim
@@ -0,0 +1,196 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Basic type definitions the module graph needs in order to support
+## incremental compilations.
+
+const nimIncremental* = defined(nimIncremental)
+
+import options, lineinfos
+
+when nimIncremental:
+  import ast, msgs, intsets, btrees, db_sqlite, std / sha1
+  from strutils import parseInt
+
+  type
+    Writer* = object
+      sstack*: seq[PSym]          # a stack of symbols to process
+      tstack*: seq[PType]         # a stack of types to process
+      tmarks*, smarks*: IntSet
+      forwardedSyms*: seq[PSym]
+
+    Reader* = object
+      syms*: BTree[int, PSym]
+      types*: BTree[int, PType]
+
+    IncrementalCtx* = object
+      db*: DbConn
+      w*: Writer
+      r*: Reader
+      configChanged*: bool
+
+  proc init*(incr: var IncrementalCtx) =
+    incr.w.sstack = @[]
+    incr.w.tstack = @[]
+    incr.w.tmarks = initIntSet()
+    incr.w.smarks = initIntSet()
+    incr.w.forwardedSyms = @[]
+    incr.r.syms = initBTree[int, PSym]()
+    incr.r.types = initBTree[int, PType]()
+
+
+  proc hashFileCached*(conf: ConfigRef; fileIdx: FileIndex; fullpath: string): string =
+    result = msgs.getHash(conf, fileIdx)
+    if result.len == 0:
+      result = $secureHashFile(fullpath)
+      msgs.setHash(conf, fileIdx, result)
+
+  proc toDbFileId*(incr: var IncrementalCtx; conf: ConfigRef; fileIdx: FileIndex): int =
+    if fileIdx == FileIndex(-1): return -1
+    let fullpath = toFullPath(conf, fileIdx)
+    let row = incr.db.getRow(sql"select id, fullhash from filenames where fullpath = ?",
+      fullpath)
+    let id = row[0]
+    let fullhash = hashFileCached(conf, fileIdx, fullpath)
+    if id.len == 0:
+      result = int incr.db.insertID(sql"insert into filenames(fullpath, fullhash) values (?, ?)",
+        fullpath, fullhash)
+    else:
+      if row[1] != fullhash:
+        incr.db.exec(sql"update filenames set fullhash = ? where fullpath = ?", fullhash, fullpath)
+      result = parseInt(id)
+
+  proc fromDbFileId*(incr: var IncrementalCtx; conf: ConfigRef; dbId: int): FileIndex =
+    if dbId == -1: return FileIndex(-1)
+    let fullpath = incr.db.getValue(sql"select fullpath from filenames where id = ?", dbId)
+    doAssert fullpath.len > 0, "cannot find file name for DB ID " & $dbId
+    result = fileInfoIdx(conf, fullpath)
+
+
+  proc addModuleDep*(incr: var IncrementalCtx; conf: ConfigRef;
+                     module, fileIdx: FileIndex;
+                     isIncludeFile: bool) =
+    if conf.symbolFiles != v2Sf: return
+
+    let a = toDbFileId(incr, conf, module)
+    let b = toDbFileId(incr, conf, fileIdx)
+
+    incr.db.exec(sql"insert into deps(module, dependency, isIncludeFile) values (?, ?, ?)",
+      a, b, ord(isIncludeFile))
+
+  # --------------- Database model ---------------------------------------------
+
+  proc createDb*(db: DbConn) =
+    db.exec(sql"""
+      create table if not exists controlblock(
+        idgen integer not null
+      );
+    """)
+
+    db.exec(sql"""
+      create table if not exists config(
+        config varchar(8000) not null
+      );
+    """)
+
+    db.exec(sql"""
+      create table if not exists filenames(
+        id integer primary key,
+        fullpath varchar(8000) not null,
+        fullHash varchar(256) not null
+      );
+    """)
+    db.exec sql"create index if not exists FilenameIx on filenames(fullpath);"
+
+    db.exec(sql"""
+      create table if not exists modules(
+        id integer primary key,
+        nimid integer not null,
+        fullpath varchar(8000) not null,
+        interfHash varchar(256) not null,
+        fullHash varchar(256) not null,
+
+        created timestamp not null default (DATETIME('now'))
+      );""")
+    db.exec(sql"""create unique index if not exists SymNameIx on modules(fullpath);""")
+
+    db.exec(sql"""
+      create table if not exists deps(
+        id integer primary key,
+        module integer not null,
+        dependency integer not null,
+        isIncludeFile integer not null,
+        foreign key (module) references filenames(id),
+        foreign key (dependency) references filenames(id)
+      );""")
+    db.exec(sql"""create index if not exists DepsIx on deps(module);""")
+
+    db.exec(sql"""
+      create table if not exists types(
+        id integer primary key,
+        nimid integer not null,
+        module integer not null,
+        data blob not null,
+        foreign key (module) references module(id)
+      );
+    """)
+    db.exec sql"create index TypeByModuleIdx on types(module);"
+    db.exec sql"create index TypeByNimIdIdx on types(nimid);"
+
+    db.exec(sql"""
+      create table if not exists syms(
+        id integer primary key,
+        nimid integer not null,
+        module integer not null,
+        name varchar(256) not null,
+        data blob not null,
+        exported int not null,
+        foreign key (module) references module(id)
+      );
+    """)
+    db.exec sql"create index if not exists SymNameIx on syms(name);"
+    db.exec sql"create index SymByNameAndModuleIdx on syms(name, module);"
+    db.exec sql"create index SymByModuleIdx on syms(module);"
+    db.exec sql"create index SymByNimIdIdx on syms(nimid);"
+
+
+    db.exec(sql"""
+      create table if not exists toplevelstmts(
+        id integer primary key,
+        position integer not null,
+        module integer not null,
+        data blob not null,
+        foreign key (module) references module(id)
+      );
+    """)
+    db.exec sql"create index TopLevelStmtByModuleIdx on toplevelstmts(module);"
+    db.exec sql"create index TopLevelStmtByPositionIdx on toplevelstmts(position);"
+
+    db.exec(sql"""
+      create table if not exists statics(
+        id integer primary key,
+        module integer not null,
+        data blob not null,
+        foreign key (module) references module(id)
+      );
+    """)
+    db.exec sql"create index StaticsByModuleIdx on toplevelstmts(module);"
+    db.exec sql"insert into controlblock(idgen) values (0)"
+
+
+else:
+  type
+    IncrementalCtx* = object
+
+  template init*(incr: IncrementalCtx) = discard
+
+  template addModuleDep*(incr: var IncrementalCtx; conf: ConfigRef;
+                     module, fileIdx: FileIndex;
+                     isIncludeFile: bool) =
+    discard
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 25b554f7b..ef54841ae 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -31,8 +31,8 @@ implements the required case distinction.
 import
   ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
   nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables,
-  times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils,
-  intsets, cgmeth, lowerings, sighashes, configuration
+  times, ropes, math, passes, ccgutils, wordrecg, renderer,
+  intsets, cgmeth, lowerings, sighashes, lineinfos, rodutils
 
 from modulegraphs import ModuleGraph
 
@@ -72,7 +72,7 @@ type
                              # has been used (i.e. the label should be emitted)
     isLoop: bool             # whether it's a 'block' or 'while'
 
-  TGlobals = object
+  PGlobals = ref object of RootObj
     typeInfo, constants, code: Rope
     forwarded: seq[PSym]
     generatedSyms: IntSet
@@ -80,7 +80,6 @@ type
     classes: seq[(PType, Rope)]
     unique: int    # for temp identifier generation
 
-  PGlobals = ref TGlobals
   PProc = ref TProc
   TProc = object
     procDef: PNode
@@ -537,7 +536,7 @@ proc genLineDir(p: PProc, n: PNode) =
   let line = toLinenumber(n.info)
   if optLineDir in p.options:
     lineF(p, "// line $2 \"$1\"$n",
-         [rope(toFilename(n.info)), rope(line)])
+         [rope(toFilename(p.config, n.info)), rope(line)])
   if {optStackTrace, optEndb} * p.options == {optStackTrace, optEndb} and
       ((p.prc == nil) or sfPure notin p.prc.flags):
     useMagic(p, "endb")
@@ -606,11 +605,11 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
   var length = sonsLen(n)
   var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch
   if catchBranchesExist:
-    add(p.body, "++excHandler;" & tnl)
+    add(p.body, "++excHandler;\L")
   var tmpFramePtr = rope"F"
   if optStackTrace notin p.options:
     tmpFramePtr = p.getTemp(true)
-    line(p, tmpFramePtr & " = framePtr;" & tnl)
+    line(p, tmpFramePtr & " = framePtr;\L")
   lineF(p, "try {$n", [])
   var a: TCompRes
   gen(p, n.sons[0], a)
@@ -648,15 +647,15 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
   if catchBranchesExist:
     if not generalCatchBranchExists:
       useMagic(p, "reraiseException")
-      line(p, "else {" & tnl)
-      line(p, "\treraiseException();" & tnl)
-      line(p, "}" & tnl)
+      line(p, "else {\L")
+      line(p, "\treraiseException();\L")
+      line(p, "}\L")
     addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar])
-  line(p, "} finally {" & tnl)
+  line(p, "} finally {\L")
   line(p, "framePtr = $1;$n" % [tmpFramePtr])
   if i < length and n.sons[i].kind == nkFinally:
     genStmt(p, n.sons[i].sons[0])
-  line(p, "}" & tnl)
+  line(p, "}\L")
 
 proc genRaiseStmt(p: PProc, n: PNode) =
   genLineDir(p, n)
@@ -669,7 +668,7 @@ proc genRaiseStmt(p: PProc, n: PNode) =
              [a.rdLoc, makeJSString(typ.sym.name.s)])
   else:
     useMagic(p, "reraiseException")
-    line(p, "reraiseException();" & tnl)
+    line(p, "reraiseException();\L")
 
 proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
   var
@@ -775,7 +774,7 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) =
       var r: TCompRes
       gen(p, it, r)
       p.body.add(r.rdLoc)
-  p.body.add tnl
+  p.body.add "\L"
 
 proc genIf(p: PProc, n: PNode, r: var TCompRes) =
   var cond, stmt: TCompRes
@@ -798,7 +797,7 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) =
       p.nested: gen(p, it.sons[0], stmt)
     moveInto(p, stmt, r)
     lineF(p, "}$n", [])
-  line(p, repeat('}', toClose) & tnl)
+  line(p, repeat('}', toClose) & "\L")
 
 proc generateHeader(p: PProc, typ: PType): Rope =
   result = nil
@@ -969,7 +968,7 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
   internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex
   r.address = a.res
   var typ = skipTypes(m.sons[0].typ, abstractPtrs)
-  if typ.kind == tyArray: first = firstOrd(typ.sons[0])
+  if typ.kind == tyArray: first = firstOrd(p.config, typ.sons[0])
   else: first = 0
   if optBoundsCheck in p.options:
     useMagic(p, "chckIndx")
@@ -1366,7 +1365,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
   of tyBool:
     result = putToSeq("false", indirect)
   of tyArray:
-    let length = int(lengthOrd(t))
+    let length = int(lengthOrd(p.config, t))
     let e = elemType(t)
     let jsTyp = arrayTypeForElemType(e)
     if not jsTyp.isNil:
@@ -1680,7 +1679,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mIsNil: unaryExpr(p, n, r, "", "($1 === null)")
   of mEnumToStr: genRepr(p, n, r)
   of mNew, mNewFinalize: genNew(p, n)
-  of mSizeOf: r.res = rope(getSize(n.sons[1].typ))
+  of mSizeOf: r.res = rope(getSize(p.config, n.sons[1].typ))
   of mChr, mArrToSeq: gen(p, n.sons[1], r)      # nothing to do
   of mOrd: genOrd(p, n, r)
   of mLengthStr:
@@ -1901,13 +1900,13 @@ proc frameCreate(p: PProc; procname, filename: Rope): Rope =
   result.add p.indentLine(ropes.`%`("framePtr = F;$n", []))
 
 proc frameDestroy(p: PProc): Rope =
-  result = p.indentLine rope(("framePtr = F.prev;") & tnl)
+  result = p.indentLine rope(("framePtr = F.prev;") & "\L")
 
 proc genProcBody(p: PProc, prc: PSym): Rope =
   if hasFrameInfo(p):
     result = frameCreate(p,
               makeJSString(prc.owner.name.s & '.' & prc.name.s),
-              makeJSString(toFilename(prc.info)))
+              makeJSString(toFilename(p.config, prc.info)))
   else:
     result = nil
   if p.beforeRetNeeded:
@@ -1926,7 +1925,7 @@ proc optionaLine(p: Rope): Rope =
   if p == nil:
     return nil
   else:
-    return p & tnl
+    return p & "\L"
 
 proc genProc(oldProc: PProc, prc: PSym): Rope =
   var
@@ -1968,7 +1967,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
               optionaLine(genProcBody(p, prc)),
               optionaLine(p.indentLine(returnStmt))]
   else:
-    result = ~tnl
+    result = ~"\L"
 
     if optHotCodeReloading in p.config.options:
       # Here, we introduce thunks that create the equivalent of a jump table
@@ -2156,7 +2155,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
   of nkRaiseStmt: genRaiseStmt(p, n)
   of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt,
      nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
-     nkFromStmt, nkTemplateDef, nkMacroDef: discard
+     nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt: discard
   of nkPragma: genPragma(p, n)
   of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
     var s = n.sons[namePos].sym
@@ -2170,14 +2169,14 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
     discard "XXX to implement for better stack traces"
   else: internalError(p.config, n.info, "gen: unknown node type: " & $n.kind)
 
-var globals: PGlobals # XXX global variable here
-
-proc newModule(module: PSym): BModule =
+proc newModule(g: ModuleGraph; module: PSym): BModule =
   new(result)
   result.module = module
   result.sigConflicts = initCountTable[SigHash]()
-  if globals == nil:
-    globals = newGlobals()
+  if g.backend == nil:
+    g.backend = newGlobals()
+  result.graph = g
+  result.config = g.config
 
 proc genHeader(): Rope =
   result = (
@@ -2200,7 +2199,7 @@ proc genModule(p: PProc, n: PNode) =
   if optStackTrace in p.options:
     add(p.body, frameCreate(p,
         makeJSString("module " & p.module.module.name.s),
-        makeJSString(toFilename(p.module.module.info))))
+        makeJSString(toFilename(p.config, p.module.module.info))))
   genStmt(p, n)
   if optStackTrace in p.options:
     add(p.body, frameDestroy(p))
@@ -2210,6 +2209,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
   let m = BModule(b)
   if passes.skipCodegen(m.config, n): return n
   if m.module == nil: internalError(m.config, n.info, "myProcess")
+  let globals = PGlobals(m.graph.backend)
   var p = newProc(globals, m, nil, m.module.options)
   p.unique = globals.unique
   genModule(p, n)
@@ -2217,6 +2217,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
   add(p.g.code, p.body)
 
 proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
+  let globals = PGlobals(graph.backend)
   for prc in globals.forwarded:
     if not globals.generatedSyms.containsOrIncl(prc.id):
       var p = newProc(globals, m, nil, m.module.options)
@@ -2261,8 +2262,9 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
   var m = BModule(b)
   if passes.skipCodegen(m.config, n): return n
   if sfMainModule in m.module.flags:
+    let globals = PGlobals(graph.backend)
     let ext = "js"
-    let f = if globals.classes.len == 0: toFilename(FileIndex m.module.position)
+    let f = if globals.classes.len == 0: toFilename(m.config, FileIndex m.module.position)
             else: "nimsystem"
     let code = wholeCode(graph, m)
     let outfile =
@@ -2275,15 +2277,8 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
     for obj, content in items(globals.classes):
       genClass(m.config, obj, content, ext)
 
-proc myOpenCached(graph: ModuleGraph; s: PSym, rd: PRodReader): PPassContext =
-  internalError(graph.config, "symbol files are not possible with the JS code generator")
-  result = nil
-
-proc myOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext =
-  var r = newModule(s)
-  r.graph = graph
-  r.config = graph.config
-  result = r
+proc myOpen(graph: ModuleGraph; s: PSym): PPassContext =
+  result = newModule(graph, s)
 
-const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
+const JSgenPass* = makePass(myOpen, myProcess, myClose)
 
diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim
index f9e4246eb..d86b09a03 100644
--- a/compiler/jstypes.nim
+++ b/compiler/jstypes.nim
@@ -25,7 +25,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
     else:
       s = nil
       for i in countup(0, length - 1):
-        if i > 0: add(s, ", " & tnl)
+        if i > 0: add(s, ", \L")
         add(s, genObjectFields(p, typ, n.sons[i]))
       result = ("{kind: 2, len: $1, offset: 0, " &
           "typ: null, name: null, sons: [$2]}") % [rope(length), s]
@@ -56,15 +56,15 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
           else:
             add(u, rope(getOrdValue(b.sons[j])))
       of nkElse:
-        u = rope(lengthOrd(field.typ))
+        u = rope(lengthOrd(p.config, field.typ))
       else: internalError(p.config, n.info, "genObjectFields(nkRecCase)")
-      if result != nil: add(result, ", " & tnl)
+      if result != nil: add(result, ", \L")
       addf(result, "[setConstr($1), $2]",
            [u, genObjectFields(p, typ, lastSon(b))])
     result = ("{kind: 3, offset: \"$1\", len: $3, " &
         "typ: $2, name: $4, sons: [$5]}") % [
         mangleName(p.module, field), s,
-        rope(lengthOrd(field.typ)), makeJSString(field.name.s), result]
+        rope(lengthOrd(p.config, field.typ)), makeJSString(field.name.s), result]
   else: internalError(p.config, n.info, "genObjectFields")
 
 proc objHasTypeField(t: PType): bool {.inline.} =
@@ -85,7 +85,7 @@ proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
 proc genTupleFields(p: PProc, typ: PType): Rope =
   var s: Rope = nil
   for i in 0 ..< typ.len:
-    if i > 0: add(s, ", " & tnl)
+    if i > 0: add(s, ", \L")
     s.addf("{kind: 1, offset: \"Field$1\", len: 0, " &
            "typ: $2, name: \"Field$1\", sons: null}",
            [i.rope, genTypeInfo(p, typ.sons[i])])
@@ -106,7 +106,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
   for i in countup(0, length - 1):
     if (typ.n.sons[i].kind != nkSym): internalError(p.config, typ.n.info, "genEnumInfo")
     let field = typ.n.sons[i].sym
-    if i > 0: add(s, ", " & tnl)
+    if i > 0: add(s, ", \L")
     let extName = if field.ast == nil: field.name.s else: field.ast.strVal
     addf(s, "\"$1\": {kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}",
          [rope(field.position), name, makeJSString(extName)])
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 40e5bb6b0..0a4801150 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -11,7 +11,7 @@
 
 import
   intsets, strutils, options, ast, astalgo, trees, treetab, msgs,
-  idents, renderer, types, magicsys, rodread, lowerings, tables, modulegraphs
+  idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos
 
 discard """
   The basic approach is that captured vars need to be put on the heap and
@@ -136,7 +136,7 @@ proc createClosureIterStateType*(g: ModuleGraph; iter: PSym): PType =
   rawAddSon(result, intType)
 
 proc createStateField(g: ModuleGraph; iter: PSym): PSym =
-  result = newSym(skField, getIdent(":state"), iter, iter.info)
+  result = newSym(skField, getIdent(g.cache, ":state"), iter, iter.info)
   result.typ = createClosureIterStateType(g, iter)
 
 proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType =
@@ -145,12 +145,12 @@ proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType =
   result = createObj(g, owner, info, final=false)
   rawAddField(result, createStateField(g, owner))
 
-proc getClosureIterResult*(iter: PSym): PSym =
+proc getClosureIterResult*(g: ModuleGraph; iter: PSym): PSym =
   if resultPos < iter.ast.len:
     result = iter.ast.sons[resultPos].sym
   else:
     # XXX a bit hacky:
-    result = newSym(skResult, getIdent":result", iter, iter.info, {})
+    result = newSym(skResult, getIdent(g.cache, ":result"), iter, iter.info, {})
     result.typ = iter.typ.sons[0]
     incl(result.flags, sfUsed)
     iter.ast.add newSymNode(result)
@@ -244,7 +244,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   var env: PNode
   if owner.isIterator:
     let it = getHiddenParam(g, owner)
-    addUniqueField(it.typ.sons[0], hp)
+    addUniqueField(it.typ.sons[0], hp, g.cache)
     env = indirectAccess(newSymNode(it), hp, hp.info)
   else:
     let e = newSym(skLet, iter.name, owner, n.info)
@@ -261,7 +261,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
 proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
   let envParam = getHiddenParam(g, owner)
   let obj = envParam.typ.lastSon
-  addField(obj, s)
+  addField(obj, s, g.cache)
 
   var access = newSymNode(envParam)
   assert obj.kind == tyObject
@@ -278,7 +278,7 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
   let s = n.sym
   if illegalCapture(s):
     localError(g.config, n.info, "illegal capture '$1' of type <$2> which is declared here: $3" %
-      [s.name.s, typeToString(s.typ), $s.info])
+      [s.name.s, typeToString(s.typ), g.config$s.info])
   elif owner.typ.callConv notin {ccClosure, ccDefault}:
     localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
       [s.name.s, owner.name.s, CallingConvToStr[owner.typ.callConv]])
@@ -325,7 +325,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
   if refObj == fieldType:
     localError(c.graph.config, dep.info, "internal error: invalid up reference computed")
 
-  let upIdent = getIdent(upName)
+  let upIdent = getIdent(c.graph.cache, upName)
   let upField = lookupInRecord(obj.n, upIdent)
   if upField != nil:
     if upField.typ != fieldType:
@@ -366,7 +366,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
   let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
   let t = c.getEnvTypeForOwner(owner, info)
   if cp == nil:
-    cp = newSym(skParam, getIdent(paramName), fn, fn.info)
+    cp = newSym(skParam, getIdent(c.graph.cache, paramName), fn, fn.info)
     incl(cp.flags, sfFromGeneric)
     cp.typ = t
     addHiddenParam(fn, cp)
@@ -400,10 +400,10 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
             let obj = getHiddenParam(c.graph, owner).typ.lastSon
             #let obj = c.getEnvTypeForOwner(s.owner).lastSon
 
-            if s.name == getIdent(":state"):
+            if s.name.id == getIdent(c.graph.cache, ":state").id:
               obj.n[0].sym.id = -s.id
             else:
-              addField(obj, s)
+              addField(obj, s, c.graph.cache)
       # but always return because the rest of the proc is only relevant when
       # ow != owner:
       return
@@ -428,7 +428,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
       if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
         let obj = c.getEnvTypeForOwner(ow, n.info).lastSon
         #getHiddenParam(owner).typ.lastSon
-        addField(obj, s)
+        addField(obj, s, c.graph.cache)
       # create required upFields:
       var w = owner.skipGenericOwner
       if isInnerProc(w) or owner.isIterator:
@@ -485,14 +485,14 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
       let field = getFieldFromObj(obj, s)
       if field != nil:
         return rawIndirectAccess(access, field, n.info)
-      let upField = lookupInRecord(obj.n, getIdent(upName))
+      let upField = lookupInRecord(obj.n, getIdent(g.cache, upName))
       if upField == nil: break
       access = rawIndirectAccess(access, upField, n.info)
   localError(g.config, n.info, "internal error: environment misses: " & s.name.s)
   result = n
 
-proc newEnvVar(owner: PSym; typ: PType): PNode =
-  var v = newSym(skVar, getIdent(envName), owner, owner.info)
+proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType): PNode =
+  var v = newSym(skVar, getIdent(cache, envName), owner, owner.info)
   incl(v.flags, sfShadowed)
   v.typ = typ
   result = newSymNode(v)
@@ -513,14 +513,14 @@ proc setupEnvVar(owner: PSym; d: DetectionPass;
     let envVarType = d.ownerToType.getOrDefault(owner.id)
     if envVarType.isNil:
       localError d.graph.config, owner.info, "internal error: could not determine closure type"
-    result = newEnvVar(owner, envVarType)
+    result = newEnvVar(d.graph.cache, owner, envVarType)
     c.envVars[owner.id] = result
 
 proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode =
   let p = getHiddenParam(g, owner)
   result = p.newSymNode
   if owner.isIterator:
-    let upField = lookupInRecord(p.typ.lastSon.n, getIdent(upName))
+    let upField = lookupInRecord(p.typ.lastSon.n, getIdent(g.cache, upName))
     if upField == nil:
       localError(g.config, owner.info, "could not find up reference for closure iter")
     else:
@@ -549,7 +549,7 @@ proc rawClosureCreation(owner: PSym;
         # add ``env.param = param``
         result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info))
 
-  let upField = lookupInRecord(env.typ.lastSon.n, getIdent(upName))
+  let upField = lookupInRecord(env.typ.lastSon.n, getIdent(d.graph.cache, upName))
   if upField != nil:
     let up = getUpViaParam(d.graph, owner)
     if up != nil and upField.typ == up.typ:
@@ -565,13 +565,13 @@ proc closureCreationForIter(iter: PNode;
                             d: DetectionPass; c: var LiftingPass): PNode =
   result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
   let owner = iter.sym.skipGenericOwner
-  var v = newSym(skVar, getIdent(envName), owner, iter.info)
+  var v = newSym(skVar, getIdent(d.graph.cache, envName), owner, iter.info)
   incl(v.flags, sfShadowed)
   v.typ = getHiddenParam(d.graph, iter.sym).typ
   var vnode: PNode
   if owner.isIterator:
     let it = getHiddenParam(d.graph, owner)
-    addUniqueField(it.typ.sons[0], v)
+    addUniqueField(it.typ.sons[0], v, d.graph.cache)
     vnode = indirectAccess(newSymNode(it), v, v.info)
   else:
     vnode = v.newSymNode
@@ -580,7 +580,7 @@ proc closureCreationForIter(iter: PNode;
     result.add(vs)
   result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
 
-  let upField = lookupInRecord(v.typ.lastSon.n, getIdent(upName))
+  let upField = lookupInRecord(v.typ.lastSon.n, getIdent(d.graph.cache, upName))
   if upField != nil:
     let u = setupEnvVar(owner, d, c)
     if u.typ == upField.typ:
@@ -607,81 +607,6 @@ proc getStateField*(g: ModuleGraph; owner: PSym): PSym =
 proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
                       c: var LiftingPass): PNode
 
-proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
-                    c: var LiftingPass): PNode =
-  if c.inContainer > 0:
-    localError(d.graph.config, n.info, "invalid control flow: 'yield' within a constructor")
-  let state = getStateField(d.graph, owner)
-  assert state != nil
-  assert state.typ != nil
-  assert state.typ.n != nil
-  inc state.typ.n.sons[1].intVal
-  let stateNo = state.typ.n.sons[1].intVal
-
-  var stateAsgnStmt = newNodeI(nkAsgn, n.info)
-  stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)),
-                    state, n.info))
-  stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo,
-                    getSysType(d.graph, n.info, tyInt)))
-
-  var retStmt = newNodeI(nkReturnStmt, n.info)
-  if n.sons[0].kind != nkEmpty:
-    var a = newNodeI(nkAsgn, n.sons[0].info)
-    var retVal = liftCapturedVars(n.sons[0], owner, d, c)
-    addSon(a, newSymNode(getClosureIterResult(owner)))
-    addSon(a, retVal)
-    retStmt.add(a)
-  else:
-    retStmt.add(emptyNode)
-
-  var stateLabelStmt = newNodeI(nkState, n.info)
-  stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo,
-                     getSysType(d.graph, n.info, tyInt)))
-
-  result = newNodeI(nkStmtList, n.info)
-  result.add(stateAsgnStmt)
-  result.add(retStmt)
-  result.add(stateLabelStmt)
-
-proc transformReturn(n: PNode; owner: PSym; d: DetectionPass;
-                     c: var LiftingPass): PNode =
-  let state = getStateField(d.graph, owner)
-  result = newNodeI(nkStmtList, n.info)
-  var stateAsgnStmt = newNodeI(nkAsgn, n.info)
-  stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)),
-                    state, n.info))
-  stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(d.graph, n.info, tyInt)))
-  result.add(stateAsgnStmt)
-  result.add(n)
-
-proc wrapIterBody(g: ModuleGraph; n: PNode; owner: PSym): PNode =
-  if not owner.isIterator: return n
-  when false:
-    # unfortunately control flow is still convoluted and we can end up
-    # multiple times here for the very same iterator. We shield against this
-    # with some rather primitive check for now:
-    if n.kind == nkStmtList and n.len > 0:
-      if n.sons[0].kind == nkGotoState: return n
-      if n.len > 1 and n[1].kind == nkStmtList and n[1].len > 0 and
-          n[1][0].kind == nkGotoState:
-        return n
-  let info = n.info
-  result = newNodeI(nkStmtList, info)
-  var gs = newNodeI(nkGotoState, info)
-  gs.add(rawIndirectAccess(newSymNode(getHiddenParam(g, owner)), getStateField(g, owner), info))
-  result.add(gs)
-  var state0 = newNodeI(nkState, info)
-  state0.add(newIntNode(nkIntLit, 0))
-  result.add(state0)
-
-  result.add(n)
-
-  var stateAsgnStmt = newNodeI(nkAsgn, info)
-  stateAsgnStmt.add(rawIndirectAccess(newSymNode(getHiddenParam(g, owner)),
-                    getStateField(g, owner), info))
-  stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(g, info, tyInt)))
-  result.add(stateAsgnStmt)
-
 proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
                   c: var LiftingPass): PNode =
   let s = n.sym
@@ -703,7 +628,7 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
       if access.typ == wanted:
         return makeClosure(d.graph, s, access, n.info)
       let obj = access.typ.sons[0]
-      let upField = lookupInRecord(obj.n, getIdent(upName))
+      let upField = lookupInRecord(obj.n, getIdent(d.graph.cache, upName))
       if upField == nil:
         localError(d.graph.config, n.info, "internal error: no environment found")
         return n
@@ -722,8 +647,6 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
         let oldInContainer = c.inContainer
         c.inContainer = 0
         var body = liftCapturedVars(s.getBody, s, d, c)
-        if oldIterTransf in d.graph.config.features:
-          body = wrapIterBody(d.graph, body, s)
         if c.envvars.getOrDefault(s.id).isNil:
           s.ast.sons[bodyPos] = body
         else:
@@ -766,11 +689,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
       if n[1].kind == nkClosure: result = n[1]
   else:
     if owner.isIterator:
-      if oldIterTransf in d.graph.config.features and n.kind == nkYieldStmt:
-        return transformYield(n, owner, d, c)
-      elif oldIterTransf in d.graph.config.features and n.kind == nkReturnStmt:
-        return transformReturn(n, owner, d, c)
-      elif nfLL in n.flags:
+      if nfLL in n.flags:
         # special case 'when nimVm' due to bug #3636:
         n.sons[1] = liftCapturedVars(n[1], owner, d, c)
         return
@@ -811,7 +730,7 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNo
   fn.typ.callConv = ccClosure
   d.ownerToType[fn.id] = ptrType
   detectCapturedVars(body, fn, d)
-  result = wrapIterBody(g, liftCapturedVars(body, fn, d, c), fn)
+  result = liftCapturedVars(body, fn, d, c)
   fn.kind = oldKind
   fn.typ.callConv = oldCC
 
@@ -840,9 +759,6 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PN
       result = liftCapturedVars(body, fn, d, c)
       if c.envvars.getOrDefault(fn.id) != nil:
         result = newTree(nkStmtList, rawClosureCreation(fn, d, c), result)
-
-      if oldIterTransf in g.config.features:
-        result = wrapIterBody(g, result, fn)
     else:
       result = body
     #if fn.name.s == "get2":
@@ -932,7 +848,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
       body[i].sym.kind = skLet
     addSon(vpart, body[i])
 
-  addSon(vpart, ast.emptyNode) # no explicit type
+  addSon(vpart, newNodeI(nkEmpty, body.info)) # no explicit type
   if not env.isNil:
     call.sons[0] = makeClosure(g, call.sons[0].sym, env.newSymNode, body.info)
   addSon(vpart, call)
@@ -942,12 +858,12 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
   var bs = newNodeI(nkBreakState, body.info)
   bs.addSon(call.sons[0])
 
-  let ibs = newNode(nkIfStmt)
-  let elifBranch = newNode(nkElifBranch)
+  let ibs = newNodeI(nkIfStmt, body.info)
+  let elifBranch = newNodeI(nkElifBranch, body.info)
   elifBranch.add(bs)
 
-  let br = newNode(nkBreakStmt)
-  br.add(emptyNode)
+  let br = newNodeI(nkBreakStmt, body.info)
+  br.add(g.emptyNode)
 
   elifBranch.add(br)
   ibs.add(elifBranch)
diff --git a/compiler/layouter.nim b/compiler/layouter.nim
new file mode 100644
index 000000000..36ad08696
--- /dev/null
+++ b/compiler/layouter.nim
@@ -0,0 +1,274 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Layouter for nimpretty.
+
+import idents, lexer, lineinfos, llstream, options, msgs, strutils
+from os import changeFileExt
+
+const
+  MaxLineLen = 80
+  LineCommentColumn = 30
+
+type
+  SplitKind = enum
+    splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary
+
+  SemicolonKind = enum
+    detectSemicolonKind, useSemicolon, dontTouch
+
+  Emitter* = object
+    config: ConfigRef
+    fid: FileIndex
+    lastTok: TTokType
+    inquote: bool
+    semicolons: SemicolonKind
+    col, lastLineNumber, lineSpan, indentLevel, indWidth: int
+    nested: int
+    doIndentMore*: int
+    content: string
+    indentStack: seq[int]
+    fixedUntil: int # marks where we must not go in the content
+    altSplitPos: array[SplitKind, int] # alternative split positions
+
+proc openEmitter*(em: var Emitter, cache: IdentCache;
+                  config: ConfigRef, fileIdx: FileIndex) =
+  let fullPath = config.toFullPath(fileIdx)
+  em.indWidth = getIndentWidth(fileIdx, llStreamOpen(fullPath, fmRead),
+                               cache, config)
+  if em.indWidth == 0: em.indWidth = 2
+  em.config = config
+  em.fid = fileIdx
+  em.lastTok = tkInvalid
+  em.inquote = false
+  em.col = 0
+  em.content = newStringOfCap(16_000)
+  em.indentStack = newSeqOfCap[int](30)
+  em.indentStack.add 0
+
+proc closeEmitter*(em: var Emitter) =
+  var f = llStreamOpen(em.config.outFile, fmWrite)
+  if f == nil:
+    rawMessage(em.config, errGenerated, "cannot open file: " & em.config.outFile)
+  f.llStreamWrite em.content
+  llStreamClose(f)
+
+proc countNewlines(s: string): int =
+  result = 0
+  for i in 0..<s.len:
+    if s[i] == '\L': inc result
+
+proc calcCol(em: var Emitter; s: string) =
+  var i = s.len-1
+  em.col = 0
+  while i >= 0 and s[i] != '\L':
+    dec i
+    inc em.col
+
+template wr(x) =
+  em.content.add x
+  inc em.col, x.len
+
+template goodCol(col): bool = col in 40..MaxLineLen
+
+const
+  openPars = {tkParLe, tkParDotLe,
+              tkBracketLe, tkBracketLeColon, tkCurlyDotLe,
+              tkCurlyLe}
+  splitters = openPars + {tkComma, tkSemicolon}
+  oprSet = {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
+            tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor}
+
+template rememberSplit(kind) =
+  if goodCol(em.col):
+    em.altSplitPos[kind] = em.content.len
+
+template moreIndent(em): int =
+  (if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth)
+
+proc softLinebreak(em: var Emitter, lit: string) =
+  # XXX Use an algorithm that is outlined here:
+  # https://llvm.org/devmtg/2013-04/jasper-slides.pdf
+  # +2 because we blindly assume a comma or ' &' might follow
+  if not em.inquote and em.col+lit.len+2 >= MaxLineLen:
+    if em.lastTok in splitters:
+      while em.content.len > 0 and em.content[em.content.high] == ' ':
+        setLen(em.content, em.content.len-1)
+      wr("\L")
+      em.col = 0
+      for i in 1..em.indentLevel+moreIndent(em): wr(" ")
+    else:
+      # search backwards for a good split position:
+      for a in em.altSplitPos:
+        if a > em.fixedUntil:
+          var spaces = 0
+          while a+spaces < em.content.len and em.content[a+spaces] == ' ':
+            inc spaces
+          if spaces > 0: delete(em.content, a, a+spaces-1)
+          let ws = "\L" & repeat(' ',em.indentLevel+moreIndent(em))
+          em.col = em.content.len - a
+          em.content.insert(ws, a)
+          break
+
+proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
+
+  template endsInWhite(em): bool =
+    em.content.len == 0 or em.content[em.content.high] in {' ', '\L'}
+  template endsInAlpha(em): bool =
+    em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'}
+
+  proc emitComment(em: var Emitter; tok: TToken) =
+    let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, tok.commentOffsetB)
+    em.lineSpan = countNewlines(lit)
+    if em.lineSpan > 0: calcCol(em, lit)
+    if not endsInWhite(em):
+      wr(" ")
+      if em.lineSpan == 0 and max(em.col, LineCommentColumn) + lit.len <= MaxLineLen:
+        for i in 1 .. LineCommentColumn - em.col: wr(" ")
+    wr lit
+
+  var preventComment = false
+  if tok.tokType == tkComment and tok.line == em.lastLineNumber and tok.indent >= 0:
+    # we have an inline comment so handle it before the indentation token:
+    emitComment(em, tok)
+    preventComment = true
+    em.fixedUntil = em.content.high
+
+  elif tok.indent >= 0:
+    if em.lastTok in (splitters + oprSet):
+      em.indentLevel = tok.indent
+    else:
+      if tok.indent > em.indentStack[^1]:
+        em.indentStack.add tok.indent
+      else:
+        # dedent?
+        while em.indentStack.len > 1 and em.indentStack[^1] > tok.indent:
+          discard em.indentStack.pop()
+      em.indentLevel = em.indentStack.high * em.indWidth
+    #[ we only correct the indentation if it is not in an expression context,
+       so that code like
+
+        const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe,
+                          tkBracketLe, tkBracketLeColon, tkCurlyDotLe,
+                          tkCurlyLe}
+
+       is not touched.
+    ]#
+    # remove trailing whitespace:
+    while em.content.len > 0 and em.content[em.content.high] == ' ':
+      setLen(em.content, em.content.len-1)
+    wr("\L")
+    for i in 2..tok.line - em.lastLineNumber: wr("\L")
+    em.col = 0
+    for i in 1..em.indentLevel:
+      wr(" ")
+    em.fixedUntil = em.content.high
+
+  case tok.tokType
+  of tokKeywordLow..tokKeywordHigh:
+    if endsInAlpha(em):
+      wr(" ")
+    elif not em.inquote and not endsInWhite(em) and
+        em.lastTok notin openPars:
+      #and tok.tokType in oprSet
+      wr(" ")
+
+    if not em.inquote:
+      wr(TokTypeToStr[tok.tokType])
+
+      case tok.tokType
+      of tkAnd: rememberSplit(splitAnd)
+      of tkOr: rememberSplit(splitOr)
+      of tkIn, tkNotin:
+        rememberSplit(splitIn)
+        wr(" ")
+      else: discard
+    else:
+      # keywords in backticks are not normalized:
+      wr(tok.ident.s)
+
+  of tkColon:
+    wr(TokTypeToStr[tok.tokType])
+    wr(" ")
+  of tkSemicolon, tkComma:
+    wr(TokTypeToStr[tok.tokType])
+    rememberSplit(splitComma)
+    wr(" ")
+  of tkParDotLe, tkParLe, tkBracketDotLe, tkBracketLe,
+     tkCurlyLe, tkCurlyDotLe, tkBracketLeColon:
+    if tok.strongSpaceA > 0 and not em.endsInWhite:
+      wr(" ")
+    wr(TokTypeToStr[tok.tokType])
+    rememberSplit(splitParLe)
+  of tkParRi,
+     tkBracketRi, tkCurlyRi,
+     tkBracketDotRi,
+     tkCurlyDotRi,
+     tkParDotRi,
+     tkColonColon, tkDot:
+    wr(TokTypeToStr[tok.tokType])
+  of tkEquals:
+    if not em.inquote and not em.endsInWhite: wr(" ")
+    wr(TokTypeToStr[tok.tokType])
+    if not em.inquote: wr(" ")
+  of tkOpr, tkDotDot:
+    if tok.strongSpaceA == 0 and tok.strongSpaceB == 0:
+      # if not surrounded by whitespace, don't produce any whitespace either:
+      wr(tok.ident.s)
+    else:
+      if not em.endsInWhite: wr(" ")
+      wr(tok.ident.s)
+      template isUnary(tok): bool =
+        tok.strongSpaceB == 0 and tok.strongSpaceA > 0
+
+      if not isUnary(tok):
+        wr(" ")
+        rememberSplit(splitBinary)
+  of tkAccent:
+    if not em.inquote and endsInAlpha(em): wr(" ")
+    wr(TokTypeToStr[tok.tokType])
+    em.inquote = not em.inquote
+  of tkComment:
+    if not preventComment:
+      emitComment(em, tok)
+  of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit:
+    let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
+    softLinebreak(em, lit)
+    if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wr(" ")
+    em.lineSpan = countNewlines(lit)
+    if em.lineSpan > 0: calcCol(em, lit)
+    wr lit
+  of tkEof: discard
+  else:
+    let lit = if tok.ident != nil: tok.ident.s else: tok.literal
+    softLinebreak(em, lit)
+    if endsInAlpha(em): wr(" ")
+    wr lit
+
+  em.lastTok = tok.tokType
+  em.lastLineNumber = tok.line + em.lineSpan
+  em.lineSpan = 0
+
+proc starWasExportMarker*(em: var Emitter) =
+  if em.content.endsWith(" * "):
+    setLen(em.content, em.content.len-3)
+    em.content.add("*")
+    dec em.col, 2
+
+proc commaWasSemicolon*(em: var Emitter) =
+  if em.semicolons == detectSemicolonKind:
+    em.semicolons = if em.content.endsWith(", "): dontTouch else: useSemicolon
+  if em.semicolons == useSemicolon and em.content.endsWith(", "):
+    setLen(em.content, em.content.len-2)
+    em.content.add("; ")
+
+proc curlyRiWasPragma*(em: var Emitter) =
+  if em.content.endsWith("}"):
+    setLen(em.content, em.content.len-1)
+    em.content.add(".}")
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index d498cf4af..c5afa6e97 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -17,7 +17,7 @@
 
 import
   hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
-  wordrecg, configuration
+  wordrecg, lineinfos
 
 const
   MaxLineLength* = 80         # lines longer than this lead to a warning
@@ -273,10 +273,10 @@ template tokenBegin(tok, pos) {.dirty.} =
 template tokenEnd(tok, pos) {.dirty.} =
   when defined(nimsuggest):
     let colB = getColNumber(L, pos)+1
-    if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
-        L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
+    if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and
+        L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
       L.cursor = CursorPosition.InToken
-      gTrackPos.col = colA.int16
+      L.config.m.trackPos.col = colA.int16
     colA = 0
   when defined(nimpretty):
     tok.offsetB = L.offsetBase + pos
@@ -284,10 +284,10 @@ template tokenEnd(tok, pos) {.dirty.} =
 template tokenEndIgnore(tok, pos) =
   when defined(nimsuggest):
     let colB = getColNumber(L, pos)
-    if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
-        L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
-      gTrackPos.fileIndex = trackPosInvalidFileIdx
-      gTrackPos.line = 0'u16
+    if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and
+        L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
+      L.config.m.trackPos.fileIndex = trackPosInvalidFileIdx
+      L.config.m.trackPos.line = 0'u16
     colA = 0
   when defined(nimpretty):
     tok.offsetB = L.offsetBase + pos
@@ -298,11 +298,11 @@ template tokenEndPrevious(tok, pos) =
     # to the token that came before that, but only if we haven't detected
     # the cursor in a string literal or comment:
     let colB = getColNumber(L, pos)
-    if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
-        L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
+    if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and
+        L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
       L.cursor = CursorPosition.BeforeToken
-      gTrackPos = L.previousToken
-      gTrackPosAttached = true
+      L.config.m.trackPos = L.previousToken
+      L.config.m.trackPosAttached = true
     colA = 0
   when defined(nimpretty):
     tok.offsetB = L.offsetBase + pos
@@ -630,14 +630,14 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) =
     if L.config.oldNewlines:
       if tok.tokType == tkCharLit:
         lexMessage(L, errGenerated, "\\n not allowed in character literal")
-      add(tok.literal, tnl)
+      add(tok.literal, L.config.target.tnl)
     else:
       add(tok.literal, '\L')
     inc(L.bufpos)
   of 'p', 'P':
     if tok.tokType == tkCharLit:
       lexMessage(L, errGenerated, "\\p not allowed in character literal")
-    add(tok.literal, tnl)
+    add(tok.literal, L.config.target.tnl)
     inc(L.bufpos)
   of 'r', 'R', 'c', 'C':
     add(tok.literal, CR)
@@ -714,11 +714,6 @@ proc handleCRLF(L: var TLexer, pos: int): int =
     if col > MaxLineLength:
       lexMessagePos(L, hintLineTooLong, pos)
 
-    if optEmbedOrigSrc in L.config.globalOptions:
-      let lineStart = cast[ByteAddress](L.buf) + L.lineStart
-      let line = newString(cast[cstring](lineStart), col)
-      addSourceLine(L.fileIdx, line)
-
   case L.buf[pos]
   of CR:
     registerLine()
@@ -872,7 +867,7 @@ proc getOperator(L: var TLexer, tok: var TToken) =
   if buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
     tok.strongSpaceB = -1
 
-proc newlineFollows*(L: var TLexer): bool =
+proc newlineFollows*(L: TLexer): bool =
   var pos = L.bufpos
   var buf = L.buf
   while true:
@@ -954,6 +949,8 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int;
       if isDoc or defined(nimpretty): tok.literal.add buf[pos]
       inc(pos)
   L.bufpos = pos
+  when defined(nimpretty):
+    tok.commentOffsetB = L.offsetBase + pos - 1
 
 proc scanComment(L: var TLexer, tok: var TToken) =
   var pos = L.bufpos
@@ -962,6 +959,9 @@ proc scanComment(L: var TLexer, tok: var TToken) =
   # iNumber contains the number of '\n' in the token
   tok.iNumber = 0
   assert buf[pos+1] == '#'
+  when defined(nimpretty):
+    tok.commentOffsetA = L.offsetBase + pos - 1
+
   if buf[pos+2] == '[':
     skipMultiLineComment(L, tok, pos+3, true)
     return
@@ -1001,6 +1001,8 @@ proc scanComment(L: var TLexer, tok: var TToken) =
       tokenEndIgnore(tok, pos)
       break
   L.bufpos = pos
+  when defined(nimpretty):
+    tok.commentOffsetB = L.offsetBase + pos - 1
 
 proc skip(L: var TLexer, tok: var TToken) =
   var pos = L.bufpos
@@ -1021,6 +1023,9 @@ proc skip(L: var TLexer, tok: var TToken) =
       inc(pos)
     of CR, LF:
       tokenEndPrevious(tok, pos)
+      when defined(nimpretty):
+        # we are not yet in a comment, so update the comment token's line information:
+        if not hasComment: inc tok.line
       pos = handleCRLF(L, pos)
       buf = L.buf
       var indent = 0
@@ -1060,7 +1065,7 @@ proc skip(L: var TLexer, tok: var TToken) =
   L.bufpos = pos
   when defined(nimpretty):
     if hasComment:
-      tok.commentOffsetB = L.offsetBase + pos
+      tok.commentOffsetB = L.offsetBase + pos - 1
       tok.tokType = tkComment
     if gIndentationWidth <= 0:
       gIndentationWidth = tok.indent
@@ -1121,9 +1126,9 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
       else:
         tok.tokType = tkParLe
         when defined(nimsuggest):
-          if L.fileIdx == gTrackPos.fileIndex and tok.col < gTrackPos.col and
-                    tok.line == gTrackPos.line.int and L.config.ideCmd == ideCon:
-            gTrackPos.col = tok.col.int16
+          if L.fileIdx == L.config.m.trackPos.fileIndex and tok.col < L.config.m.trackPos.col and
+                    tok.line == L.config.m.trackPos.line.int and L.config.ideCmd == ideCon:
+            L.config.m.trackPos.col = tok.col.int16
     of ')':
       tok.tokType = tkParRi
       inc(L.bufpos)
@@ -1142,11 +1147,11 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
       inc(L.bufpos)
     of '.':
       when defined(nimsuggest):
-        if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and
-            tok.line == gTrackPos.line.int and L.config.ideCmd == ideSug:
+        if L.fileIdx == L.config.m.trackPos.fileIndex and tok.col+1 == L.config.m.trackPos.col and
+            tok.line == L.config.m.trackPos.line.int and L.config.ideCmd == ideSug:
           tok.tokType = tkDot
           L.cursor = CursorPosition.InToken
-          gTrackPos.col = tok.col.int16
+          L.config.m.trackPos.col = tok.col.int16
           inc(L.bufpos)
           atTokenEnd()
           return
@@ -1215,3 +1220,15 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
         lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')')
         inc(L.bufpos)
   atTokenEnd()
+
+proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream;
+                     cache: IdentCache; config: ConfigRef): int =
+  var lex: TLexer
+  var tok: TToken
+  initToken(tok)
+  openLexer(lex, fileIdx, inputstream, cache, config)
+  while true:
+    rawGetTok(lex, tok)
+    result = tok.indent
+    if result > 0 or tok.tokType == tkEof: break
+  closeLexer(lex)
diff --git a/compiler/liftlocals.nim b/compiler/liftlocals.nim
index 4603d357b..ae789cd88 100644
--- a/compiler/liftlocals.nim
+++ b/compiler/liftlocals.nim
@@ -11,7 +11,7 @@
 
 import
   intsets, strutils, options, ast, astalgo, msgs,
-  idents, renderer, types, lowerings
+  idents, renderer, types, lowerings, lineinfos
 
 from pragmas import getPragmaVal
 from wordrecg import wLiftLocals
@@ -20,13 +20,14 @@ type
   Ctx = object
     partialParam: PSym
     objType: PType
+    cache: IdentCache
 
 proc interestingVar(s: PSym): bool {.inline.} =
   result = s.kind in {skVar, skLet, skTemp, skForVar, skResult} and
     sfGlobal notin s.flags
 
 proc lookupOrAdd(c: var Ctx; s: PSym; info: TLineInfo): PNode =
-  let field = addUniqueField(c.objType, s)
+  let field = addUniqueField(c.objType, s, c.cache)
   var deref = newNodeI(nkHiddenDeref, info)
   deref.typ = c.objType
   add(deref, newSymNode(c.partialParam, info))
@@ -52,7 +53,7 @@ proc lookupParam(params, dest: PNode): PSym =
     if params[i].kind == nkSym and params[i].sym.name.id == dest.ident.id:
       return params[i].sym
 
-proc liftLocalsIfRequested*(prc: PSym; n: PNode; conf: ConfigRef): PNode =
+proc liftLocalsIfRequested*(prc: PSym; n: PNode; cache: IdentCache; conf: ConfigRef): PNode =
   let liftDest = getPragmaVal(prc.ast, wLiftLocals)
   if liftDest == nil: return n
   let partialParam = lookupParam(prc.typ.n, liftDest)
@@ -64,7 +65,7 @@ proc liftLocalsIfRequested*(prc: PSym; n: PNode; conf: ConfigRef): PNode =
   if objType.kind != tyObject or tfPartial notin objType.flags:
     localError(conf, liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest)
     return n
-  var c = Ctx(partialParam: partialParam, objType: objType)
+  var c = Ctx(partialParam: partialParam, objType: objType, cache: cache)
   let w = newTree(nkStmtList, n)
   liftLocals(w, 0, c)
   result = w[0]
diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim
new file mode 100644
index 000000000..cad1fe6aa
--- /dev/null
+++ b/compiler/lineinfos.nim
@@ -0,0 +1,267 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module contains the ``TMsgKind`` enum as well as the
+## ``TLineInfo`` object.
+
+import ropes, tables
+
+const
+  explanationsBaseUrl* = "https://nim-lang.org/docs/manual"
+
+type
+  TMsgKind* = enum
+    errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile,
+    errXExpected,
+    errGridTableNotImplemented,
+    errGeneralParseError,
+    errNewSectionExpected,
+    errInvalidDirectiveX,
+    errGenerated,
+    errUser,
+    warnCannotOpenFile,
+    warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
+    warnDeprecated, warnConfigDeprecated,
+    warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
+    warnUnknownSubstitutionX, warnLanguageXNotSupported,
+    warnFieldXNotSupported, warnCommentXIgnored,
+    warnTypelessParam,
+    warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
+    warnEachIdentIsTuple, warnShadowIdent,
+    warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
+    warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
+    warnInconsistentSpacing, warnUser,
+    hintSuccess, hintSuccessX,
+    hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
+    hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
+    hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
+    hintConditionAlwaysTrue, hintName, hintPattern,
+    hintExecuting, hintLinking, hintDependency,
+    hintSource, hintPerformance, hintStackTrace, hintGCStats,
+    hintGlobalVar,
+    hintUser, hintUserRaw
+
+const
+  MsgKindToStr*: array[TMsgKind, string] = [
+    errUnknown: "unknown error",
+    errInternal: "internal error: $1",
+    errIllFormedAstX: "illformed AST: $1",
+    errCannotOpenFile: "cannot open '$1'",
+    errXExpected: "'$1' expected",
+    errGridTableNotImplemented: "grid table is not implemented",
+    errGeneralParseError: "general parse error",
+    errNewSectionExpected: "new section expected",
+    errInvalidDirectiveX: "invalid directive: '$1'",
+    errGenerated: "$1",
+    errUser: "$1",
+    warnCannotOpenFile: "cannot open '$1'",
+    warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
+    warnXIsNeverRead: "'$1' is never read",
+    warnXmightNotBeenInit: "'$1' might not have been initialized",
+    warnDeprecated: "$1 is deprecated",
+    warnConfigDeprecated: "config file '$1' is deprecated",
+    warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
+    warnUnknownMagic: "unknown magic '$1' might crash the compiler",
+    warnRedefinitionOfLabel: "redefinition of label '$1'",
+    warnUnknownSubstitutionX: "unknown substitution '$1'",
+    warnLanguageXNotSupported: "language '$1' not supported",
+    warnFieldXNotSupported: "field '$1' not supported",
+    warnCommentXIgnored: "comment '$1' ignored",
+    warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
+    warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
+    warnWriteToForeignHeap: "write to foreign heap",
+    warnUnsafeCode: "unsafe code: '$1'",
+    warnEachIdentIsTuple: "each identifier is a tuple",
+    warnShadowIdent: "shadowed identifier: '$1'",
+    warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.",
+    warnProveField: "cannot prove that field '$1' is accessible",
+    warnProveIndex: "cannot prove index '$1' is valid",
+    warnGcUnsafe: "not GC-safe: '$1'",
+    warnGcUnsafe2: "$1",
+    warnUninit: "'$1' might not have been initialized",
+    warnGcMem: "'$1' uses GC'ed memory",
+    warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
+    warnLockLevel: "$1",
+    warnResultShadowed: "Special variable 'result' is shadowed.",
+    warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
+    warnUser: "$1",
+    hintSuccess: "operation successful",
+    hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
+    hintLineTooLong: "line too long",
+    hintXDeclaredButNotUsed: "'$1' is declared but not used",
+    hintConvToBaseNotNeeded: "conversion to base object is not needed",
+    hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless",
+    hintExprAlwaysX: "expression evaluates always to '$1'",
+    hintQuitCalled: "quit() called",
+    hintProcessing: "$1",
+    hintCodeBegin: "generated code listing:",
+    hintCodeEnd: "end of listing",
+    hintConf: "used config file '$1'",
+    hintPath: "added path: '$1'",
+    hintConditionAlwaysTrue: "condition is always true: '$1'",
+    hintName: "name should be: '$1'",
+    hintPattern: "$1",
+    hintExecuting: "$1",
+    hintLinking: "",
+    hintDependency: "$1",
+    hintSource: "$1",
+    hintPerformance: "$1",
+    hintStackTrace: "$1",
+    hintGCStats: "$1",
+    hintGlobalVar: "global variable declared here",
+    hintUser: "$1",
+    hintUserRaw: "$1"]
+
+const
+  WarningsToStr* = ["CannotOpenFile", "OctalEscape",
+    "XIsNeverRead", "XmightNotBeenInit",
+    "Deprecated", "ConfigDeprecated",
+    "SmallLshouldNotBeUsed", "UnknownMagic",
+    "RedefinitionOfLabel", "UnknownSubstitutionX",
+    "LanguageXNotSupported", "FieldXNotSupported",
+    "CommentXIgnored",
+    "TypelessParam", "UseBase", "WriteToForeignHeap",
+    "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
+    "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
+    "GcMem", "Destructor", "LockLevel", "ResultShadowed",
+    "Spacing", "User"]
+
+  HintsToStr* = ["Success", "SuccessX", "LineTooLong",
+    "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
+    "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
+    "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
+    "Source", "Performance", "StackTrace", "GCStats", "GlobalVar",
+    "User", "UserRaw"]
+
+const
+  fatalMin* = errUnknown
+  fatalMax* = errInternal
+  errMin* = errUnknown
+  errMax* = errUser
+  warnMin* = warnCannotOpenFile
+  warnMax* = pred(hintSuccess)
+  hintMin* = hintSuccess
+  hintMax* = high(TMsgKind)
+
+static:
+  doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1
+  doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1
+
+type
+  TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
+  TNoteKinds* = set[TNoteKind]
+
+const
+  NotesVerbosity*: array[0..3, TNoteKinds] = [
+    {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
+                                         warnProveField, warnProveIndex,
+                                         warnGcUnsafe,
+                                         hintSuccessX, hintPath, hintConf,
+                                         hintProcessing, hintPattern,
+                                         hintDependency,
+                                         hintExecuting, hintLinking,
+                                         hintCodeBegin, hintCodeEnd,
+                                         hintSource, hintStackTrace,
+                                         hintGlobalVar, hintGCStats},
+    {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
+                                         warnProveField, warnProveIndex,
+                                         warnGcUnsafe,
+                                         hintPath,
+                                         hintDependency,
+                                         hintCodeBegin, hintCodeEnd,
+                                         hintSource, hintStackTrace,
+                                         hintGlobalVar, hintGCStats},
+    {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit},
+    {low(TNoteKind)..high(TNoteKind)}]
+
+const
+  errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
+  errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
+
+type
+  TFileInfo* = object
+    fullPath*: string          # This is a canonical full filesystem path
+    projPath*: string          # This is relative to the project's root
+    shortName*: string         # short name of the module
+    quotedName*: Rope          # cached quoted short name for codegen
+                               # purposes
+    quotedFullName*: Rope      # cached quoted full name for codegen
+                               # purposes
+
+    lines*: seq[string]        # the source code of the module
+                               #   used for better error messages and
+                               #   embedding the original source in the
+                               #   generated code
+    dirtyfile*: string         # the file that is actually read into memory
+                               # and parsed; usually "" but is used
+                               # for 'nimsuggest'
+    hash*: string              # the checksum of the file
+    dirty*: bool               # for 'nimfix' / 'nimpretty' like tooling
+    when defined(nimpretty):
+      fullContent*: string
+  FileIndex* = distinct int32
+  TLineInfo* = object          # This is designed to be as small as possible,
+                               # because it is used
+                               # in syntax nodes. We save space here by using
+                               # two int16 and an int32.
+                               # On 64 bit and on 32 bit systems this is
+                               # only 8 bytes.
+    line*: uint16
+    col*: int16
+    fileIndex*: FileIndex
+    when defined(nimpretty):
+      offsetA*, offsetB*: int
+      commentOffsetA*, commentOffsetB*: int
+
+  TErrorOutput* = enum
+    eStdOut
+    eStdErr
+
+  TErrorOutputs* = set[TErrorOutput]
+
+  ERecoverableError* = object of ValueError
+  ESuggestDone* = object of Exception
+
+proc `==`*(a, b: FileIndex): bool {.borrow.}
+
+const
+  InvalidFileIDX* = FileIndex(-1)
+
+proc unknownLineInfo*(): TLineInfo =
+  result.line = uint16(0)
+  result.col = int16(-1)
+  result.fileIndex = InvalidFileIDX
+
+type
+  Severity* {.pure.} = enum ## VS Code only supports these three
+    Hint, Warning, Error
+
+const trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no suggestions
+                                   # are produced within comments and string literals
+
+type
+  MsgConfig* = object ## does not need to be stored in the incremental cache
+    trackPos*: TLineInfo
+    trackPosAttached*: bool ## whether the tracking position was attached to
+                            ## some close token.
+
+    errorOutputs*: TErrorOutputs
+    msgContext*: seq[TLineInfo]
+    lastError*: TLineInfo
+    filenameToIndexTbl*: Table[string, FileIndex]
+    fileInfos*: seq[TFileInfo]
+    systemFileIdx*: FileIndex
+
+
+proc initMsgConfig*(): MsgConfig =
+  result.msgContext = @[]
+  result.lastError = unknownLineInfo()
+  result.filenameToIndexTbl = initTable[string, FileIndex]()
+  result.fileInfos = @[]
+  result.errorOutputs = {eStdOut, eStdErr}
diff --git a/compiler/nimfix/pretty.nim b/compiler/linter.nim
index 96429ad53..7c9cdec83 100644
--- a/compiler/nimfix/pretty.nim
+++ b/compiler/linter.nim
@@ -13,9 +13,18 @@
 import
   strutils, os, intsets, strtabs
 
-import ".." / [options, ast, astalgo, msgs, semdata, ropes, idents,
-  configuration]
-import prettybase
+import options, ast, astalgo, msgs, semdata, ropes, idents,
+  lineinfos
+
+const
+  Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
+
+proc identLen*(line: string, start: int): int =
+  while start+result < line.len and line[start+result] in Letters:
+    inc result
+
+when false:
+  import prettybase
 
 type
   StyleCheck* {.pure.} = enum None, Warn, Auto
@@ -27,19 +36,19 @@ var
 
 proc overwriteFiles*(conf: ConfigRef) =
   let doStrip = options.getConfigVar(conf, "pretty.strip").normalize == "on"
-  for i in 0 .. high(gSourceFiles):
-    if gSourceFiles[i].dirty and not gSourceFiles[i].isNimfixFile and
-        (not gOnlyMainfile or gSourceFiles[i].fileIdx == conf.projectMainIdx.FileIndex):
-      let newFile = if gOverWrite: gSourceFiles[i].fullpath
-                    else: gSourceFiles[i].fullpath.changeFileExt(".pretty.nim")
+  for i in 0 .. high(conf.m.fileInfos):
+    if conf.m.fileInfos[i].dirty and
+        (not gOnlyMainfile or FileIndex(i) == conf.projectMainIdx):
+      let newFile = if gOverWrite: conf.m.fileInfos[i].fullpath
+                    else: conf.m.fileInfos[i].fullpath.changeFileExt(".pretty.nim")
       try:
         var f = open(newFile, fmWrite)
-        for line in gSourceFiles[i].lines:
+        for line in conf.m.fileInfos[i].lines:
           if doStrip:
             f.write line.strip(leading = false, trailing = true)
           else:
             f.write line
-          f.write(gSourceFiles[i].newline)
+          f.write(conf.m.fileInfos[i], "\L")
         f.close
       except IOError:
         rawMessage(conf, errGenerated, "cannot open file: " & newFile)
@@ -93,14 +102,16 @@ proc beautifyName(s: string, k: TSymKind): string =
       result.add s[i]
     inc i
 
-proc replaceInFile(info: TLineInfo; newName: string) =
-  loadFile(info)
+proc differ*(line: string, a, b: int, x: string): bool =
+  let y = line[a..b]
+  result = cmpIgnoreStyle(y, x) == 0 and y != x
 
-  let line = gSourceFiles[info.fileIndex.int].lines[info.line.int-1]
+proc replaceInFile(conf: ConfigRef; info: TLineInfo; newName: string) =
+  let line = conf.m.fileInfos[info.fileIndex.int].lines[info.line.int-1]
   var first = min(info.col.int, line.len)
   if first < 0: return
   #inc first, skipIgnoreCase(line, "proc ", first)
-  while first > 0 and line[first-1] in prettybase.Letters: dec first
+  while first > 0 and line[first-1] in Letters: dec first
   if first < 0: return
   if line[first] == '`': inc first
 
@@ -108,46 +119,56 @@ proc replaceInFile(info: TLineInfo; newName: string) =
   if differ(line, first, last, newName):
     # last-first+1 != newName.len or
     var x = line.substr(0, first-1) & newName & line.substr(last+1)
-    system.shallowCopy(gSourceFiles[info.fileIndex.int].lines[info.line.int-1], x)
-    gSourceFiles[info.fileIndex.int].dirty = true
+    system.shallowCopy(conf.m.fileInfos[info.fileIndex.int].lines[info.line.int-1], x)
+    conf.m.fileInfos[info.fileIndex.int].dirty = true
 
-proc checkStyle(conf: ConfigRef; info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
+proc checkStyle(conf: ConfigRef; cache: IdentCache; info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
   let beau = beautifyName(s, k)
   if s != beau:
     if gStyleCheck == StyleCheck.Auto:
-      sym.name = getIdent(beau)
-      replaceInFile(info, beau)
+      sym.name = getIdent(cache, beau)
+      replaceInFile(conf, info, beau)
     else:
       message(conf, info, hintName, beau)
 
-proc styleCheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
+proc styleCheckDefImpl(conf: ConfigRef; cache: IdentCache; info: TLineInfo; s: PSym; k: TSymKind) =
   # operators stay as they are:
-  if k in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters: return
+  if k in {skResult, skTemp} or s.name.s[0] notin Letters: return
   if k in {skType, skGenericParam} and sfAnon in s.flags: return
   if {sfImportc, sfExportc} * s.flags == {} or gCheckExtern:
-    checkStyle(conf, info, s.name.s, k, s)
+    checkStyle(conf, cache, info, s.name.s, k, s)
+
+proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
+  # operators stay as they are:
+  if k in {skResult, skTemp} or s.name.s[0] notin Letters: return
+  if k in {skType, skGenericParam} and sfAnon in s.flags: return
+  let beau = beautifyName(s.name.s, k)
+  if s.name.s != beau:
+    message(conf, info, hintName, beau)
 
-template styleCheckDef*(info: TLineInfo; s: PSym; k: TSymKind) =
+template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
+  if optCheckNep1 in conf.globalOptions:
+    nep1CheckDefImpl(conf, info, s, k)
   when defined(nimfix):
-    if gStyleCheck != StyleCheck.None: styleCheckDefImpl(conf, info, s, k)
+    if gStyleCheck != StyleCheck.None: styleCheckDefImpl(conf, cache, info, s, k)
 
-template styleCheckDef*(info: TLineInfo; s: PSym) =
-  styleCheckDef(info, s, s.kind)
-template styleCheckDef*(s: PSym) =
-  styleCheckDef(s.info, s, s.kind)
+template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym) =
+  styleCheckDef(conf, info, s, s.kind)
+template styleCheckDef*(conf: ConfigRef; s: PSym) =
+  styleCheckDef(conf, s.info, s, s.kind)
 
-proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
+proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) =
   if info.fileIndex.int < 0: return
   # we simply convert it to what it looks like in the definition
   # for consistency
 
   # operators stay as they are:
-  if s.kind in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters:
+  if s.kind in {skResult, skTemp} or s.name.s[0] notin Letters:
     return
   if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return
   let newName = s.name.s
 
-  replaceInFile(info, newName)
+  replaceInFile(conf, info, newName)
   #if newName == "File": writeStackTrace()
 
 template styleCheckUse*(info: TLineInfo; s: PSym) =
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index b6d63d0bd..87694988a 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -10,8 +10,8 @@
 # This module implements lookup helpers.
 
 import
-  intsets, ast, astalgo, idents, semdata, types, msgs, options, rodread,
-  renderer, wordrecg, idgen, nimfix.prettybase, configuration, strutils
+  intsets, ast, astalgo, idents, semdata, types, msgs, options,
+  renderer, wordrecg, idgen, nimfix.prettybase, lineinfos, strutils
 
 proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
 
@@ -22,13 +22,13 @@ proc noidentError(conf: ConfigRef; n, origin: PNode) =
   m.add "identifier expected, but found '" & n.renderTree & "'"
   localError(conf, n.info, m)
 
-proc considerQuotedIdent*(conf: ConfigRef; n: PNode, origin: PNode = nil): PIdent =
+proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
   ## Retrieve a PIdent from a PNode, taking into account accent nodes.
   ## ``origin`` can be nil. If it is not nil, it is used for a better
   ## error message.
   template handleError(n, origin: PNode) =
-    noidentError(conf, n, origin)
-    result = getIdent"<Error>"
+    noidentError(c.config, n, origin)
+    result = getIdent(c.cache, "<Error>")
 
   case n.kind
   of nkIdent: result = n.ident
@@ -36,7 +36,7 @@ proc considerQuotedIdent*(conf: ConfigRef; n: PNode, origin: PNode = nil): PIden
   of nkAccQuoted:
     case n.len
     of 0: handleError(n, origin)
-    of 1: result = considerQuotedIdent(conf, n.sons[0], origin)
+    of 1: result = considerQuotedIdent(c, n.sons[0], origin)
     else:
       var id = ""
       for i in 0..<n.len:
@@ -46,7 +46,7 @@ proc considerQuotedIdent*(conf: ConfigRef; n: PNode, origin: PNode = nil): PIden
         of nkSym: id.add(x.sym.name.s)
         of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
         else: handleError(n, origin)
-      result = getIdent(id)
+      result = getIdent(c.cache, id)
   of nkOpenSymChoice, nkClosedSymChoice:
     if n[0].kind == nkSym:
       result = n.sons[0].sym.name
@@ -86,7 +86,7 @@ proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
   else:
     result = s.owner
     if conf.cmd == cmdPretty:
-      prettybase.replaceDeprecated(n.info, s, result)
+      prettybase.replaceDeprecated(conf, n.info, s, result)
     else:
       message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
               s.name.s)
@@ -100,15 +100,16 @@ proc searchInScopes*(c: PContext, s: PIdent): PSym =
     if result != nil: return
   result = nil
 
-proc debugScopes*(c: PContext; limit=0) {.deprecated.} =
-  var i = 0
-  for scope in walkScopes(c.currentScope):
-    echo "scope ", i
-    for h in 0 .. high(scope.symbols.data):
-      if scope.symbols.data[h] != nil:
-        echo scope.symbols.data[h].name.s
-    if i == limit: break
-    inc i
+when declared(echo):
+  proc debugScopes*(c: PContext; limit=0) {.deprecated.} =
+    var i = 0
+    for scope in walkScopes(c.currentScope):
+      echo "scope ", i
+      for h in 0 .. high(scope.symbols.data):
+        if scope.symbols.data[h] != nil:
+          echo scope.symbols.data[h].name.s
+      if i == limit: break
+      inc i
 
 proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym =
   for scope in walkScopes(c.currentScope):
@@ -125,9 +126,9 @@ proc errorSym*(c: PContext, n: PNode): PSym =
   # ensure that 'considerQuotedIdent' can't fail:
   if m.kind == nkDotExpr: m = m.sons[1]
   let ident = if m.kind in {nkIdent, nkSym, nkAccQuoted}:
-      considerQuotedIdent(c.config, m)
+      considerQuotedIdent(c, m)
     else:
-      getIdent("err:" & renderTree(m))
+      getIdent(c.cache, "err:" & renderTree(m))
   result = newSym(skError, ident, getCurrOwner(c), n.info, {})
   result.typ = errorType(c)
   incl(result.flags, sfDiscardable)
@@ -139,7 +140,7 @@ type
   TOverloadIterMode* = enum
     oimDone, oimNoQualifier, oimSelfModule, oimOtherModule, oimSymChoice,
     oimSymChoiceLocalLookup
-  TOverloadIter*{.final.} = object
+  TOverloadIter* = object
     it*: TIdentIter
     m*: PSym
     mode*: TOverloadIterMode
@@ -147,10 +148,10 @@ type
     scope*: PScope
     inSymChoice: IntSet
 
-proc getSymRepr*(s: PSym): string =
+proc getSymRepr*(conf: ConfigRef; s: PSym): string =
   case s.kind
   of skProc, skFunc, skMethod, skConverter, skIterator:
-    result = getProcHeader(s)
+    result = getProcHeader(conf, s)
   else:
     result = s.name.s
 
@@ -164,7 +165,8 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
       # too many 'implementation of X' errors are annoying
       # and slow 'suggest' down:
       if missingImpls == 0:
-        localError(c.config, s.info, "implementation of '$1' expected" % getSymRepr(s))
+        localError(c.config, s.info, "implementation of '$1' expected" %
+            getSymRepr(c.config, s))
       inc missingImpls
     elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options:
       if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}:
@@ -172,7 +174,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
         # maybe they can be made skGenericParam as well.
         if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and
            s.typ.kind != tyGenericParam:
-          message(c.config, s.info, hintXDeclaredButNotUsed, getSymRepr(s))
+          message(c.config, s.info, hintXDeclaredButNotUsed, getSymRepr(c.config, s))
     s = nextIter(it, scope.symbols)
 
 proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string) =
@@ -275,7 +277,7 @@ proc lookUp*(c: PContext, n: PNode): PSym =
   of nkSym:
     result = n.sym
   of nkAccQuoted:
-    var ident = considerQuotedIdent(c.config, n)
+    var ident = considerQuotedIdent(c, n)
     result = searchInScopes(c, ident).skipAlias(n, c.config)
     if result == nil:
       fixSpelling(n, ident, searchInScopes)
@@ -286,7 +288,8 @@ proc lookUp*(c: PContext, n: PNode): PSym =
     return
   if contains(c.ambiguousSymbols, result.id):
     errorUseQualifier(c, n.info, result)
-  if result.kind == skStub: loadStub(result)
+  when false:
+    if result.kind == skStub: loadStub(result)
 
 type
   TLookupFlag* = enum
@@ -296,7 +299,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
   const allExceptModule = {low(TSymKind)..high(TSymKind)}-{skModule,skPackage}
   case n.kind
   of nkIdent, nkAccQuoted:
-    var ident = considerQuotedIdent(c.config, n)
+    var ident = considerQuotedIdent(c, n)
     if checkModule in flags:
       result = searchInScopes(c, ident).skipAlias(n, c.config)
     else:
@@ -322,7 +325,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
       if n.sons[1].kind == nkIdent:
         ident = n.sons[1].ident
       elif n.sons[1].kind == nkAccQuoted:
-        ident = considerQuotedIdent(c.config, n.sons[1])
+        ident = considerQuotedIdent(c, n.sons[1])
       if ident != nil:
         if m == c.module:
           result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config)
@@ -341,12 +344,13 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
         result = errorSym(c, n.sons[1])
   else:
     result = nil
-  if result != nil and result.kind == skStub: loadStub(result)
+  when false:
+    if result != nil and result.kind == skStub: loadStub(result)
 
 proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
   case n.kind
   of nkIdent, nkAccQuoted:
-    var ident = considerQuotedIdent(c.config, n)
+    var ident = considerQuotedIdent(c, n)
     o.scope = c.currentScope
     o.mode = oimNoQualifier
     while true:
@@ -367,7 +371,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
       if n.sons[1].kind == nkIdent:
         ident = n.sons[1].ident
       elif n.sons[1].kind == nkAccQuoted:
-        ident = considerQuotedIdent(c.config, n.sons[1], n)
+        ident = considerQuotedIdent(c, n.sons[1], n)
       if ident != nil:
         if o.m == c.module:
           # a module may access its private members:
@@ -390,7 +394,8 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
     o.inSymChoice = initIntSet()
     incl(o.inSymChoice, result.id)
   else: discard
-  if result != nil and result.kind == skStub: loadStub(result)
+  when false:
+    if result != nil and result.kind == skStub: loadStub(result)
 
 proc lastOverloadScope*(o: TOverloadIter): int =
   case o.mode
@@ -441,7 +446,8 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
       result = firstIdentExcluding(o.it, o.scope.symbols,
                                    n.sons[0].sym.name, o.inSymChoice).skipAlias(n, c.config)
 
-  if result != nil and result.kind == skStub: loadStub(result)
+  when false:
+    if result != nil and result.kind == skStub: loadStub(result)
 
 proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind];
               flags: TSymFlags = {}): PSym =
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 13336f00e..1b17f620c 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -12,7 +12,8 @@
 const
   genPrefix* = ":tmp"         # prefix for generated names
 
-import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs
+import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs,
+  lineinfos
 from trees import getMagic
 
 proc newDeref*(n: PNode): PNode {.inline.} =
@@ -30,8 +31,8 @@ proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
 proc addVar*(father, v: PNode) =
   var vpart = newNodeI(nkIdentDefs, v.info, 3)
   vpart.sons[0] = v
-  vpart.sons[1] = ast.emptyNode
-  vpart.sons[2] = ast.emptyNode
+  vpart.sons[1] = newNodeI(nkEmpty, v.info)
+  vpart.sons[2] = vpart[1]
   addSon(father, vpart)
 
 proc newAsgnStmt(le, ri: PNode): PNode =
@@ -49,7 +50,7 @@ proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   let value = n.lastSon
   result = newNodeI(nkStmtList, n.info)
 
-  var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info, g.config.options)
+  var temp = newSym(skTemp, getIdent(g.cache, genPrefix), owner, value.info, g.config.options)
   temp.typ = skipTypes(value.typ, abstractInst)
   incl(temp.flags, sfFromGeneric)
 
@@ -73,17 +74,17 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
 proc newTryFinally*(body, final: PNode): PNode =
   result = newTree(nkTryStmt, body, newTree(nkFinally, final))
 
-proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
+proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   let value = n.lastSon
   result = newNodeI(nkStmtList, n.info)
 
-  var temp = newSym(skLet, getIdent("_"), owner, value.info, owner.options)
+  var temp = newSym(skLet, getIdent(g.cache, "_"), owner, value.info, owner.options)
   var v = newNodeI(nkLetSection, value.info)
   let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
 
   var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
   vpart.sons[0] = tempAsNode
-  vpart.sons[1] = ast.emptyNode
+  vpart.sons[1] = newNodeI(nkEmpty, value.info)
   vpart.sons[2] = value
   addSon(v, vpart)
   result.add(v)
@@ -92,10 +93,10 @@ proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
   for i in 0 .. lhs.len-1:
     result.add newAsgnStmt(lhs.sons[i], newTupleAccessRaw(tempAsNode, i))
 
-proc lowerSwap*(n: PNode; owner: PSym): PNode =
+proc lowerSwap*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   result = newNodeI(nkStmtList, n.info)
   # note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
-  var temp = newSym(skVar, getIdent(genPrefix), owner, n.info, owner.options)
+  var temp = newSym(skVar, getIdent(g.cache, genPrefix), owner, n.info, owner.options)
   temp.typ = n.sons[1].typ
   incl(temp.flags, sfFromGeneric)
 
@@ -104,7 +105,7 @@ proc lowerSwap*(n: PNode; owner: PSym): PNode =
 
   var vpart = newNodeI(nkIdentDefs, v.info, 3)
   vpart.sons[0] = tempAsNode
-  vpart.sons[1] = ast.emptyNode
+  vpart.sons[1] = newNodeI(nkEmpty, v.info)
   vpart.sons[2] = n[1]
   addSon(v, vpart)
 
@@ -120,7 +121,7 @@ proc createObj*(g: ModuleGraph; owner: PSym, info: TLineInfo; final=true): PType
   else:
     rawAddSon(result, getCompilerProc(g, "RootObj").typ)
   result.n = newNodeI(nkRecList, info)
-  let s = newSym(skType, getIdent("Env_" & info.toFilename),
+  let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info)),
                   owner, info, owner.options)
   incl s.flags, sfAnon
   s.typ = result
@@ -171,10 +172,10 @@ proc lookupInRecord(n: PNode, id: int): PSym =
     if n.sym.id == -abs(id): result = n.sym
   else: discard
 
-proc addField*(obj: PType; s: PSym) =
+proc addField*(obj: PType; s: PSym; cache: IdentCache) =
   # because of 'gensym' support, we have to mangle the name with its ID.
   # This is hacky but the clean solution is much more complex than it looks.
-  var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info,
+  var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), s.owner, s.info,
                      s.options)
   field.id = -s.id
   let t = skipIntLit(s.typ)
@@ -183,10 +184,10 @@ proc addField*(obj: PType; s: PSym) =
   field.position = sonsLen(obj.n)
   addSon(obj.n, newSymNode(field))
 
-proc addUniqueField*(obj: PType; s: PSym): PSym {.discardable.} =
+proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache): PSym {.discardable.} =
   result = lookupInRecord(obj.n, s.id)
   if result == nil:
-    var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info,
+    var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), s.owner, s.info,
                        s.options)
     field.id = -s.id
     let t = skipIntLit(s.typ)
@@ -227,13 +228,13 @@ proc indirectAccess*(a: PNode, b: int, info: TLineInfo): PNode =
   addSon(result, newSymNode(field))
   result.typ = field.typ
 
-proc indirectAccess(a: PNode, b: string, info: TLineInfo): PNode =
+proc indirectAccess(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
   # returns a[].b as a node
   var deref = newNodeI(nkHiddenDeref, info)
   deref.typ = a.typ.skipTypes(abstractInst).sons[0]
   var t = deref.typ.skipTypes(abstractInst)
   var field: PSym
-  let bb = getIdent(b)
+  let bb = getIdent(cache, b)
   while true:
     assert t.kind == tyObject
     field = getSymFromList(t.n, bb)
@@ -335,17 +336,29 @@ proc typeNeedsNoDeepCopy(t: PType): bool =
   if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon
   result = not containsGarbageCollectedRef(t)
 
+proc hoistExpr*(varSection, expr: PNode, name: PIdent, owner: PSym): PSym =
+  result = newSym(skLet, name, owner, varSection.info, owner.options)
+  result.flags.incl sfHoisted
+  result.typ = expr.typ
+
+  var varDef = newNodeI(nkIdentDefs, varSection.info, 3)
+  varDef.sons[0] = newSymNode(result)
+  varDef.sons[1] = newNodeI(nkEmpty, varSection.info)
+  varDef.sons[2] = expr
+
+  varSection.add varDef
+
 proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType;
                  v: PNode; useShallowCopy=false): PSym =
-  result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info,
+  result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info,
                   owner.options)
   result.typ = typ
   incl(result.flags, sfFromGeneric)
 
   var vpart = newNodeI(nkIdentDefs, varSection.info, 3)
   vpart.sons[0] = newSymNode(result)
-  vpart.sons[1] = ast.emptyNode
-  vpart.sons[2] = if varInit.isNil: v else: ast.emptyNode
+  vpart.sons[1] = newNodeI(nkEmpty, varSection.info)
+  vpart.sons[2] = if varInit.isNil: v else: vpart[1]
   varSection.add vpart
   if varInit != nil:
     if useShallowCopy and typeNeedsNoDeepCopy(typ):
@@ -410,7 +423,7 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
     # generate:
     #   fv.owner = threadParam
     body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
-      "owner", fv.info), threadParam.newSymNode)
+      "owner", fv.info, g.cache), threadParam.newSymNode)
 
   body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.newSymNode)
   if spawnKind == srByVar:
@@ -421,12 +434,12 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
       localError(g.config, f.info, "cannot create a flowVar of type: " &
         typeToString(fv.typ.sons[1]))
     body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
-      if fk == fvGC: "data" else: "blob", fv.info), call)
+      if fk == fvGC: "data" else: "blob", fv.info, g.cache), call)
     if fk == fvGC:
       let incRefCall = newNodeI(nkCall, fv.info, 2)
       incRefCall.sons[0] = newSymNode(getSysMagic(g, fv.info, "GCref", mGCref))
       incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode,
-                                          "data", fv.info)
+                                          "data", fv.info, g.cache)
       body.add incRefCall
     if barrier == nil:
       # by now 'fv' is shared and thus might have beeen overwritten! we need
@@ -438,7 +451,7 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
     body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.newSymNode)
 
   var params = newNodeI(nkFormalParams, f.info)
-  params.add emptyNode
+  params.add newNodeI(nkEmpty, f.info)
   params.add threadParam.newSymNode
   params.add argsParam.newSymNode
 
@@ -452,14 +465,18 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
   t.n.add argsParam.newSymNode
 
   let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper"
-  result = newSym(skProc, getIdent(name), argsParam.owner, f.info,
+  result = newSym(skProc, getIdent(g.cache, name), argsParam.owner, f.info,
                   argsParam.options)
-  result.ast = newProcNode(nkProcDef, f.info, body, params, newSymNode(result))
+  let emptyNode = newNodeI(nkEmpty, f.info)
+  result.ast = newProcNode(nkProcDef, f.info, body = body,
+      params = params, name = newSymNode(result), pattern = emptyNode,
+      genericParams = emptyNode, pragmas = emptyNode,
+      exceptions = emptyNode)
   result.typ = t
 
 proc createCastExpr(argsParam: PSym; objType: PType): PNode =
   result = newNodeI(nkCast, argsParam.info)
-  result.add emptyNode
+  result.add newNodeI(nkEmpty, argsParam.info)
   result.add newSymNode(argsParam)
   result.typ = newType(tyPtr, objType.owner)
   result.typ.rawAddSon(objType)
@@ -468,7 +485,7 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchOb
                              castExpr, call,
                              varSection, varInit, result: PNode) =
   let formals = n[0].typ.n
-  let tmpName = getIdent(genPrefix)
+  let tmpName = getIdent(g.cache, genPrefix)
   for i in 1 ..< n.len:
     # we pick n's type here, which hopefully is 'tyArray' and not
     # 'tyOpenArray':
@@ -481,7 +498,7 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchOb
     let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
     var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
     field.typ = argType
-    objType.addField(field)
+    objType.addField(field, g.cache)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
 
     let temp = addLocalVar(g, varSection, varInit, objType.owner, argType,
@@ -511,7 +528,7 @@ proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
 
 proc genHigh*(g: ModuleGraph; n: PNode): PNode =
   if skipTypes(n.typ, abstractVar).kind == tyArray:
-    result = newIntLit(g, n.info, lastOrd(skipTypes(n.typ, abstractVar)))
+    result = newIntLit(g, n.info, lastOrd(g.config, skipTypes(n.typ, abstractVar)))
   else:
     result = newNodeI(nkCall, n.info, 2)
     result.typ = getSysType(g, n.info, tyInt)
@@ -522,7 +539,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb
                              castExpr, call,
                              varSection, varInit, result: PNode) =
   let formals = n[0].typ.n
-  let tmpName = getIdent(genPrefix)
+  let tmpName = getIdent(g.cache, genPrefix)
   # we need to copy the foreign scratch object fields into local variables
   # for correctness: These are called 'threadLocal' here.
   for i in 1 ..< n.len:
@@ -543,17 +560,17 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb
       slice.sons[0].typ = getSysType(g, n.info, tyInt) # fake type
       var fieldB = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
       fieldB.typ = getSysType(g, n.info, tyInt)
-      objType.addField(fieldB)
+      objType.addField(fieldB, g.cache)
 
       if getMagic(n) == mSlice:
         let a = genAddrOf(n[1])
         field.typ = a.typ
-        objType.addField(field)
+        objType.addField(field, g.cache)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
 
         var fieldA = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
         fieldA.typ = getSysType(g, n.info, tyInt)
-        objType.addField(fieldA)
+        objType.addField(fieldA, g.cache)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
         result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
 
@@ -564,7 +581,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb
       else:
         let a = genAddrOf(n)
         field.typ = a.typ
-        objType.addField(field)
+        objType.addField(field, g.cache)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n))
 
@@ -577,12 +594,12 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb
                                     useShallowCopy=true)
       slice.sons[3] = threadLocal.newSymNode
       call.add slice
-    elif (let size = computeSize(argType); size < 0 or size > 16) and
+    elif (let size = computeSize(g.config, argType); size < 0 or size > 16) and
         n.getRoot != nil:
       # it is more efficient to pass a pointer instead:
       let a = genAddrOf(n)
       field.typ = a.typ
-      objType.addField(field)
+      objType.addField(field, g.cache)
       result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
       let threadLocal = addLocalVar(g, varSection,nil, objType.owner, field.typ,
                                     indirectAccess(castExpr, field, n.info),
@@ -591,7 +608,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb
     else:
       # boring case
       field.typ = argType
-      objType.addField(field)
+      objType.addField(field, g.cache)
       result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
       let threadLocal = addLocalVar(g, varSection, varInit,
                                     objType.owner, field.typ,
@@ -623,8 +640,8 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
     if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
       localError(g.config, n.info, "'spawn' takes a GC safe call expression")
   var
-    threadParam = newSym(skParam, getIdent"thread", owner, n.info, g.config.options)
-    argsParam = newSym(skParam, getIdent"args", owner, n.info, g.config.options)
+    threadParam = newSym(skParam, getIdent(g.cache, "thread"), owner, n.info, g.config.options)
+    argsParam = newSym(skParam, getIdent(g.cache, "args"), owner, n.info, g.config.options)
   block:
     let ptrType = getSysType(g, n.info, tyPointer)
     threadParam.typ = ptrType
@@ -635,7 +652,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
   incl(objType.flags, tfFinal)
   let castExpr = createCastExpr(argsParam, objType)
 
-  var scratchObj = newSym(skVar, getIdent"scratch", owner, n.info, g.config.options)
+  var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), owner, n.info, g.config.options)
   block:
     scratchObj.typ = objType
     incl(scratchObj.flags, sfFromGeneric)
@@ -653,9 +670,9 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
                                                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, g.config.options)
+    var field = newSym(skField, getIdent(g.cache, "fn"), owner, n.info, g.config.options)
     field.typ = argType
-    objType.addField(field)
+    objType.addField(field, g.cache)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
     fn = indirectAccess(castExpr, field, n.info)
   elif fn.kind == nkSym and fn.sym.kind == skIterator:
@@ -677,17 +694,17 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
   if barrier != nil:
     let typ = newType(tyPtr, owner)
     typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ)
-    var field = newSym(skField, getIdent"barrier", owner, n.info, g.config.options)
+    var field = newSym(skField, getIdent(g.cache, "barrier"), owner, n.info, g.config.options)
     field.typ = typ
-    objType.addField(field)
+    objType.addField(field, g.cache)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
     barrierAsExpr = indirectAccess(castExpr, field, n.info)
 
   var fvField, fvAsExpr: PNode = nil
   if spawnKind == srFlowVar:
-    var field = newSym(skField, getIdent"fv", owner, n.info, g.config.options)
+    var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
     field.typ = retType
-    objType.addField(field)
+    objType.addField(field, g.cache)
     fvField = newDotExpr(scratchObj, field)
     fvAsExpr = indirectAccess(castExpr, field, n.info)
     # create flowVar:
@@ -696,10 +713,10 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
       result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField)
 
   elif spawnKind == srByVar:
-    var field = newSym(skField, getIdent"fv", owner, n.info, g.config.options)
+    var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
     field.typ = newType(tyPtr, objType.owner)
     field.typ.rawAddSon(retType)
-    objType.addField(field)
+    objType.addField(field, g.cache)
     fvAsExpr = indirectAccess(castExpr, field, n.info)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
 
diff --git a/compiler/macrocacheimpl.nim b/compiler/macrocacheimpl.nim
new file mode 100644
index 000000000..d23040763
--- /dev/null
+++ b/compiler/macrocacheimpl.nim
@@ -0,0 +1,79 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements helpers for the macro cache.
+
+import lineinfos, ast, modulegraphs, vmdef, magicsys
+
+proc recordInc*(c: PCtx; info: TLineInfo; key: string; by: BiggestInt) =
+  var recorded = newNodeI(nkCommentStmt, info)
+  recorded.add newStrNode("inc", info)
+  recorded.add newStrNode(key, info)
+  recorded.add newIntNode(nkIntLit, by)
+  c.graph.recordStmt(c.graph, c.module, recorded)
+
+proc recordPut*(c: PCtx; info: TLineInfo; key: string; k: string; val: PNode) =
+  var recorded = newNodeI(nkCommentStmt, info)
+  recorded.add newStrNode("put", info)
+  recorded.add newStrNode(key, info)
+  recorded.add newStrNode(k, info)
+  recorded.add copyTree(val)
+  c.graph.recordStmt(c.graph, c.module, recorded)
+
+proc recordAdd*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
+  var recorded = newNodeI(nkCommentStmt, info)
+  recorded.add newStrNode("add", info)
+  recorded.add newStrNode(key, info)
+  recorded.add copyTree(val)
+  c.graph.recordStmt(c.graph, c.module, recorded)
+
+proc recordIncl*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
+  var recorded = newNodeI(nkCommentStmt, info)
+  recorded.add newStrNode("incl", info)
+  recorded.add newStrNode(key, info)
+  recorded.add copyTree(val)
+  c.graph.recordStmt(c.graph, c.module, recorded)
+
+when false:
+  proc genCall3(g: ModuleGraph; m: TMagic; s: string; a, b, c: PNode): PNode =
+    newTree(nkStaticStmt, newTree(nkCall, createMagic(g, s, m).newSymNode, a, b, c))
+
+  proc genCall2(g: ModuleGraph; m: TMagic; s: string; a, b: PNode): PNode =
+    newTree(nkStaticStmt, newTree(nkCall, createMagic(g, s, m).newSymNode, a, b))
+
+  template nodeFrom(s: string): PNode =
+    var res = newStrNode(s, info)
+    res.typ = getSysType(g, info, tyString)
+    res
+
+  template nodeFrom(i: BiggestInt): PNode =
+    var res = newIntNode(i, info)
+    res.typ = getSysType(g, info, tyInt)
+    res
+
+  template nodeFrom(n: PNode): PNode = copyTree(n)
+
+  template record(call) =
+    g.recordStmt(g, c.module, call)
+
+  proc recordInc*(c: PCtx; info: TLineInfo; key: string; by: BiggestInt) =
+    let g = c.graph
+    record genCall2(mNccInc, "inc", nodeFrom key, nodeFrom by)
+
+  proc recordPut*(c: PCtx; info: TLineInfo; key: string; k: string; val: PNode) =
+    let g = c.graph
+    record genCall3(mNctPut, "[]=", nodeFrom key, nodeFrom k, nodeFrom val)
+
+  proc recordAdd*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
+    let g = c.graph
+    record genCall2(mNcsAdd, "add", nodeFrom key, nodeFrom val)
+
+  proc recordIncl*(c: PCtx; info: TLineInfo; key: string; val: PNode) =
+    let g = c.graph
+    record genCall2(mNcsIncl, "incl", nodeFrom key, nodeFrom val)
diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim
index b5577d961..d40b9d732 100644
--- a/compiler/magicsys.nim
+++ b/compiler/magicsys.nim
@@ -10,8 +10,8 @@
 # Built-in types and compilerprocs are registered here.
 
 import
-  ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread,
-  modulegraphs
+  ast, astalgo, hashes, msgs, platform, nversion, times, idents,
+  modulegraphs, lineinfos
 
 export createMagic
 
@@ -26,30 +26,18 @@ proc newSysType(g: ModuleGraph; kind: TTypeKind, size: int): PType =
   result.align = size.int16
 
 proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym =
-  result = strTableGet(g.systemModule.tab, getIdent(name))
+  result = strTableGet(g.systemModule.tab, getIdent(g.cache, name))
   if result == nil:
     localError(g.config, info, "system module needs: " & name)
-    result = newSym(skError, getIdent(name), g.systemModule, g.systemModule.info, {})
+    result = newSym(skError, getIdent(g.cache, name), g.systemModule, g.systemModule.info, {})
     result.typ = newType(tyError, g.systemModule)
-  if result.kind == skStub: loadStub(result)
   if result.kind == skAlias: result = result.owner
 
-when false:
-  proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
-    result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
-    result.magic = m
-
-when false:
-  let
-    opNot* = createMagic("not", mNot)
-    opContains* = createMagic("contains", mInSet)
-
 proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym =
   var ti: TIdentIter
-  let id = getIdent(name)
+  let id = getIdent(g.cache, name)
   var r = initIdentIter(ti, g.systemModule.tab, id)
   while r != nil:
-    if r.kind == skStub: loadStub(r)
     if r.magic == m:
       # prefer the tyInt variant:
       if r.typ.sons[0] != nil and r.typ.sons[0].kind == tyInt: return r
@@ -87,7 +75,7 @@ proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType =
     of tyString: result = sysTypeFromName("string")
     of tyCString: result = sysTypeFromName("cstring")
     of tyPointer: result = sysTypeFromName("pointer")
-    of tyNil: result = newSysType(g, tyNil, ptrSize)
+    of tyNil: result = newSysType(g, tyNil, g.config.target.ptrSize)
     else: internalError(g.config, "request for typekind: " & $kind)
     g.sysTypes[kind] = result
   if result.kind != kind:
@@ -139,7 +127,7 @@ proc addSonSkipIntLit*(father, son: PType) =
 
 proc setIntLitType*(g: ModuleGraph; result: PNode) =
   let i = result.intVal
-  case platform.intSize
+  case g.config.target.intSize
   of 8: result.typ = getIntLitType(g, result)
   of 4:
     if i >= low(int32) and i <= high(int32):
@@ -167,15 +155,8 @@ proc setIntLitType*(g: ModuleGraph; result: PNode) =
     internalError(g.config, result.info, "invalid int size")
 
 proc getCompilerProc*(g: ModuleGraph; name: string): PSym =
-  let ident = getIdent(name)
+  let ident = getIdent(g.cache, name)
   result = strTableGet(g.compilerprocs, ident)
-  when false:
-    if result == nil:
-      result = strTableGet(g.rodCompilerprocs, ident)
-      if result != nil:
-        strTableAdd(g.compilerprocs, result)
-        if result.kind == skStub: loadStub(result)
-        if result.kind == skAlias: result = result.owner
 
 proc registerCompilerProc*(g: ModuleGraph; s: PSym) =
   strTableAdd(g.compilerprocs, s)
@@ -187,9 +168,9 @@ proc registerNimScriptSymbol*(g: ModuleGraph; s: PSym) =
     strTableAdd(g.exposed, s)
   else:
     localError(g.config, s.info,
-      "symbol conflicts with other .exportNims symbol at: " & $conflict.info)
+      "symbol conflicts with other .exportNims symbol at: " & g.config$conflict.info)
 
 proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym =
-  strTableGet(g.exposed, getIdent(name))
+  strTableGet(g.exposed, getIdent(g.cache, name))
 
 proc resetNimScriptSymbols*(g: ModuleGraph) = initStrTable(g.exposed)
diff --git a/compiler/main.nim b/compiler/main.nim
index e3f00db9e..cd05ded62 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -9,74 +9,68 @@
 
 # implements the command dispatcher and several commands
 
+when not defined(nimcore):
+  {.error: "nimcore MUST be defined for Nim's core tooling".}
+
 import
   llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs,
-  os, condsyms, rodread, rodwrite, times,
+  os, condsyms, times,
   wordrecg, sem, semdata, idents, passes, docgen, extccomp,
   cgen, jsgen, json, nversion,
   platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
-  docgen2, service, parser, modules, ccgutils, sigmatch, ropes,
-  modulegraphs, tables, rod, configuration
+  docgen2, parser, modules, ccgutils, sigmatch, ropes,
+  modulegraphs, tables, rod, lineinfos
 
 from magicsys import resetSysTypes
 
-proc rodPass(g: ModuleGraph) =
-  if g.config.symbolFiles in {enabledSf, writeOnlySf}:
-    registerPass(rodwritePass)
-
-proc codegenPass =
-  registerPass cgenPass
+proc codegenPass(g: ModuleGraph) =
+  registerPass g, cgenPass
 
-proc semanticPasses =
-  registerPass verbosePass
-  registerPass semPass
+proc semanticPasses(g: ModuleGraph) =
+  registerPass g, verbosePass
+  registerPass g, semPass
 
 proc writeDepsFile(g: ModuleGraph; project: string) =
   let f = open(changeFileExt(project, "deps"), fmWrite)
   for m in g.modules:
     if m != nil:
-      f.writeLine(toFullPath(m.position.FileIndex))
+      f.writeLine(toFullPath(g.config, m.position.FileIndex))
   for k in g.inclToMod.keys:
     if g.getModule(k).isNil:  # don't repeat includes which are also modules
-      f.writeLine(k.toFullPath)
+      f.writeLine(toFullPath(g.config, k))
   f.close()
 
-proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) =
-  semanticPasses()
-  registerPass(gendependPass)
-  #registerPass(cleanupPass)
-  compileProject(graph, cache)
+proc commandGenDepend(graph: ModuleGraph) =
+  semanticPasses(graph)
+  registerPass(graph, gendependPass)
+  compileProject(graph)
   let project = graph.config.projectFull
   writeDepsFile(graph, project)
-  generateDot(project)
+  generateDot(graph, project)
   execExternalProgram(graph.config, "dot -Tpng -o" & changeFileExt(project, "png") &
       ' ' & changeFileExt(project, "dot"))
 
-proc commandCheck(graph: ModuleGraph; cache: IdentCache) =
+proc commandCheck(graph: ModuleGraph) =
   graph.config.errorMax = high(int)  # do not stop after first error
   defineSymbol(graph.config.symbols, "nimcheck")
-  semanticPasses()            # use an empty backend for semantic checking only
-  rodPass(graph)
-  compileProject(graph, cache)
+  semanticPasses(graph)  # use an empty backend for semantic checking only
+  compileProject(graph)
 
-proc commandDoc2(graph: ModuleGraph; cache: IdentCache; json: bool) =
+proc commandDoc2(graph: ModuleGraph; json: bool) =
   graph.config.errorMax = high(int)  # do not stop after first error
-  semanticPasses()
-  if json: registerPass(docgen2JsonPass)
-  else: registerPass(docgen2Pass)
-  #registerPass(cleanupPass())
-  compileProject(graph, cache)
+  semanticPasses(graph)
+  if json: registerPass(graph, docgen2JsonPass)
+  else: registerPass(graph, docgen2Pass)
+  compileProject(graph)
   finishDoc2Pass(graph.config.projectName)
 
-proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) =
+proc commandCompileToC(graph: ModuleGraph) =
   let conf = graph.config
   extccomp.initVars(conf)
-  semanticPasses()
-  registerPass(cgenPass)
-  rodPass(graph)
-  #registerPass(cleanupPass())
+  semanticPasses(graph)
+  registerPass(graph, cgenPass)
 
-  compileProject(graph, cache)
+  compileProject(graph)
   cgenWriteModules(graph.backend, conf)
   if conf.cmd != cmdRun:
     let proj = changeFileExt(conf.projectFull, "")
@@ -85,53 +79,51 @@ proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) =
     if optGenScript in graph.config.globalOptions:
       writeDepsFile(graph, toGeneratedFile(conf, proj, ""))
 
-proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) =
+proc commandJsonScript(graph: ModuleGraph) =
   let proj = changeFileExt(graph.config.projectFull, "")
   extccomp.runJsonBuildInstructions(graph.config, proj)
 
-proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) =
+proc commandCompileToJS(graph: ModuleGraph) =
   #incl(gGlobalOptions, optSafeCode)
-  setTarget(osJS, cpuJS)
+  setTarget(graph.config.target, osJS, cpuJS)
   #initDefines()
   defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility
   defineSymbol(graph.config.symbols, "js")
-  semanticPasses()
-  registerPass(JSgenPass)
-  compileProject(graph, cache)
+  semanticPasses(graph)
+  registerPass(graph, JSgenPass)
+  compileProject(graph)
 
-proc interactivePasses(graph: ModuleGraph; cache: IdentCache) =
-  #incl(gGlobalOptions, optSafeCode)
-  #setTarget(osNimrodVM, cpuNimrodVM)
+proc interactivePasses(graph: ModuleGraph) =
   initDefines(graph.config.symbols)
   defineSymbol(graph.config.symbols, "nimscript")
   when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
-  registerPass(verbosePass)
-  registerPass(semPass)
-  registerPass(evalPass)
+  registerPass(graph, verbosePass)
+  registerPass(graph, semPass)
+  registerPass(graph, evalPass)
 
-proc commandInteractive(graph: ModuleGraph; cache: IdentCache) =
+proc commandInteractive(graph: ModuleGraph) =
   graph.config.errorMax = high(int)  # do not stop after first error
-  interactivePasses(graph, cache)
-  compileSystemModule(graph, cache)
+  interactivePasses(graph)
+  compileSystemModule(graph)
   if graph.config.commandArgs.len > 0:
-    discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), cache, {})
+    discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
   else:
     var m = graph.makeStdinModule()
     incl(m.flags, sfMainModule)
-    processModule(graph, m, llStreamOpenStdIn(), nil, cache)
+    processModule(graph, m, llStreamOpenStdIn())
 
 const evalPasses = [verbosePass, semPass, evalPass]
 
-proc evalNim(graph: ModuleGraph; nodes: PNode, module: PSym; cache: IdentCache) =
-  carryPasses(graph, nodes, module, cache, evalPasses)
+proc evalNim(graph: ModuleGraph; nodes: PNode, module: PSym) =
+  carryPasses(graph, nodes, module, evalPasses)
 
-proc commandEval(graph: ModuleGraph; cache: IdentCache; exp: string) =
+proc commandEval(graph: ModuleGraph; exp: string) =
   if graph.systemModule == nil:
-    interactivePasses(graph, cache)
-    compileSystemModule(graph, cache)
+    interactivePasses(graph)
+    compileSystemModule(graph)
   let echoExp = "echo \"eval\\t\", " & "repr(" & exp & ")"
-  evalNim(graph, echoExp.parseString(cache, graph.config),
-    makeStdinModule(graph), cache)
+  evalNim(graph, echoExp.parseString(graph.cache, graph.config),
+    makeStdinModule(graph))
 
 proc commandScan(cache: IdentCache, config: ConfigRef) =
   var f = addFileExt(mainCommandArg(config), NimExt)
@@ -153,12 +145,13 @@ proc commandScan(cache: IdentCache, config: ConfigRef) =
 const
   PrintRopeCacheStats = false
 
-proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
+proc mainCommand*(graph: ModuleGraph) =
   let conf = graph.config
+  let cache = graph.cache
 
-  setupModuleCache()
+  setupModuleCache(graph)
   # In "nim serve" scenario, each command must reset the registered passes
-  clearPasses()
+  clearPasses(graph)
   conf.lastCmdTime = epochTime()
   conf.searchPaths.add(conf.libpath)
   setId(100)
@@ -166,69 +159,69 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
   of "c", "cc", "compile", "compiletoc":
     # compile means compileToC currently
     conf.cmd = cmdCompileToC
-    commandCompileToC(graph, cache)
+    commandCompileToC(graph)
   of "cpp", "compiletocpp":
     conf.cmd = cmdCompileToCpp
     defineSymbol(graph.config.symbols, "cpp")
-    commandCompileToC(graph, cache)
+    commandCompileToC(graph)
   of "objc", "compiletooc":
     conf.cmd = cmdCompileToOC
     defineSymbol(graph.config.symbols, "objc")
-    commandCompileToC(graph, cache)
+    commandCompileToC(graph)
   of "run":
     conf.cmd = cmdRun
     when hasTinyCBackend:
       extccomp.setCC("tcc")
-      commandCompileToC(graph, cache)
+      commandCompileToC(graph)
     else:
       rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc")
   of "js", "compiletojs":
     conf.cmd = cmdCompileToJS
-    commandCompileToJS(graph, cache)
+    commandCompileToJS(graph)
   of "doc0":
     wantMainModule(conf)
     conf.cmd = cmdDoc
     loadConfigs(DocConfig, cache, conf)
-    commandDoc(conf)
+    commandDoc(cache, conf)
   of "doc2", "doc":
     conf.cmd = cmdDoc
     loadConfigs(DocConfig, cache, conf)
     defineSymbol(conf.symbols, "nimdoc")
-    commandDoc2(graph, cache, false)
+    commandDoc2(graph, false)
   of "rst2html":
     conf.cmd = cmdRst2html
     loadConfigs(DocConfig, cache, conf)
-    commandRst2Html(conf)
+    commandRst2Html(cache, conf)
   of "rst2tex":
     conf.cmd = cmdRst2tex
     loadConfigs(DocTexConfig, cache, conf)
-    commandRst2TeX(conf)
+    commandRst2TeX(cache, conf)
   of "jsondoc0":
     wantMainModule(conf)
     conf.cmd = cmdDoc
     loadConfigs(DocConfig, cache, conf)
     wantMainModule(conf)
     defineSymbol(conf.symbols, "nimdoc")
-    commandJson(conf)
+    commandJson(cache, conf)
   of "jsondoc2", "jsondoc":
     conf.cmd = cmdDoc
     loadConfigs(DocConfig, cache, conf)
     wantMainModule(conf)
     defineSymbol(conf.symbols, "nimdoc")
-    commandDoc2(graph, cache, true)
+    commandDoc2(graph, true)
   of "ctags":
     wantMainModule(conf)
     conf.cmd = cmdDoc
     loadConfigs(DocConfig, cache, conf)
     defineSymbol(conf.symbols, "nimdoc")
-    commandTags(conf)
+    commandTags(cache, conf)
   of "buildindex":
     conf.cmd = cmdDoc
     loadConfigs(DocConfig, cache, conf)
-    commandBuildIndex(conf)
+    commandBuildIndex(cache, conf)
   of "gendepend":
     conf.cmd = cmdGenDepend
-    commandGenDepend(graph, cache)
+    commandGenDepend(graph)
   of "dump":
     conf.cmd = cmdDump
     if getConfigVar(conf, "dump.format") == "json":
@@ -257,11 +250,11 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
       for it in conf.searchPaths: msgWriteln(conf, it)
   of "check":
     conf.cmd = cmdCheck
-    commandCheck(graph, cache)
+    commandCheck(graph)
   of "parse":
     conf.cmd = cmdParse
     wantMainModule(conf)
-    discard parseFile(FileIndex conf.projectMainIdx, cache, conf)
+    discard parseFile(conf.projectMainIdx, cache, conf)
   of "scan":
     conf.cmd = cmdScan
     wantMainModule(conf)
@@ -269,15 +262,15 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
     msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
   of "secret":
     conf.cmd = cmdInteractive
-    commandInteractive(graph, cache)
+    commandInteractive(graph)
   of "e":
-    commandEval(graph, cache, mainCommandArg(conf))
+    commandEval(graph, mainCommandArg(conf))
   of "nop", "help":
     # prevent the "success" message:
     conf.cmd = cmdDump
   of "jsonscript":
     conf.cmd = cmdJsonScript
-    commandJsonScript(graph, cache)
+    commandJsonScript(graph)
   else:
     rawMessage(conf, errGenerated, "invalid command: " & conf.command)
 
@@ -302,5 +295,3 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
                                        ffDecimal, 3)
 
   resetAttributes(conf)
-
-#proc mainCommand*() = mainCommand(newModuleGraph(newConfigRef()), newIdentCache())
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 460d0b4a5..334cd1ae6 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -9,7 +9,7 @@
 
 ## This module implements the module graph data structure. The module graph
 ## represents a complete Nim project. Single modules can either be kept in RAM
-## or stored in a ROD file. The ROD file mechanism is not yet integrated here.
+## or stored in a Sqlite database.
 ##
 ## The caching of modules is critical for 'nimsuggest' and is tricky to get
 ## right. If module E is being edited, we need autocompletion (and type
@@ -25,7 +25,8 @@
 ## - Its dependent module stays the same.
 ##
 
-import ast, intsets, tables, options, rod, msgs, hashes, idents
+import ast, intsets, tables, options, lineinfos, hashes, idents,
+  incremental, btrees
 
 type
   ModuleGraph* = ref object
@@ -40,16 +41,27 @@ type
                                   # module dependencies.
     backend*: RootRef # minor hack so that a backend can extend this easily
     config*: ConfigRef
+    cache*: IdentCache
+    vm*: RootRef # unfortunately the 'vm' state is shared project-wise, this will
+                 # be clarified in later compiler implementations.
     doStopCompile*: proc(): bool {.closure.}
     usageSym*: PSym # for nimsuggest
     owners*: seq[PSym]
-    methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]]
+    methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]] # needs serialization!
     systemModule*: PSym
     sysTypes*: array[TTypeKind, PType]
     compilerprocs*: TStrTable
     exposed*: TStrTable
     intTypeCache*: array[-5..64, PType]
     opContains*, opNot*: PSym
+    emptyNode*: PNode
+    incr*: IncrementalCtx
+    importModuleCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PSym {.nimcall.}
+    includeFileCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PNode {.nimcall.}
+    recordStmt*: proc (graph: ModuleGraph; m: PSym; n: PNode) {.nimcall.}
+    cacheSeqs*: Table[string, PNode] # state that is shared to suppor the 'macrocache' API
+    cacheCounters*: Table[string, BiggestInt]
+    cacheTables*: Table[string, BTree[string, PNode]]
 
 proc hash*(x: FileIndex): Hash {.borrow.}
 
@@ -59,26 +71,31 @@ proc stopCompile*(g: ModuleGraph): bool {.inline.} =
   result = doStopCompile != nil and doStopCompile()
 
 proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
-  result = newSym(skProc, getIdent(name), nil, unknownLineInfo(), {})
+  result = newSym(skProc, getIdent(g.cache, name), nil, unknownLineInfo(), {})
   result.magic = m
 
-proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph =
+proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
   result = ModuleGraph()
   initStrTable(result.packageSyms)
   result.deps = initIntSet()
   result.modules = @[]
   result.importStack = @[]
   result.inclToMod = initTable[FileIndex, FileIndex]()
-  if config.isNil:
-    result.config = newConfigRef()
-  else:
-    result.config = config
+  result.config = config
+  result.cache = cache
   result.owners = @[]
   result.methods = @[]
   initStrTable(result.compilerprocs)
   initStrTable(result.exposed)
   result.opNot = createMagic(result, "not", mNot)
   result.opContains = createMagic(result, "contains", mInSet)
+  result.emptyNode = newNode(nkEmpty)
+  init(result.incr)
+  result.recordStmt = proc (graph: ModuleGraph; m: PSym; n: PNode) {.nimcall.} =
+    discard
+  result.cacheSeqs = initTable[string, PNode]()
+  result.cacheCounters = initTable[string, BiggestInt]()
+  result.cacheTables = initTable[string, BTree[string, PNode]]()
 
 proc resetAllModules*(g: ModuleGraph) =
   initStrTable(packageSyms)
@@ -100,15 +117,15 @@ proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
 
 proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
   assert m.position == m.info.fileIndex.int32
-  addModuleDep(m.info.fileIndex, dep, isIncludeFile = false)
+  addModuleDep(g.incr, g.config, m.info.fileIndex, dep, isIncludeFile = false)
   if suggestMode:
     deps.incl m.position.dependsOn(dep.int)
     # we compute the transitive closure later when quering the graph lazily.
-    # this improve efficiency quite a lot:
+    # this improves efficiency quite a lot:
     #invalidTransitiveClosure = true
 
 proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) =
-  addModuleDep(module, includeFile, isIncludeFile = true)
+  addModuleDep(g.incr, g.config, module, includeFile, isIncludeFile = true)
   discard hasKeyOrPut(inclToMod, includeFile, module)
 
 proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex =
diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim
index daa55c2ba..87c7f3541 100644
--- a/compiler/modulepaths.nim
+++ b/compiler/modulepaths.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-import ast, renderer, strutils, msgs, options, idents, os
+import ast, renderer, strutils, msgs, options, idents, os, lineinfos
 
 import nimblecmd
 
@@ -120,7 +120,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
   case n.kind
   of nkStrLit, nkRStrLit, nkTripleStrLit:
     try:
-      result = pathSubs(conf, n.strVal, n.info.toFullPath().splitFile().dir)
+      result = pathSubs(conf, n.strVal, toFullPath(conf, n.info).splitFile().dir)
     except ValueError:
       localError(conf, n.info, "invalid path: " & n.strVal)
       result = n.strVal
@@ -131,7 +131,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
   of nkInfix:
     let n0 = n[0]
     let n1 = n[1]
-    if n0.kind == nkIdent and n0.ident.id == getIdent("as").id:
+    if n0.kind == nkIdent and n0.ident.s == "as":
       # XXX hack ahead:
       n.kind = nkImportAs
       n.sons[0] = n.sons[1]
@@ -177,7 +177,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
 proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
   # This returns the full canonical path for a given module import
   let modulename = getModuleName(conf, n)
-  let fullPath = findModule(conf, modulename, n.info.toFullPath)
+  let fullPath = findModule(conf, modulename, toFullPath(conf, n.info))
   if fullPath.len == 0:
     if doLocalError:
       let m = if modulename.len > 0: modulename else: $n
diff --git a/compiler/modules.nim b/compiler/modules.nim
index 5d1eba1f2..b3a1e90d6 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -10,9 +10,9 @@
 ## Implements the module handling, including the caching of modules.
 
 import
-  ast, astalgo, magicsys, std / sha1, rodread, msgs, cgendata, sigmatch, options,
+  ast, astalgo, magicsys, std / sha1, msgs, cgendata, sigmatch, options,
   idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod,
-  configuration
+  lineinfos
 
 proc resetSystemArtifacts*(g: ModuleGraph) =
   magicsys.resetSysTypes(g)
@@ -23,8 +23,8 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
   new(result)
   result.id = -1             # for better error checking
   result.kind = skModule
-  let filename = fileIdx.toFullPath
-  result.name = getIdent(splitFile(filename).name)
+  let filename = toFullPath(graph.config, fileIdx)
+  result.name = getIdent(graph.cache, splitFile(filename).name)
   if not isNimIdentifier(result.name.s):
     rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s)
 
@@ -32,17 +32,19 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
   let
     pck = getPackageName(graph.config, filename)
     pck2 = if pck.len > 0: pck else: "unknown"
-    pack = getIdent(pck2)
+    pack = getIdent(graph.cache, pck2)
   var packSym = graph.packageSyms.strTableGet(pack)
   if packSym == nil:
-    packSym = newSym(skPackage, getIdent(pck2), nil, result.info)
+    packSym = newSym(skPackage, getIdent(graph.cache, pck2), nil, result.info)
     initStrTable(packSym.tab)
     graph.packageSyms.strTableAdd(packSym)
 
   result.owner = packSym
   result.position = int fileIdx
 
-  growCache graph.modules, int fileIdx
+  if int(fileIdx) >= graph.modules.len:
+    setLen(graph.modules, int(fileIdx) + 1)
+  #growCache graph.modules, int fileIdx
   graph.modules[result.position] = result
 
   incl(result.flags, sfUsed)
@@ -50,95 +52,76 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
   strTableAdd(result.tab, result) # a module knows itself
   let existing = strTableGet(packSym.tab, result.name)
   if existing != nil and existing.info.fileIndex != result.info.fileIndex:
-    localError(graph.config, result.info, "module names need to be unique per Nimble package; module clashes with " & existing.info.fileIndex.toFullPath)
+    localError(graph.config, result.info,
+      "module names need to be unique per Nimble package; module clashes with " &
+        toFullPath(graph.config, existing.info.fileIndex))
   # strTableIncl() for error corrections:
   discard strTableIncl(packSym.tab, result)
 
-proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; cache: IdentCache, flags: TSymFlags): PSym =
+proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym =
   result = graph.getModule(fileIdx)
   if result == nil:
-    #growCache gMemCacheData, fileIdx
-    #gMemCacheData[fileIdx].needsRecompile = Probing
     result = newModule(graph, fileIdx)
-    var rd: PRodReader
     result.flags = result.flags + flags
     if sfMainModule in result.flags:
-      gMainPackageId = result.owner.id
-
-    when false:
-      if conf.cmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}:
-        rd = handleSymbolFile(result, cache)
-        if result.id < 0:
-          internalError("handleSymbolFile should have set the module's ID")
-          return
-      else:
-        discard
-    result.id = getModuleId(fileIdx, toFullPath(fileIdx))
+      graph.config.mainPackageId = result.owner.id
+
+    result.id = getModuleId(graph, fileIdx, toFullPath(graph.config, fileIdx))
     discard processModule(graph, result,
-      if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil,
-      rd, cache)
-    #if optCaasEnabled in gGlobalOptions:
-    #  gMemCacheData[fileIdx].needsRecompile = Recompiled
-    #  if validFile: doHash fileIdx
+      if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil)
   elif graph.isDirty(result):
     result.flags.excl sfDirty
     # reset module fields:
     initStrTable(result.tab)
     result.ast = nil
     discard processModule(graph, result,
-      if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil,
-      nil, cache)
+      if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil)
     graph.markClientsDirty(fileIdx)
-    when false:
-      if checkDepMem(fileIdx) == Yes:
-        result = compileModule(fileIdx, cache, flags)
-      else:
-        result = gCompiledModules[fileIdx]
-
-proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
-                   cache: IdentCache): PSym {.procvar.} =
+
+proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym {.procvar.} =
   # this is called by the semantic checking phase
   assert graph.config != nil
-  result = compileModule(graph, fileIdx, cache, {})
+  result = compileModule(graph, fileIdx, {})
   graph.addDep(s, fileIdx)
   #if sfSystemModule in result.flags:
   #  localError(result.info, errAttemptToRedefine, result.name.s)
   # restore the notes for outer module:
   graph.config.notes =
-      if s.owner.id == gMainPackageId: graph.config.mainPackageNotes
-      else: graph.config.foreignPackageNotes
+    if s.owner.id == graph.config.mainPackageId: graph.config.mainPackageNotes
+    else: graph.config.foreignPackageNotes
 
-proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
-                    cache: IdentCache): PNode {.procvar.} =
-  result = syntaxes.parseFile(fileIdx, cache, graph.config)
+proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode {.procvar.} =
+  result = syntaxes.parseFile(fileIdx, graph.cache, graph.config)
   graph.addDep(s, fileIdx)
   graph.addIncludeDep(s.position.FileIndex, fileIdx)
 
-proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) =
+proc connectCallbacks*(graph: ModuleGraph) =
+  graph.includeFileCallback = includeModule
+  graph.importModuleCallback = importModule
+
+proc compileSystemModule*(graph: ModuleGraph) =
   if graph.systemModule == nil:
-    systemFileIdx = fileInfoIdx(graph.config, graph.config.libpath / "system.nim")
-    discard graph.compileModule(systemFileIdx, cache, {sfSystemModule})
+    connectCallbacks(graph)
+    graph.config.m.systemFileIdx = fileInfoIdx(graph.config, graph.config.libpath / "system.nim")
+    discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule})
 
 proc wantMainModule*(conf: ConfigRef) =
   if conf.projectFull.len == 0:
     fatal(conf, newLineInfo(conf, "command line", 1, 1), errGenerated, "command expects a filename")
-  conf.projectMainIdx = int32 fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))
-
-passes.gIncludeFile = includeModule
-passes.gImportModule = importModule
+  conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))
 
-proc compileProject*(graph: ModuleGraph; cache: IdentCache;
-                     projectFileIdx = InvalidFileIDX) =
+proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIDX) =
+  connectCallbacks(graph)
   let conf = graph.config
   wantMainModule(conf)
   let systemFileIdx = fileInfoIdx(conf, conf.libpath / "system.nim")
-  let projectFile = if projectFileIdx == InvalidFileIDX: FileIndex(conf.projectMainIdx) else: projectFileIdx
+  let projectFile = if projectFileIdx == InvalidFileIDX: conf.projectMainIdx else: projectFileIdx
   graph.importStack.add projectFile
   if projectFile == systemFileIdx:
-    discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule})
+    discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule})
   else:
-    graph.compileSystemModule(cache)
-    discard graph.compileModule(projectFile, cache, {sfMainModule})
+    graph.compileSystemModule()
+    discard graph.compileModule(projectFile, {sfMainModule})
 
 proc makeModule*(graph: ModuleGraph; filename: string): PSym =
   result = graph.newModule(fileInfoIdx(graph.config, filename))
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 533d3a57f..be2ece911 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -9,83 +9,28 @@
 
 import
   options, strutils, os, tables, ropes, platform, terminal, macros,
-  configuration
+  lineinfos
 
-#type
-#  MsgConfig* = ref object of RootObj
-
-type
-  TFileInfo* = object
-    fullPath: string           # This is a canonical full filesystem path
-    projPath*: string          # This is relative to the project's root
-    shortName*: string         # short name of the module
-    quotedName*: Rope          # cached quoted short name for codegen
-                               # purposes
-    quotedFullName*: Rope      # cached quoted full name for codegen
-                               # purposes
-
-    lines*: seq[Rope]          # the source code of the module
-                               #   used for better error messages and
-                               #   embedding the original source in the
-                               #   generated code
-    dirtyfile: string          # the file that is actually read into memory
-                               # and parsed; usually 'nil' but is used
-                               # for 'nimsuggest'
-    hash*: string              # the checksum of the file
-    when defined(nimpretty):
-      fullContent*: string
-  FileIndex* = distinct int32
-  TLineInfo* = object          # This is designed to be as small as possible,
-                               # because it is used
-                               # in syntax nodes. We save space here by using
-                               # two int16 and an int32.
-                               # On 64 bit and on 32 bit systems this is
-                               # only 8 bytes.
-    line*: uint16
-    col*: int16
-    fileIndex*: FileIndex
-    when defined(nimpretty):
-      offsetA*, offsetB*: int
-      commentOffsetA*, commentOffsetB*: int
-
-  TErrorOutput* = enum
-    eStdOut
-    eStdErr
-
-  TErrorOutputs* = set[TErrorOutput]
-
-  ERecoverableError* = object of ValueError
-  ESuggestDone* = object of Exception
-
-proc `==`*(a, b: FileIndex): bool {.borrow.}
-
-
-const
-  InvalidFileIDX* = FileIndex(-1)
-
-var
-  filenameToIndexTbl = initTable[string, FileIndex]()
-  fileInfos*: seq[TFileInfo] = @[]
-  systemFileIdx*: FileIndex
-
-proc toCChar*(c: char): string =
+proc toCChar*(c: char; result: var string) =
   case c
-  of '\0'..'\x1F', '\x7F'..'\xFF': result = '\\' & toOctal(c)
-  of '\'', '\"', '\\', '?': result = '\\' & c
-  else: result = $(c)
+  of '\0'..'\x1F', '\x7F'..'\xFF':
+    result.add '\\'
+    result.add toOctal(c)
+  of '\'', '\"', '\\', '?':
+    result.add '\\'
+    result.add c
+  else:
+    result.add c
 
 proc makeCString*(s: string): Rope =
-  const
-    MaxLineLength = 64
+  const MaxLineLength = 64
   result = nil
   var res = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
   add(res, "\"")
   for i in countup(0, len(s) - 1):
     if (i + 1) mod MaxLineLength == 0:
-      add(res, '\"')
-      add(res, tnl)
-      add(res, '\"')
-    add(res, toCChar(s[i]))
+      add(res, "\"\L\"")
+    toCChar(s[i], res)
   add(res, '\"')
   add(result, rope(res))
 
@@ -110,8 +55,8 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo =
         result.fullContent = ""
 
 when defined(nimpretty):
-  proc fileSection*(fid: FileIndex; a, b: int): string =
-    substr(fileInfos[fid.int].fullContent, a, b)
+  proc fileSection*(conf: ConfigRef; fid: FileIndex; a, b: int): string =
+    substr(conf.m.fileInfos[fid.int].fullContent, a, b)
 
 proc fileInfoKnown*(conf: ConfigRef; filename: string): bool =
   var
@@ -120,7 +65,7 @@ proc fileInfoKnown*(conf: ConfigRef; filename: string): bool =
     canon = canonicalizePath(conf, filename)
   except:
     canon = filename
-  result = filenameToIndexTbl.hasKey(canon)
+  result = conf.m.filenameToIndexTbl.hasKey(canon)
 
 proc fileInfoIdx*(conf: ConfigRef; filename: string; isKnownFile: var bool): FileIndex =
   var
@@ -136,14 +81,14 @@ proc fileInfoIdx*(conf: ConfigRef; filename: string; isKnownFile: var bool): Fil
     # This flag indicates that we are working with such a path here
     pseudoPath = true
 
-  if filenameToIndexTbl.hasKey(canon):
-    result = filenameToIndexTbl[canon]
+  if conf.m.filenameToIndexTbl.hasKey(canon):
+    result = conf.m.filenameToIndexTbl[canon]
   else:
     isKnownFile = false
-    result = fileInfos.len.FileIndex
-    fileInfos.add(newFileInfo(canon, if pseudoPath: filename
-                                     else: shortenDir(conf, canon)))
-    filenameToIndexTbl[canon] = result
+    result = conf.m.fileInfos.len.FileIndex
+    conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: filename
+                                            else: shortenDir(conf, canon)))
+    conf.m.filenameToIndexTbl[canon] = result
 
 proc fileInfoIdx*(conf: ConfigRef; filename: string): FileIndex =
   var dummy: bool
@@ -157,34 +102,9 @@ proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
 proc newLineInfo*(conf: ConfigRef; filename: string, line, col: int): TLineInfo {.inline.} =
   result = newLineInfo(fileInfoIdx(conf, filename), line, col)
 
-when false:
-  fileInfos.add(newFileInfo("", "command line"))
-  var gCmdLineInfo* = newLineInfo(FileIndex(0), 1, 1)
-
-  fileInfos.add(newFileInfo("", "compilation artifact"))
-  var gCodegenLineInfo* = newLineInfo(FileIndex(1), 1, 1)
-
 proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
   raise newException(ERecoverableError, msg)
 
-proc sourceLine*(conf: ConfigRef; i: TLineInfo): Rope
-
-proc unknownLineInfo*(): TLineInfo =
-  result.line = uint16(0)
-  result.col = int16(-1)
-  result.fileIndex = InvalidFileIDX
-
-type
-  Severity* {.pure.} = enum ## VS Code only supports these three
-    Hint, Warning, Error
-
-var
-  msgContext: seq[TLineInfo] = @[]
-  lastError = unknownLineInfo()
-
-  errorOutputs* = {eStdOut, eStdErr}
-  writelnHook*: proc (output: string) {.closure.}
-  structuredErrorHook*: proc (info: TLineInfo; msg: string; severity: Severity) {.closure.}
 
 proc concat(strings: openarray[string]): string =
   var totalLen = 0
@@ -192,13 +112,13 @@ proc concat(strings: openarray[string]): string =
   result = newStringOfCap totalLen
   for s in strings: result.add s
 
-proc suggestWriteln*(s: string) =
-  if eStdOut in errorOutputs:
-    if isNil(writelnHook):
+proc suggestWriteln*(conf: ConfigRef; s: string) =
+  if eStdOut in conf.m.errorOutputs:
+    if isNil(conf.writelnHook):
       writeLine(stdout, s)
       flushFile(stdout)
     else:
-      writelnHook(s)
+      conf.writelnHook(s)
 
 proc msgQuit*(x: int8) = quit x
 proc msgQuit*(x: string) = quit x
@@ -219,61 +139,61 @@ const
   HintTitle    = "Hint: "
   HintColor    = fgGreen
 
-proc getInfoContextLen*(): int = return msgContext.len
-proc setInfoContextLen*(L: int) = setLen(msgContext, L)
+proc getInfoContextLen*(conf: ConfigRef): int = return conf.m.msgContext.len
+proc setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L)
 
-proc pushInfoContext*(info: TLineInfo) =
-  msgContext.add(info)
+proc pushInfoContext*(conf: ConfigRef; info: TLineInfo) =
+  conf.m.msgContext.add(info)
 
-proc popInfoContext*() =
-  setLen(msgContext, len(msgContext) - 1)
+proc popInfoContext*(conf: ConfigRef) =
+  setLen(conf.m.msgContext, len(conf.m.msgContext) - 1)
 
-proc getInfoContext*(index: int): TLineInfo =
-  let L = msgContext.len
+proc getInfoContext*(conf: ConfigRef; index: int): TLineInfo =
+  let L = conf.m.msgContext.len
   let i = if index < 0: L + index else: index
   if i >=% L: result = unknownLineInfo()
-  else: result = msgContext[i]
+  else: result = conf.m.msgContext[i]
 
-template toFilename*(fileIdx: FileIndex): string =
-  (if fileIdx.int32 < 0: "???" else: fileInfos[fileIdx.int32].projPath)
+template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
+  (if fileIdx.int32 < 0 or conf == nil: "???" else: conf.m.fileInfos[fileIdx.int32].projPath)
 
-proc toFullPath*(fileIdx: FileIndex): string =
-  if fileIdx.int32 < 0: result = "???"
-  else: result = fileInfos[fileIdx.int32].fullPath
+proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string =
+  if fileIdx.int32 < 0 or conf == nil: result = "???"
+  else: result = conf.m.fileInfos[fileIdx.int32].fullPath
 
-proc setDirtyFile*(fileIdx: FileIndex; filename: string) =
+proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: string) =
   assert fileIdx.int32 >= 0
-  fileInfos[fileIdx.int32].dirtyFile = filename
+  conf.m.fileInfos[fileIdx.int32].dirtyFile = filename
 
-proc setHash*(fileIdx: FileIndex; hash: string) =
+proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
   assert fileIdx.int32 >= 0
-  shallowCopy(fileInfos[fileIdx.int32].hash, hash)
+  shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash)
 
-proc getHash*(fileIdx: FileIndex): string =
+proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string =
   assert fileIdx.int32 >= 0
-  shallowCopy(result, fileInfos[fileIdx.int32].hash)
+  shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash)
 
-proc toFullPathConsiderDirty*(fileIdx: FileIndex): string =
+proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): string =
   if fileIdx.int32 < 0:
     result = "???"
-  elif not fileInfos[fileIdx.int32].dirtyFile.isNil:
-    result = fileInfos[fileIdx.int32].dirtyFile
+  elif not conf.m.fileInfos[fileIdx.int32].dirtyFile.isNil:
+    result = conf.m.fileInfos[fileIdx.int32].dirtyFile
   else:
-    result = fileInfos[fileIdx.int32].fullPath
+    result = conf.m.fileInfos[fileIdx.int32].fullPath
 
-template toFilename*(info: TLineInfo): string =
-  info.fileIndex.toFilename
+template toFilename*(conf: ConfigRef; info: TLineInfo): string =
+  toFilename(conf, info.fileIndex)
 
-template toFullPath*(info: TLineInfo): string =
-  info.fileIndex.toFullPath
+template toFullPath*(conf: ConfigRef; info: TLineInfo): string =
+  toFullPath(conf, info.fileIndex)
 
 proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
   if info.fileIndex.int32 < 0:
     result = "???"
   elif optListFullPaths in conf.globalOptions:
-    result = fileInfos[info.fileIndex.int32].fullPath
+    result = conf.m.fileInfos[info.fileIndex.int32].fullPath
   else:
-    result = fileInfos[info.fileIndex.int32].projPath
+    result = conf.m.fileInfos[info.fileIndex.int32].projPath
 
 proc toLinenumber*(info: TLineInfo): int {.inline.} =
   result = int info.line
@@ -281,23 +201,19 @@ proc toLinenumber*(info: TLineInfo): int {.inline.} =
 proc toColumn*(info: TLineInfo): int {.inline.} =
   result = info.col
 
-proc toFileLine*(info: TLineInfo): string {.inline.} =
-  result = info.toFilename & ":" & $info.line
+proc toFileLine*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
+  result = toFilename(conf, info) & ":" & $info.line
 
-proc toFileLineCol*(info: TLineInfo): string {.inline.} =
-  result = info.toFilename & "(" & $info.line & ", " & $info.col & ")"
+proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
+  result = toFilename(conf, info) & "(" & $info.line & ", " & $info.col & ")"
 
-proc `$`*(info: TLineInfo): string = toFileLineCol(info)
+proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info)
 
-proc `??`* (info: TLineInfo, filename: string): bool =
-  # only for debugging purposes
-  result = filename in info.toFilename
+proc `$`*(info: TLineInfo): string {.error.} = discard
 
-const trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no suggestions
-                                   # are produced within comments and string literals
-var gTrackPos*: TLineInfo
-var gTrackPosAttached*: bool ## whether the tracking position was attached to some
-                             ## close token.
+proc `??`* (conf: ConfigRef; info: TLineInfo, filename: string): bool =
+  # only for debugging purposes
+  result = filename in toFilename(conf, info)
 
 type
   MsgFlag* = enum  ## flags altering msgWriteln behavior
@@ -315,14 +231,14 @@ proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) =
   ## support.
   #if conf.cmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
 
-  if not isNil(writelnHook) and msgSkipHook notin flags:
-    writelnHook(s)
+  if not isNil(conf.writelnHook) and msgSkipHook notin flags:
+    conf.writelnHook(s)
   elif optStdout in conf.globalOptions or msgStdout in flags:
-    if eStdOut in errorOutputs:
+    if eStdOut in conf.m.errorOutputs:
       writeLine(stdout, s)
       flushFile(stdout)
   else:
-    if eStdErr in errorOutputs:
+    if eStdErr in conf.m.errorOutputs:
       writeLine(stderr, s)
       # On Windows stderr is fully-buffered when piped, regardless of C std.
       when defined(windows):
@@ -353,17 +269,17 @@ macro callStyledWriteLineStderr(args: varargs[typed]): untyped =
     result.add(arg)
 
 template callWritelnHook(args: varargs[string, `$`]) =
-  writelnHook concat(args)
+  conf.writelnHook concat(args)
 
 template styledMsgWriteln*(args: varargs[typed]) =
-  if not isNil(writelnHook):
+  if not isNil(conf.writelnHook):
     callIgnoringStyle(callWritelnHook, nil, args)
   elif optStdout in conf.globalOptions:
-    if eStdOut in errorOutputs:
+    if eStdOut in conf.m.errorOutputs:
       callIgnoringStyle(writeLine, stdout, args)
       flushFile(stdout)
   else:
-    if eStdErr in errorOutputs:
+    if eStdErr in conf.m.errorOutputs:
       if optUseColors in conf.globalOptions:
         callStyledWriteLineStderr(args)
       else:
@@ -394,7 +310,7 @@ proc log*(s: string) {.procvar.} =
 
 proc quit(conf: ConfigRef; msg: TMsgKind) =
   if defined(debug) or msg == errInternal or hintStackTrace in conf.notes:
-    if stackTraceAvailable() and isNil(writelnHook):
+    if stackTraceAvailable() and isNil(conf.writelnHook):
       writeStackTrace()
     else:
       styledMsgWriteln(fgRed, "No stack traceback available\n" &
@@ -425,19 +341,19 @@ proc exactEquals*(a, b: TLineInfo): bool =
 proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) =
   const instantiationFrom = "template/generic instantiation from here"
   var info = lastinfo
-  for i in countup(0, len(msgContext) - 1):
-    if msgContext[i] != lastinfo and msgContext[i] != info:
-      if structuredErrorHook != nil:
-        structuredErrorHook(msgContext[i], instantiationFrom,
-                            Severity.Error)
+  for i in 0 ..< len(conf.m.msgContext):
+    if conf.m.msgContext[i] != lastinfo and conf.m.msgContext[i] != info:
+      if conf.structuredErrorHook != nil:
+        conf.structuredErrorHook(conf, conf.m.msgContext[i], instantiationFrom,
+                                  Severity.Error)
       else:
         styledMsgWriteln(styleBright,
-                         PosFormat % [toMsgFilename(conf, msgContext[i]),
-                                      coordToStr(msgContext[i].line.int),
-                                      coordToStr(msgContext[i].col+1)],
+                         PosFormat % [toMsgFilename(conf, conf.m.msgContext[i]),
+                                      coordToStr(conf.m.msgContext[i].line.int),
+                                      coordToStr(conf.m.msgContext[i].col+1)],
                          resetStyle,
                          instantiationFrom)
-    info = msgContext[i]
+    info = conf.m.msgContext[i]
 
 proc ignoreMsgBecauseOfIdeTools(conf: ConfigRef; msg: TMsgKind): bool =
   msg >= errGenerated and conf.cmd == cmdIdeTools and optIdeDebug notin conf.globalOptions
@@ -473,8 +389,9 @@ proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
     inc(conf.hintCounter)
   let s = msgKindToString(msg) % args
 
-  if structuredErrorHook != nil:
-    structuredErrorHook(unknownLineInfo(), s & (if kind != nil: KindFormat % kind else: ""), sev)
+  if conf.structuredErrorHook != nil:
+    conf.structuredErrorHook(conf, unknownLineInfo(),
+      s & (if kind != nil: KindFormat % kind else: ""), sev)
 
   if not ignoreMsgBecauseOfIdeTools(conf, msg):
     if kind != nil:
@@ -491,6 +408,24 @@ proc resetAttributes*(conf: ConfigRef) =
   if {optUseColors, optStdout} * conf.globalOptions == {optUseColors}:
     terminal.resetAttributes(stderr)
 
+proc addSourceLine(conf: ConfigRef; fileIdx: FileIndex, line: string) =
+  conf.m.fileInfos[fileIdx.int32].lines.add line
+
+proc sourceLine*(conf: ConfigRef; i: TLineInfo): string =
+  if i.fileIndex.int32 < 0: return ""
+
+  if not optPreserveOrigSource(conf) and conf.m.fileInfos[i.fileIndex.int32].lines.len == 0:
+    try:
+      for line in lines(toFullPath(conf, i)):
+        addSourceLine conf, i.fileIndex, line.string
+    except IOError:
+      discard
+  assert i.fileIndex.int32 < conf.m.fileInfos.len
+  # can happen if the error points to EOF:
+  if i.line.int > conf.m.fileInfos[i.fileIndex.int32].lines.len: return ""
+
+  result = conf.m.fileInfos[i.fileIndex.int32].lines[i.line.int-1]
+
 proc writeSurroundingSrc(conf: ConfigRef; info: TLineInfo) =
   const indent = "  "
   msgWriteln(conf, indent & $sourceLine(conf, info))
@@ -523,7 +458,7 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
     # we try to filter error messages so that not two error message
     # in the same file and line are produced:
     #ignoreMsg = lastError == info and eh != doAbort
-    lastError = info
+    conf.m.lastError = info
   of warnMin..warnMax:
     sev = Severity.Warning
     ignoreMsg = optWarns notin conf.options or msg notin conf.notes
@@ -547,8 +482,8 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
   let s = getMessageStr(msg, arg)
 
   if not ignoreMsg:
-    if structuredErrorHook != nil:
-      structuredErrorHook(info, s & (if kind != nil: KindFormat % kind else: ""), sev)
+    if conf.structuredErrorHook != nil:
+      conf.structuredErrorHook(conf, info, s & (if kind != nil: KindFormat % kind else: ""), sev)
     if not ignoreMsgBecauseOfIdeTools(conf, msg):
       if kind != nil:
         styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s,
@@ -562,7 +497,7 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
 proc fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
   # this fixes bug #7080 so that it is at least obvious 'fatal'
   # was executed.
-  errorOutputs = {eStdOut, eStdErr}
+  conf.m.errorOutputs = {eStdOut, eStdErr}
   liMessage(conf, info, msg, arg, doAbort)
 
 proc globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
@@ -584,12 +519,12 @@ proc message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
   liMessage(conf, info, msg, arg, doNothing)
 
 proc internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) =
-  if conf.cmd == cmdIdeTools and structuredErrorHook.isNil: return
+  if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
   writeContext(conf, info)
   liMessage(conf, info, errInternal, errMsg, doAbort)
 
 proc internalError*(conf: ConfigRef; errMsg: string) =
-  if conf.cmd == cmdIdeTools and structuredErrorHook.isNil: return
+  if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
   writeContext(conf, unknownLineInfo())
   rawMessage(conf, errInternal, errMsg)
 
@@ -600,44 +535,19 @@ template assertNotNil*(conf: ConfigRef; e): untyped =
 template internalAssert*(conf: ConfigRef, e: bool) =
   if not e: internalError(conf, $instantiationInfo())
 
-proc addSourceLine*(fileIdx: FileIndex, line: string) =
-  fileInfos[fileIdx.int32].lines.add line.rope
-
-proc sourceLine*(conf: ConfigRef; i: TLineInfo): Rope =
-  if i.fileIndex.int32 < 0: return nil
-
-  if not optPreserveOrigSource(conf) and fileInfos[i.fileIndex.int32].lines.len == 0:
-    try:
-      for line in lines(i.toFullPath):
-        addSourceLine i.fileIndex, line.string
-    except IOError:
-      discard
-  assert i.fileIndex.int32 < fileInfos.len
-  # can happen if the error points to EOF:
-  if i.line.int > fileInfos[i.fileIndex.int32].lines.len: return nil
-
-  result = fileInfos[i.fileIndex.int32].lines[i.line.int-1]
-
 proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
   assert i.fileIndex.int32 >= 0
   if optExcessiveStackTrace in conf.globalOptions:
-    result = fileInfos[i.fileIndex.int32].quotedFullName
+    result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName
   else:
-    result = fileInfos[i.fileIndex.int32].quotedName
-
-ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) =
-  case err
-  of rInvalidFormatStr:
-    internalError(newPartialConfigRef(), "ropes: invalid format string: " & msg)
-  of rCannotOpenFile:
-    rawMessage(newPartialConfigRef(), if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg)
+    result = conf.m.fileInfos[i.fileIndex.int32].quotedName
 
 proc listWarnings*(conf: ConfigRef) =
   msgWriteln(conf, "Warnings:")
   for warn in warnMin..warnMax:
     msgWriteln(conf, "  [$1] $2" % [
       if warn in conf.notes: "x" else: " ",
-      configuration.WarningsToStr[ord(warn) - ord(warnMin)]
+      lineinfos.WarningsToStr[ord(warn) - ord(warnMin)]
     ])
 
 proc listHints*(conf: ConfigRef) =
@@ -645,5 +555,5 @@ proc listHints*(conf: ConfigRef) =
   for hint in hintMin..hintMax:
     msgWriteln(conf, "  [$1] $2" % [
       if hint in conf.notes: "x" else: " ",
-      configuration.HintsToStr[ord(hint) - ord(hintMin)]
+      lineinfos.HintsToStr[ord(hint) - ord(hintMin)]
     ])
diff --git a/compiler/ndi.nim b/compiler/ndi.nim
index a7ca02193..9708c388d 100644
--- a/compiler/ndi.nim
+++ b/compiler/ndi.nim
@@ -10,7 +10,7 @@
 ## This module implements the generation of ``.ndi`` files for better debugging
 ## support of Nim code. "ndi" stands for "Nim debug info".
 
-import ast, msgs, ropes
+import ast, msgs, ropes, options
 
 type
   NdiFile* = object
@@ -18,19 +18,19 @@ type
     f: File
     buf: string
 
-proc doWrite(f: var NdiFile; s: PSym) =
+proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) =
   f.buf.setLen 0
   f.buf.add s.info.line.int
   f.buf.add "\t"
   f.buf.add s.info.col.int
   f.f.write(s.name.s, "\t")
   f.f.writeRope(s.loc.r)
-  f.f.writeLine("\t", s.info.toFullPath, "\t", f.buf)
+  f.f.writeLine("\t", toFullPath(conf, s.info), "\t", f.buf)
 
-template writeMangledName*(f: NdiFile; s: PSym) =
-  if f.enabled: doWrite(f, s)
+template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) =
+  if f.enabled: doWrite(f, s, conf)
 
-proc open*(f: var NdiFile; filename: string) =
+proc open*(f: var NdiFile; filename: string; conf: ConfigRef) =
   f.enabled = filename.len > 0
   if f.enabled:
     f.f = open(filename, fmWrite, 8000)
diff --git a/compiler/nim.cfg b/compiler/nim.cfg
index 853ae7e00..1bd3fbfd6 100644
--- a/compiler/nim.cfg
+++ b/compiler/nim.cfg
@@ -5,6 +5,8 @@ path:"llvm"
 path:"$projectPath/.."
 
 define:booting
+define:nimcore
+#define:nimIncremental
 #import:"$projectpath/testability"
 
 @if windows:
@@ -13,6 +15,5 @@ define:booting
 
 define:useStdoutAsStdmsg
 
-cs:partial
 #define:useNodeIds
 #gc:markAndSweep
diff --git a/compiler/nim.nim b/compiler/nim.nim
index d3e00017f..90049bdfb 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -20,8 +20,8 @@ when defined(i386) and defined(windows) and defined(vcc):
 
 import
   commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
-  extccomp, strutils, os, osproc, platform, main, parseopt, service,
-  nodejs, scriptconfig, idents, modulegraphs, configuration
+  extccomp, strutils, os, osproc, platform, main, parseopt,
+  nodejs, scriptconfig, idents, modulegraphs, lineinfos
 
 when hasTinyCBackend:
   import tccgen
@@ -37,6 +37,25 @@ proc prependCurDir(f: string): string =
   else:
     result = f
 
+proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
+  var p = parseopt.initOptParser(cmd)
+  var argsCount = 0
+  while true:
+    parseopt.next(p)
+    case p.kind
+    of cmdEnd: break
+    of cmdLongoption, cmdShortOption:
+      if p.key == " ":
+        p.key = "-"
+        if processArgument(pass, p, argsCount, config): break
+      else:
+        processSwitch(pass, p, config)
+    of cmdArgument:
+      if processArgument(pass, p, argsCount, config): break
+  if pass == passCmd2:
+    if optRun notin config.globalOptions and config.arguments.len > 0 and config.command.normalize != "run":
+      rawMessage(config, errGenerated, errArgsNeedRunOption)
+
 proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
   condsyms.initDefines(conf.symbols)
   if paramCount() == 0:
@@ -60,7 +79,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
       conf.projectName = p.name
     else:
       conf.projectPath = canonicalizePath(conf, getCurrentDir())
-    loadConfigs(DefaultConfig, conf) # load all config files
+    loadConfigs(DefaultConfig, cache, conf) # load all config files
     let scriptFile = conf.projectFull.changeFileExt("nims")
     if fileExists(scriptFile):
       runNimScript(cache, scriptFile, freshDefines=false, conf)
@@ -75,7 +94,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
     processCmdLine(passCmd2, "", conf)
     if conf.command == "":
       rawMessage(conf, errGenerated, "command missing")
-    mainCommand(newModuleGraph(conf), cache)
+    mainCommand(newModuleGraph(cache, conf))
     if optHints in conf.options and hintGCStats in conf.notes: echo(GC_getStatistics())
     #echo(GC_getStatistics())
     if conf.errorCounter == 0:
diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim
index 8305b01f5..9a23535bf 100644
--- a/compiler/nimblecmd.nim
+++ b/compiler/nimblecmd.nim
@@ -10,7 +10,7 @@
 ## Implements some helper procs for Nimble (Nim's package manager) support.
 
 import parseutils, strutils, strtabs, os, options, msgs, sequtils,
-  configuration
+  lineinfos
 
 proc addPath*(conf: ConfigRef; path: string, info: TLineInfo) =
   if not conf.searchPaths.contains(path):
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index a455b4a44..d3b4645dc 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -11,7 +11,7 @@
 
 import
   llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer,
-  options, idents, wordrecg, strtabs, configuration
+  options, idents, wordrecg, strtabs, lineinfos
 
 # ---------------- configuration file parser -----------------------------
 # we use Nim's scanner here to save space and work
@@ -233,7 +233,7 @@ proc getSystemConfigPath(conf: ConfigRef; filename: string): string =
 
 proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) =
   setDefaultLibpath(conf)
-  
+
   var configFiles = newSeq[string]()
 
   template readConfigFile(path: string) =
@@ -264,7 +264,3 @@ proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) =
 
   for filename in configFiles:
     rawMessage(conf, hintConf, filename)
-
-proc loadConfigs*(cfg: string; conf: ConfigRef) =
-  # for backwards compatibility only.
-  loadConfigs(cfg, newIdentCache(), conf)
diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim
index ff91861d0..f20b5642c 100644
--- a/compiler/nimeval.nim
+++ b/compiler/nimeval.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nim Compiler
-#        (c) Copyright 2013 Andreas Rumpf
+#        (c) Copyright 2018 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -9,27 +9,112 @@
 
 ## exposes the Nim VM to clients.
 import
-  ast, modules, passes, passaux, condsyms,
-  options, nimconf, sem, semdata, llstream, vm, modulegraphs, idents
-
-proc execute*(program: string) =
-  passes.gIncludeFile = includeModule
-  passes.gImportModule = importModule
-  initDefines()
-  loadConfigs(DefaultConfig)
-
-  initDefines()
-  defineSymbol("nimrodvm")
-  defineSymbol("nimscript")
-  when hasFFI: defineSymbol("nimffi")
-  registerPass(verbosePass)
-  registerPass(semPass)
-  registerPass(evalPass)
-
-  searchPaths.add options.libpath
-  var graph = newModuleGraph()
+  ast, astalgo, modules, passes, condsyms,
+  options, sem, semdata, llstream, vm, vmdef,
+  modulegraphs, idents, os
+
+type
+  Interpreter* = ref object ## Use Nim as an interpreter with this object
+    mainModule: PSym
+    graph: ModuleGraph
+    scriptName: string
+
+iterator exportedSymbols*(i: Interpreter): PSym =
+  assert i != nil
+  assert i.mainModule != nil, "no main module selected"
+  var it: TTabIter
+  var s = initTabIter(it, i.mainModule.tab)
+  while s != nil:
+    yield s
+    s = nextIter(it, i.mainModule.tab)
+
+proc selectUniqueSymbol*(i: Interpreter; name: string;
+                         symKinds: set[TSymKind] = {skLet, skVar}): PSym =
+  ## Can be used to access a unique symbol of ``name`` and
+  ## the given ``symKinds`` filter.
+  assert i != nil
+  assert i.mainModule != nil, "no main module selected"
+  let n = getIdent(i.graph.cache, name)
+  var it: TIdentIter
+  var s = initIdentIter(it, i.mainModule.tab, n)
+  result = nil
+  while s != nil:
+    if s.kind in symKinds:
+      if result == nil: result = s
+      else: return nil # ambiguous
+    s = nextIdentIter(it, i.mainModule.tab)
+
+proc selectRoutine*(i: Interpreter; name: string): PSym =
+  ## Selects a declared rountine (proc/func/etc) from the main module.
+  ## The routine needs to have the export marker ``*``. The only matching
+  ## routine is returned and ``nil`` if it is overloaded.
+  result = selectUniqueSymbol(i, name, {skTemplate, skMacro, skFunc,
+                                        skMethod, skProc, skConverter})
+
+proc callRoutine*(i: Interpreter; routine: PSym; args: openArray[PNode]): PNode =
+  assert i != nil
+  result = vm.execProc(PCtx i.graph.vm, routine, args)
+
+proc getGlobalValue*(i: Interpreter; letOrVar: PSym): PNode =
+  result = vm.getGlobalValue(PCtx i.graph.vm, letOrVar)
+
+proc implementRoutine*(i: Interpreter; pkg, module, name: string;
+                       impl: proc (a: VmArgs) {.closure, gcsafe.}) =
+  assert i != nil
+  let vm = PCtx(i.graph.vm)
+  vm.registerCallback(pkg & "." & module & "." & name, impl)
+
+proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) =
+  ## This can also be used to *reload* the script.
+  assert i != nil
+  assert i.mainModule != nil, "no main module selected"
+  initStrTable(i.mainModule.tab)
+  i.mainModule.ast = nil
+
+  let s = if scriptStream != nil: scriptStream
+          else: llStreamOpen(findFile(i.graph.config, i.scriptName), fmRead)
+  processModule(i.graph, i.mainModule, s)
+
+proc findNimStdLib*(): string =
+  ## Tries to find a path to a valid "system.nim" file.
+  ## Returns "" on failure.
+  try:
+    let nimexe = os.findExe("nim")
+    if nimexe.len == 0: return ""
+    result = nimexe.splitPath()[0] /../ "lib"
+    if not fileExists(result / "system.nim"):
+      when defined(unix):
+        result = nimexe.expandSymlink.splitPath()[0] /../ "lib"
+        if not fileExists(result / "system.nim"): return ""
+  except OSError, ValueError:
+    return ""
+
+proc createInterpreter*(scriptName: string;
+                        searchPaths: openArray[string];
+                        flags: TSandboxFlags = {}): Interpreter =
+  var conf = newConfigRef()
   var cache = newIdentCache()
-  var m = makeStdinModule(graph)
+  var graph = newModuleGraph(cache, conf)
+  connectCallbacks(graph)
+  initDefines(conf.symbols)
+  defineSymbol(conf.symbols, "nimscript")
+  defineSymbol(conf.symbols, "nimconfig")
+  registerPass(graph, semPass)
+  registerPass(graph, evalPass)
+
+  for p in searchPaths:
+    conf.searchPaths.add(p)
+    if conf.libpath.len == 0: conf.libpath = p
+
+  var m = graph.makeModule(scriptName)
   incl(m.flags, sfMainModule)
-  compileSystemModule(graph,cache)
-  processModule(graph,m, llStreamOpen(program), nil, cache)
+  var vm = newCtx(m, cache, graph)
+  vm.mode = emRepl
+  vm.features = flags
+  graph.vm = vm
+  graph.compileSystemModule()
+  result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName)
+
+proc destroyInterpreter*(i: Interpreter) =
+  ## destructor.
+  discard "currently nothing to do."
diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim
index c3e29f9a1..58b019cd3 100644
--- a/compiler/nimfix/nimfix.nim
+++ b/compiler/nimfix/nimfix.nim
@@ -11,7 +11,7 @@
 
 import strutils, os, parseopt
 import compiler/[options, commands, modules, sem,
-  passes, passaux, nimfix/pretty,
+  passes, passaux, linter,
   msgs, nimconf,
   extccomp, condsyms,
   modulegraphs, idents]
diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim
index c32dbe623..c3e16e5ba 100644
--- a/compiler/nimfix/prettybase.nim
+++ b/compiler/nimfix/prettybase.nim
@@ -7,64 +7,13 @@
 #    distribution, for details about the copyright.
 #
 
-import strutils, lexbase, streams
-import ".." / [ast, msgs, idents]
+import strutils except Letters
+import lexbase, streams
+import ".." / [ast, msgs, lineinfos, idents, options, linter]
 from os import splitFile
 
-type
-  TSourceFile* = object
-    lines*: seq[string]
-    dirty*, isNimfixFile*: bool
-    fullpath*, newline*: string
-    fileIdx*: FileIndex
-
-var
-  gSourceFiles*: seq[TSourceFile] = @[]
-
-proc loadFile*(info: TLineInfo) =
-  let i = info.fileIndex.int
-  if i >= gSourceFiles.len:
-    gSourceFiles.setLen(i+1)
-  if gSourceFiles[i].lines.isNil:
-    gSourceFiles[i].fileIdx = info.fileIndex
-    gSourceFiles[i].lines = @[]
-    let path = info.toFullPath
-    gSourceFiles[i].fullpath = path
-    gSourceFiles[i].isNimfixFile = path.splitFile.ext == ".nimfix"
-    # we want to die here for IOError:
-    for line in lines(path):
-      gSourceFiles[i].lines.add(line)
-    # extract line ending of the file:
-    var lex: BaseLexer
-    open(lex, newFileStream(path, fmRead))
-    var pos = lex.bufpos
-    while true:
-      case lex.buf[pos]
-      of '\c':
-        gSourceFiles[i].newline = "\c\L"
-        break
-      of '\L', '\0':
-        gSourceFiles[i].newline = "\L"
-        break
-      else: discard
-      inc pos
-    close(lex)
-
-const
-  Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
-
-proc identLen*(line: string, start: int): int =
-  while start+result < line.len and line[start+result] in Letters:
-    inc result
-
-proc differ*(line: string, a, b: int, x: string): bool =
-  let y = line[a..b]
-  result = cmpIgnoreStyle(y, x) == 0 and y != x
-
-proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
-  loadFile(info)
-
-  let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1]
+proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent) =
+  let line = sourceLine(conf, info)
   var first = min(info.col.int, line.len)
   if first < 0: return
   #inc first, skipIgnoreCase(line, "proc ", first)
@@ -75,20 +24,18 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
   let last = first+identLen(line, first)-1
   if cmpIgnoreStyle(line[first..last], oldSym.s) == 0:
     var x = line.substr(0, first-1) & newSym.s & line.substr(last+1)
-    system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x)
-    gSourceFiles[info.fileIndex.int32].dirty = true
+    system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x)
+    conf.m.fileInfos[info.fileIndex.int32].dirty = true
     #if newSym.s == "File": writeStackTrace()
 
-proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) =
-  replaceDeprecated(info, oldSym.name, newSym.name)
-
-proc replaceComment*(info: TLineInfo) =
-  loadFile(info)
+proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PSym) =
+  replaceDeprecated(conf, info, oldSym.name, newSym.name)
 
-  let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1]
+proc replaceComment*(conf: ConfigRef; info: TLineInfo) =
+  let line = sourceLine(conf, info)
   var first = info.col.int
   if line[first] != '#': inc first
 
   var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape
-  system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x)
-  gSourceFiles[info.fileIndex.int32].dirty = true
+  system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x)
+  conf.m.fileInfos[info.fileIndex.int32].dirty = true
diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim
index 6cb675ed8..b00353e20 100644
--- a/compiler/nimsets.nim
+++ b/compiler/nimsets.nim
@@ -10,7 +10,8 @@
 # this unit handles Nim sets; it implements symbolic sets
 
 import
-  ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer
+  ast, astalgo, trees, nversion, lineinfos, platform, bitsets, types, renderer,
+  options
 
 proc inSet*(s: PNode, elem: PNode): bool =
   assert s.kind == nkCurly
@@ -58,10 +59,10 @@ proc someInSet*(s: PNode, a, b: PNode): bool =
         return true
   result = false
 
-proc toBitSet*(s: PNode, b: var TBitSet) =
+proc toBitSet*(conf: ConfigRef; s: PNode, b: var TBitSet) =
   var first, j: BiggestInt
-  first = firstOrd(s.typ.sons[0])
-  bitSetInit(b, int(getSize(s.typ)))
+  first = firstOrd(conf, s.typ.sons[0])
+  bitSetInit(b, int(getSize(conf, s.typ)))
   for i in countup(0, sonsLen(s) - 1):
     if s.sons[i].kind == nkRange:
       j = getOrdValue(s.sons[i].sons[0])
@@ -71,13 +72,13 @@ proc toBitSet*(s: PNode, b: var TBitSet) =
     else:
       bitSetIncl(b, getOrdValue(s.sons[i]) - first)
 
-proc toTreeSet*(s: TBitSet, settype: PType, info: TLineInfo): PNode =
+proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): PNode =
   var
     a, b, e, first: BiggestInt # a, b are interval borders
     elemType: PType
     n: PNode
   elemType = settype.sons[0]
-  first = firstOrd(elemType)
+  first = firstOrd(conf, elemType)
   result = newNodeI(nkCurly, info)
   result.typ = settype
   result.info = info
@@ -107,42 +108,42 @@ proc toTreeSet*(s: TBitSet, settype: PType, info: TLineInfo): PNode =
 
 template nodeSetOp(a, b: PNode, op: untyped) {.dirty.} =
   var x, y: TBitSet
-  toBitSet(a, x)
-  toBitSet(b, y)
+  toBitSet(conf, a, x)
+  toBitSet(conf, b, y)
   op(x, y)
-  result = toTreeSet(x, a.typ, a.info)
+  result = toTreeSet(conf, x, a.typ, a.info)
 
-proc unionSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetUnion)
-proc diffSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff)
-proc intersectSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect)
-proc symdiffSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
+proc unionSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetUnion)
+proc diffSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff)
+proc intersectSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect)
+proc symdiffSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
 
-proc containsSets*(a, b: PNode): bool =
+proc containsSets*(conf: ConfigRef; a, b: PNode): bool =
   var x, y: TBitSet
-  toBitSet(a, x)
-  toBitSet(b, y)
+  toBitSet(conf, a, x)
+  toBitSet(conf, b, y)
   result = bitSetContains(x, y)
 
-proc equalSets*(a, b: PNode): bool =
+proc equalSets*(conf: ConfigRef; a, b: PNode): bool =
   var x, y: TBitSet
-  toBitSet(a, x)
-  toBitSet(b, y)
+  toBitSet(conf, a, x)
+  toBitSet(conf, b, y)
   result = bitSetEquals(x, y)
 
-proc complement*(a: PNode): PNode =
+proc complement*(conf: ConfigRef; a: PNode): PNode =
   var x: TBitSet
-  toBitSet(a, x)
+  toBitSet(conf, a, x)
   for i in countup(0, high(x)): x[i] = not x[i]
-  result = toTreeSet(x, a.typ, a.info)
+  result = toTreeSet(conf, x, a.typ, a.info)
 
-proc deduplicate*(a: PNode): PNode =
+proc deduplicate*(conf: ConfigRef; a: PNode): PNode =
   var x: TBitSet
-  toBitSet(a, x)
-  result = toTreeSet(x, a.typ, a.info)
+  toBitSet(conf, a, x)
+  result = toTreeSet(conf, x, a.typ, a.info)
 
-proc cardSet*(a: PNode): BiggestInt =
+proc cardSet*(conf: ConfigRef; a: PNode): BiggestInt =
   var x: TBitSet
-  toBitSet(a, x)
+  toBitSet(conf, a, x)
   result = bitSetCard(x)
 
 proc setHasRange*(s: PNode): bool =
diff --git a/compiler/nversion.nim b/compiler/nversion.nim
index caa818d79..4b8cf7100 100644
--- a/compiler/nversion.nim
+++ b/compiler/nversion.nim
@@ -15,6 +15,6 @@ const
   VersionAsString* = system.NimVersion
   RodFileVersion* = "1223"       # modify this if the rod-format changes!
 
-  NimCompilerApiVersion* = 1 ## Check for the existance of this before accessing it
+  NimCompilerApiVersion* = 2 ## Check for the existance of this before accessing it
                              ## as older versions of the compiler API do not
                              ## declare this.
diff --git a/compiler/options.nim b/compiler/options.nim
index 150e67a18..1d6ddb09f 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -8,7 +8,8 @@
 #
 
 import
-  os, strutils, strtabs, osproc, sets, configuration, platform
+  os, strutils, strtabs, osproc, sets, lineinfos, platform,
+  prefixmatches
 
 from terminal import isatty
 
@@ -53,7 +54,7 @@ type                          # please make sure we have under 32 options
     optGenScript,             # generate a script file to compile the *.c files
     optGenMapping,            # generate a mapping file
     optRun,                   # run the compiled project
-    optCaasEnabled            # compiler-as-a-service is running
+    optCheckNep1,             # check that the names adhere to NEP-1
     optSkipConfigFile,        # skip the general config file
     optSkipProjConfigFile,    # skip the project's config file
     optSkipUserConfigFile,    # skip the users's config file
@@ -73,6 +74,7 @@ type                          # please make sure we have under 32 options
     optNoCppExceptions        # use C exception handling even with CPP
     optExcessiveStackTrace    # fully qualified module filenames
     optWholeProject           # for 'doc2': output any dependency
+    optMixedMode              # true if some module triggered C++ codegen
     optListFullPaths
     optNoNimblePath
     optDynlibOverrideAll
@@ -116,26 +118,65 @@ type
     callOperator,
     parallel,
     destructor,
-    notnil,
-    oldIterTransf
+    notnil
 
   SymbolFilesOption* = enum
-    disabledSf, enabledSf, writeOnlySf, readOnlySf, v2Sf
-
-  ConfigRef* = ref object ## eventually all global configuration should be moved here
+    disabledSf, writeOnlySf, readOnlySf, v2Sf
+
+  TSystemCC* = enum
+    ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
+    ccTcc, ccPcc, ccUcc, ccIcl, ccIcc
+
+  CfileFlag* {.pure.} = enum
+    Cached,    ## no need to recompile this time
+    External   ## file was introduced via .compile pragma
+
+  Cfile* = object
+    cname*, obj*: string
+    flags*: set[CFileFlag]
+  CfileList* = seq[Cfile]
+
+  Suggest* = ref object
+    section*: IdeCmd
+    qualifiedPath*: seq[string]
+    name*: ptr string         # not used beyond sorting purposes; name is also
+                              # part of 'qualifiedPath'
+    filePath*: string
+    line*: int                   # Starts at 1
+    column*: int                 # Starts at 0
+    doc*: string           # Not escaped (yet)
+    forth*: string               # type
+    quality*: range[0..100]   # matching quality
+    isGlobal*: bool # is a global variable
+    contextFits*: bool # type/non-type context matches
+    prefix*: PrefixMatch
+    symkind*: byte
+    scope*, localUsages*, globalUsages*: int # more usages is better
+    tokenLen*: int
+    version*: int
+  Suggestions* = seq[Suggest]
+
+  ConfigRef* = ref object ## every global configuration
+                          ## fields marked with '*' are subject to
+                          ## the incremental compilation mechanisms
+                          ## (+) means "part of the dependency"
+    target*: Target       # (+)
     linesCompiled*: int  # all lines that have been compiled
-    options*: TOptions
-    globalOptions*: TGlobalOptions
+    options*: TOptions    # (+)
+    globalOptions*: TGlobalOptions # (+)
+    m*: MsgConfig
+    evalTemplateCounter*: int
+    evalMacroCounter*: int
     exitcode*: int8
     cmd*: TCommands  # the command
-    selectedGC*: TGCMode       # the selected GC
+    selectedGC*: TGCMode       # the selected GC (+)
     verbosity*: int            # how verbose the compiler is
     numberOfProcessors*: int   # number of processors
     evalExpr*: string          # expression for idetools --eval
     lastCmdTime*: float        # when caas is enabled, we measure each command
     symbolFiles*: SymbolFilesOption
 
-    cppDefines*: HashSet[string]
+    cppDefines*: HashSet[string] # (*)
     headerFile*: string
     features*: set[Feature]
     arguments*: string ## the arguments to be passed to the program that
@@ -143,11 +184,13 @@ type
     helpWritten*: bool
     ideCmd*: IdeCmd
     oldNewlines*: bool
+    cCompiler*: TSystemCC
     enableNotes*: TNoteKinds
     disableNotes*: TNoteKinds
     foreignPackageNotes*: TNoteKinds
     notes*: TNoteKinds
     mainPackageNotes*: TNoteKinds
+    mainPackageId*: int
     errorCounter*: int
     hintCounter*: int
     warnCounter*: int
@@ -165,7 +208,7 @@ type
     projectPath*: string # holds a path like /home/alice/projects/nim/compiler/
     projectFull*: string # projectPath/projectName
     projectIsStdin*: bool # whether we're compiling from stdin
-    projectMainIdx*: int32 # the canonical path id of the main module
+    projectMainIdx*: FileIndex # the canonical path id of the main module
     command*: string # the main command (e.g. cc, check, scan, etc)
     commandArgs*: seq[string] # any arguments after the main command
     keepComments*: bool # whether the parser needs to keep comments
@@ -174,6 +217,33 @@ type
     docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \
     # The string uses the formatting variables `path` and `line`.
 
+     # the used compiler
+    cIncludes*: seq[string]  # directories to search for included files
+    cLibs*: seq[string]      # directories to search for lib files
+    cLinkedLibs*: seq[string]  # libraries to link
+
+    externalToLink*: seq[string]  # files to link in addition to the file
+                                  # we compiled (*)
+    linkOptionsCmd*: string
+    compileOptionsCmd*: seq[string]
+    linkOptions*: string          # (*)
+    compileOptions*: string       # (*)
+    ccompilerpath*: string
+    toCompile*: CfileList         # (*)
+    suggestionResultHook*: proc (result: Suggest) {.closure.}
+    suggestVersion*: int
+    suggestMaxResults*: int
+    lastLineInfo*: TLineInfo
+    writelnHook*: proc (output: string) {.closure.}
+    structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string;
+                                severity: Severity) {.closure.}
+
+template depConfigFields*(fn) {.dirty.} =
+  fn(target)
+  fn(options)
+  fn(globalOptions)
+  fn(selectedGC)
+
 const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
 
 const
@@ -196,9 +266,11 @@ template newPackageCache*(): untyped =
 proc newConfigRef*(): ConfigRef =
   result = ConfigRef(
     selectedGC: gcRefc,
+    cCompiler: ccGcc,
     verbosity: 1,
     options: DefaultOptions,
     globalOptions: DefaultGlobalOptions,
+    m: initMsgConfig(),
     evalExpr: "",
     cppDefines: initSet[string](),
     headerFile: "", features: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic,
@@ -216,15 +288,28 @@ proc newConfigRef*(): ConfigRef =
     projectPath: "", # holds a path like /home/alice/projects/nim/compiler/
     projectFull: "", # projectPath/projectName
     projectIsStdin: false, # whether we're compiling from stdin
-    projectMainIdx: 0'i32, # the canonical path id of the main module
+    projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module
     command: "", # the main command (e.g. cc, check, scan, etc)
     commandArgs: @[], # any arguments after the main command
     keepComments: true, # whether the parser needs to keep comments
     implicitImports: @[], # modules that are to be implicitly imported
     implicitIncludes: @[], # modules that are to be implicitly included
     docSeeSrcUrl: "",
-    arguments: ""
+    cIncludes: @[],   # directories to search for included files
+    cLibs: @[],       # directories to search for lib files
+    cLinkedLibs: @[],  # libraries to link
+
+    externalToLink: @[],
+    linkOptionsCmd: "",
+    compileOptionsCmd: @[],
+    linkOptions: "",
+    compileOptions: "",
+    ccompilerpath: "",
+    toCompile: @[],
+    arguments: "",
+    suggestMaxResults: 10_000
   )
+  setTargetFromSystem(result.target)
   # enable colors by default on terminals
   if terminal.isatty(stderr):
     incl(result.globalOptions, optUseColors)
@@ -246,39 +331,39 @@ proc cppDefine*(c: ConfigRef; define: string) =
 proc isDefined*(conf: ConfigRef; symbol: string): bool =
   if conf.symbols.hasKey(symbol):
     result = conf.symbols[symbol] != "false"
-  elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0:
+  elif cmpIgnoreStyle(symbol, CPU[conf.target.targetCPU].name) == 0:
     result = true
-  elif cmpIgnoreStyle(symbol, platform.OS[targetOS].name) == 0:
+  elif cmpIgnoreStyle(symbol, platform.OS[conf.target.targetOS].name) == 0:
     result = true
   else:
     case symbol.normalize
-    of "x86": result = targetCPU == cpuI386
-    of "itanium": result = targetCPU == cpuIa64
-    of "x8664": result = targetCPU == cpuAmd64
+    of "x86": result = conf.target.targetCPU == cpuI386
+    of "itanium": result = conf.target.targetCPU == cpuIa64
+    of "x8664": result = conf.target.targetCPU == cpuAmd64
     of "posix", "unix":
-      result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
+      result = conf.target.targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
                             osQnx, osAtari, osAix,
                             osHaiku, osVxWorks, osSolaris, osNetbsd,
                             osFreebsd, osOpenbsd, osDragonfly, osMacosx,
                             osAndroid}
     of "linux":
-      result = targetOS in {osLinux, osAndroid}
+      result = conf.target.targetOS in {osLinux, osAndroid}
     of "bsd":
-      result = targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly}
+      result = conf.target.targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly}
     of "emulatedthreadvars":
-      result = platform.OS[targetOS].props.contains(ospLacksThreadVars)
-    of "msdos": result = targetOS == osDos
-    of "mswindows", "win32": result = targetOS == osWindows
-    of "macintosh": result = targetOS in {osMacos, osMacosx}
-    of "sunos": result = targetOS == osSolaris
-    of "littleendian": result = CPU[targetCPU].endian == platform.littleEndian
-    of "bigendian": result = CPU[targetCPU].endian == platform.bigEndian
-    of "cpu8": result = CPU[targetCPU].bit == 8
-    of "cpu16": result = CPU[targetCPU].bit == 16
-    of "cpu32": result = CPU[targetCPU].bit == 32
-    of "cpu64": result = CPU[targetCPU].bit == 64
+      result = platform.OS[conf.target.targetOS].props.contains(ospLacksThreadVars)
+    of "msdos": result = conf.target.targetOS == osDos
+    of "mswindows", "win32": result = conf.target.targetOS == osWindows
+    of "macintosh": result = conf.target.targetOS in {osMacos, osMacosx}
+    of "sunos": result = conf.target.targetOS == osSolaris
+    of "littleendian": result = CPU[conf.target.targetCPU].endian == platform.littleEndian
+    of "bigendian": result = CPU[conf.target.targetCPU].endian == platform.bigEndian
+    of "cpu8": result = CPU[conf.target.targetCPU].bit == 8
+    of "cpu16": result = CPU[conf.target.targetCPU].bit == 16
+    of "cpu32": result = CPU[conf.target.targetCPU].bit == 32
+    of "cpu64": result = CPU[conf.target.targetCPU].bit == 64
     of "nimrawsetjmp":
-      result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
+      result = conf.target.targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
                             osDragonfly, osMacosx}
     else: discard
 
@@ -286,8 +371,7 @@ proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc,
 proc usesNativeGC*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
 
 template compilationCachePresent*(conf: ConfigRef): untyped =
-  conf.symbolFiles in {enabledSf, writeOnlySf}
-#  {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
+  conf.symbolFiles in {v2Sf, writeOnlySf}
 
 template optPreserveOrigSource*(conf: ConfigRef): untyped =
   optEmbedOrigSrc in conf.globalOptions
@@ -432,8 +516,10 @@ proc completeGeneratedFilePath*(conf: ConfigRef; f: string, createSubDir: bool =
   result = joinPath(subdir, tail)
   #echo "completeGeneratedFilePath(", f, ") = ", result
 
-proc rawFindFile(conf: ConfigRef; f: string): string =
+proc rawFindFile(conf: ConfigRef; f: string; suppressStdlib: bool): string =
   for it in conf.searchPaths:
+    if suppressStdlib and it.startsWith(conf.libpath):
+      continue
     result = joinPath(it, f)
     if existsFile(result):
       return canonicalizePath(conf, result)
@@ -457,13 +543,13 @@ template patchModule(conf: ConfigRef) {.dirty.} =
       let ov = conf.moduleOverrides[key]
       if ov.len > 0: result = ov
 
-proc findFile*(conf: ConfigRef; f: string): string {.procvar.} =
+proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): string {.procvar.} =
   if f.isAbsolute:
     result = if f.existsFile: f else: ""
   else:
-    result = rawFindFile(conf, f)
+    result = rawFindFile(conf, f, suppressStdlib)
     if result.len == 0:
-      result = rawFindFile(conf, f.toLowerAscii)
+      result = rawFindFile(conf, f.toLowerAscii, suppressStdlib)
       if result.len == 0:
         result = rawFindFile2(conf, f)
         if result.len == 0:
@@ -472,21 +558,15 @@ proc findFile*(conf: ConfigRef; f: string): string {.procvar.} =
 
 proc findModule*(conf: ConfigRef; modulename, currentModule: string): string =
   # returns path to module
-  when defined(nimfix):
-    # '.nimfix' modules are preferred over '.nim' modules so that specialized
-    # versions can be kept for 'nimfix'.
-    block:
-      let m = addFileExt(modulename, "nimfix")
-      let currentPath = currentModule.splitFile.dir
-      result = currentPath / m
-      if not existsFile(result):
-        result = findFile(conf, m)
-        if existsFile(result): return result
+  const pkgPrefix = "pkg/"
   let m = addFileExt(modulename, NimExt)
-  let currentPath = currentModule.splitFile.dir
-  result = currentPath / m
-  if not existsFile(result):
-    result = findFile(conf, m)
+  if m.startsWith(pkgPrefix):
+    result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true)
+  else:
+    let currentPath = currentModule.splitFile.dir
+    result = currentPath / m
+    if not existsFile(result):
+      result = findFile(conf, m)
   patchModule(conf)
 
 proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
diff --git a/compiler/parser.nim b/compiler/parser.nim
index fbc57ebb6..c513fac68 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -27,7 +27,10 @@ when isMainModule:
   outp.close
 
 import
-  llstream, lexer, idents, strutils, ast, astalgo, msgs, options, configuration
+  llstream, lexer, idents, strutils, ast, astalgo, msgs, options, lineinfos
+
+when defined(nimpretty2):
+  import layouter
 
 type
   TParser* = object            # A TParser object represents a file that
@@ -40,10 +43,16 @@ type
     tok*: TToken               # The current token
     inPragma*: int             # Pragma level
     inSemiStmtList*: int
+    emptyNode: PNode
+    when defined(nimpretty2):
+      em: Emitter
 
   SymbolMode = enum
     smNormal, smAllowNil, smAfterDot
 
+  TPrimaryMode = enum
+    pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
+
 proc parseAll*(p: var TParser): PNode
 proc closeParser*(p: var TParser)
 proc parseTopLevelStmt*(p: var TParser): PNode
@@ -75,6 +84,9 @@ proc parsePragma(p: var TParser): PNode
 proc postExprBlocks(p: var TParser, x: PNode): PNode
 proc parseExprStmt(p: var TParser): PNode
 proc parseBlock(p: var TParser): PNode
+proc primary(p: var TParser, mode: TPrimaryMode): PNode
+proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode
+
 # implementation
 
 proc getTok(p: var TParser) =
@@ -82,6 +94,11 @@ proc getTok(p: var TParser) =
   ## `tok` member.
   rawGetTok(p.lex, p.tok)
   p.hasProgress = true
+  when defined(nimpretty2):
+    emitTok(p.em, p.lex, p.tok)
+    while p.tok.tokType == tkComment:
+      rawGetTok(p.lex, p.tok)
+      emitTok(p.em, p.lex, p.tok)
 
 proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream,
                  cache: IdentCache; config: ConfigRef;
@@ -90,9 +107,12 @@ proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream,
   ##
   initToken(p.tok)
   openLexer(p.lex, fileIdx, inputStream, cache, config)
+  when defined(nimpretty2):
+    openEmitter(p.em, cache, config, fileIdx)
   getTok(p)                   # read the first token
   p.firstTok = true
   p.strongSpaces = strongSpaces
+  p.emptyNode = newNode(nkEmpty)
 
 proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
                  cache: IdentCache; config: ConfigRef;
@@ -102,6 +122,8 @@ proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
 proc closeParser(p: var TParser) =
   ## Close a parser, freeing up its resources.
   closeLexer(p.lex)
+  when defined(nimpretty2):
+    closeEmitter(p.em)
 
 proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
   ## Produce and emit the parser message `arg` to output.
@@ -131,7 +153,7 @@ proc rawSkipComment(p: var TParser, node: PNode) =
       if node.comment == nil: node.comment = ""
       when defined(nimpretty):
         if p.tok.commentOffsetB > p.tok.commentOffsetA:
-          add node.comment, fileSection(p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB)
+          add node.comment, fileSection(p.lex.config, p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB)
         else:
           add node.comment, p.tok.literal
       else:
@@ -316,6 +338,8 @@ proc colcom(p: var TParser, n: PNode) =
   eat(p, tkColon)
   skipComment(p, n)
 
+const tkBuiltInMagics = {tkType, tkStatic, tkAddr}
+
 proc parseSymbol(p: var TParser, mode = smNormal): PNode =
   #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
   #|        | IDENT | KEYW
@@ -324,7 +348,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
     result = newIdentNodeP(p.tok.ident, p)
     getTok(p)
   of tokKeywordLow..tokKeywordHigh:
-    if p.tok.tokType == tkAddr or p.tok.tokType == tkType or mode == smAfterDot:
+    if p.tok.tokType in tkBuiltInMagics or mode == smAfterDot:
       # for backwards compatibility these 2 are always valid:
       result = newIdentNodeP(p.tok.ident, p)
       getTok(p)
@@ -333,7 +357,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
       getTok(p)
     else:
       parMessage(p, errIdentifierExpected, p.tok)
-      result = ast.emptyNode
+      result = p.emptyNode
   of tkAccent:
     result = newNodeP(nkAccQuoted, p)
     getTok(p)
@@ -364,7 +388,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
     # But: this really sucks for idetools and keywords, so we don't do it
     # if it is a keyword:
     #if not isKeyword(p.tok.tokType): getTok(p)
-    result = ast.emptyNode
+    result = p.emptyNode
 
 proc colonOrEquals(p: var TParser, a: PNode): PNode =
   if p.tok.tokType == tkColon:
@@ -392,6 +416,8 @@ proc exprColonEqExpr(p: var TParser): PNode =
 
 proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
   #| exprList = expr ^+ comma
+  when defined(nimpretty2):
+    inc p.em.doIndentMore
   getTok(p)
   optInd(p, result)
   # progress guaranteed
@@ -401,6 +427,8 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
     if p.tok.tokType != tkComma: break
     getTok(p)
     optInd(p, a)
+  when defined(nimpretty2):
+    dec p.em.doIndentMore
 
 proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) =
   assert(endTok in {tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi})
@@ -509,9 +537,6 @@ proc parseGStrLit(p: var TParser, a: PNode): PNode =
   else:
     result = a
 
-type
-  TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
-
 proc complexOrSimpleStmt(p: var TParser): PNode
 proc simpleExpr(p: var TParser, mode = pmNormal): PNode
 
@@ -609,7 +634,7 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode =
   #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
   #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
   case p.tok.tokType
-  of tkSymbol, tkType, tkAddr:
+  of tkSymbol, tkBuiltInMagics:
     result = newIdentNodeP(p.tok.ident, p)
     getTok(p)
     result = parseGStrLit(p, result)
@@ -703,7 +728,7 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode =
   else:
     parMessage(p, errExprExpected, p.tok)
     getTok(p)  # we must consume a token here to prevend endless loops!
-    result = ast.emptyNode
+    result = p.emptyNode
 
 proc namedParams(p: var TParser, callee: PNode,
                  kind: TNodeKind, endTok: TTokType): PNode =
@@ -725,7 +750,12 @@ proc commandParam(p: var TParser, isFirstParam: var bool): PNode =
     addSon(result, parseExpr(p))
   isFirstParam = false
 
-proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
+const
+  tkTypeClasses = {tkRef, tkPtr, tkVar, tkStatic, tkType,
+                   tkEnum, tkTuple, tkObject, tkProc}
+
+proc primarySuffix(p: var TParser, r: PNode,
+                   baseIndent: int, mode: TPrimaryMode): PNode =
   #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
   #|       | doBlocks
   #|       | '.' optInd symbol generalizedLit?
@@ -742,7 +772,14 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
     case p.tok.tokType
     of tkParLe:
       # progress guaranteed
-      somePar()
+      if p.tok.strongSpaceA > 0:
+        # inside type sections, expressions such as `ref (int, bar)`
+        # are parsed as a nkCommand with a single tuple argument (nkPar)
+        if mode == pmTypeDef:
+          result = newNodeP(nkCommand, p)
+          result.addSon r
+          result.addSon primary(p, pmNormal)
+        break
       result = namedParams(p, result, nkCall, tkParRi)
       if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
         result.kind = nkObjConstr
@@ -758,8 +795,13 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
       # progress guaranteed
       somePar()
       result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
-    of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType,
-       tkOpr, tkDotDot:
+    of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast,
+       tkOpr, tkDotDot, tkTypeClasses - {tkRef, tkPtr}:
+        # XXX: In type sections we allow the free application of the
+        # command syntax, with the exception of expressions such as
+        # `foo ref` or `foo ptr`. Unfortunately, these two are also
+        # used as infix operators for the memory regions feature and
+        # the current parsing rules don't play well here.
       if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}):
         # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
         # solution, but pragmas.nim can't handle that
@@ -784,9 +826,6 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
     else:
       break
 
-proc primary(p: var TParser, mode: TPrimaryMode): PNode
-proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode
-
 proc parseOperators(p: var TParser, headNode: PNode,
                     limit: int, mode: TPrimaryMode): PNode =
   result = headNode
@@ -821,7 +860,11 @@ proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
   result = parseOperators(p, result, limit, mode)
 
 proc simpleExpr(p: var TParser, mode = pmNormal): PNode =
+  when defined(nimpretty2):
+    inc p.em.doIndentMore
   result = simpleExprAux(p, -1, mode)
+  when defined(nimpretty2):
+    dec p.em.doIndentMore
 
 proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode =
   #| condExpr = expr colcom expr optInd
@@ -896,8 +939,12 @@ proc parsePragma(p: var TParser): PNode =
       getTok(p)
       skipComment(p, a)
   optPar(p)
-  if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p)
-  else: parMessage(p, "expected '.}'")
+  if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}:
+    when defined(nimpretty2):
+      if p.tok.tokType == tkCurlyRi: curlyRiWasPragma(p.em)
+    getTok(p)
+  else:
+    parMessage(p, "expected '.}'")
   dec p.inPragma
 
 proc identVis(p: var TParser; allowDot=false): PNode =
@@ -905,6 +952,8 @@ proc identVis(p: var TParser; allowDot=false): PNode =
   #| identVisDot = symbol '.' optInd symbol opr?
   var a = parseSymbol(p)
   if p.tok.tokType == tkOpr:
+    when defined(nimpretty2):
+      starWasExportMarker(p.em)
     result = newNodeP(nkPostfix, p)
     addSon(result, newIdentNodeP(p.tok.ident, p))
     addSon(result, a)
@@ -982,6 +1031,8 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
       var a = parseIdentColonEquals(p, {})
       addSon(result, a)
       if p.tok.tokType notin {tkComma, tkSemiColon}: break
+      when defined(nimpretty2):
+        commaWasSemicolon(p.em)
       getTok(p)
       skipComment(p, a)
     optPar(p)
@@ -1015,7 +1066,9 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
   #| paramListColon = paramList? (':' optInd typeDesc)?
   var a: PNode
   result = newNodeP(nkFormalParams, p)
-  addSon(result, ast.emptyNode) # return type
+  addSon(result, p.emptyNode) # return type
+  when defined(nimpretty2):
+    inc p.em.doIndentMore
   let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0
   if hasParLe:
     getTok(p)
@@ -1035,6 +1088,8 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
         break
       addSon(result, a)
       if p.tok.tokType notin {tkComma, tkSemiColon}: break
+      when defined(nimpretty2):
+        commaWasSemicolon(p.em)
       getTok(p)
       skipComment(p, a)
     optPar(p)
@@ -1047,13 +1102,15 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
     result.sons[0] = parseTypeDesc(p)
   elif not retColon and not hasParle:
     # Mark as "not there" in order to mark for deprecation in the semantic pass:
-    result = ast.emptyNode
+    result = p.emptyNode
+  when defined(nimpretty2):
+    dec p.em.doIndentMore
 
 proc optPragmas(p: var TParser): PNode =
   if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)):
     result = parsePragma(p)
   else:
-    result = ast.emptyNode
+    result = p.emptyNode
 
 proc parseDoBlock(p: var TParser; info: TLineInfo): PNode =
   #| doBlock = 'do' paramListArrow pragmas? colcom stmt
@@ -1062,7 +1119,9 @@ proc parseDoBlock(p: var TParser; info: TLineInfo): PNode =
   colcom(p, result)
   result = parseStmt(p)
   if params.kind != nkEmpty:
-    result = newProcNode(nkDo, info, result, params = params, pragmas = pragmas)
+    result = newProcNode(nkDo, info,
+      body = result, params = params, name = p.emptyNode, pattern = p.emptyNode,
+      genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
 
 proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
   #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
@@ -1075,9 +1134,9 @@ proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
   if p.tok.tokType == tkEquals and isExpr:
     getTok(p)
     skipComment(p, result)
-    result = newProcNode(kind, info, parseStmt(p),
-                         params = params,
-                         pragmas = pragmas)
+    result = newProcNode(kind, info, body = parseStmt(p),
+      params = params, name = p.emptyNode, pattern = p.emptyNode,
+      genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
   else:
     result = newNodeI(nkProcTy, info)
     if hasSignature:
@@ -1087,9 +1146,9 @@ proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
 proc isExprStart(p: TParser): bool =
   case p.tok.tokType
   of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf,
-     tkProc, tkFunc, tkIterator, tkBind, tkAddr,
+     tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics,
      tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr,
-     tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut:
+     tkTuple, tkObject, tkWhen, tkCase, tkOut:
     result = true
   else: result = false
 
@@ -1150,7 +1209,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
   #|          | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum'
   #| primary = typeKeyw typeDescK
   #|         /  prefixOperator* identOrLiteral primarySuffix*
-  #|         / 'static' primary
   #|         / 'bind' primary
   if isOperator(p.tok):
     let isSigil = isSigilLike(p.tok)
@@ -1163,7 +1221,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
       #XXX prefix operators
       let baseInd = p.lex.currLineIndent
       addSon(result, primary(p, pmSkipSuffix))
-      result = primarySuffix(p, result, baseInd)
+      result = primarySuffix(p, result, baseInd, mode)
     else:
       addSon(result, primary(p, pmNormal))
     return
@@ -1193,14 +1251,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
       result = parseTypeClass(p)
     else:
       parMessage(p, "the 'concept' keyword is only valid in 'type' sections")
-  of tkStatic:
-    let info = parLineInfo(p)
-    getTokNoInd(p)
-    let next = primary(p, pmNormal)
-    if next.kind == nkBracket and next.sonsLen == 1:
-      result = newNode(nkStaticTy, info, @[next.sons[0]])
-    else:
-      result = newNode(nkStaticExpr, info, @[next])
   of tkBind:
     result = newNodeP(nkBind, p)
     getTok(p)
@@ -1215,7 +1265,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
     let baseInd = p.lex.currLineIndent
     result = identOrLiteral(p, mode)
     if mode != pmSkipSuffix:
-      result = primarySuffix(p, result, baseInd)
+      result = primarySuffix(p, result, baseInd, mode)
 
 proc parseTypeDesc(p: var TParser): PNode =
   #| typeDesc = simpleExpr
@@ -1244,8 +1294,8 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode =
   if p.tok.indent >= 0: return
 
   var
-    openingParams = emptyNode
-    openingPragmas = emptyNode
+    openingParams = p.emptyNode
+    openingPragmas = p.emptyNode
 
   if p.tok.tokType == tkDo:
     getTok(p)
@@ -1264,8 +1314,12 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode =
 
       stmtList.flags.incl nfBlockArg
       if openingParams.kind != nkEmpty:
-        result.add newProcNode(nkDo, stmtList.info, stmtList,
-                               params = openingParams, pragmas = openingPragmas)
+        result.add newProcNode(nkDo, stmtList.info, body = stmtList,
+                               params = openingParams,
+                               name = p.emptyNode, pattern = p.emptyNode,
+                               genericParams = p.emptyNode,
+                               pragmas = openingPragmas,
+                               exceptions = p.emptyNode)
       else:
         result.add stmtList
 
@@ -1424,10 +1478,10 @@ proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode =
   getTok(p)
   if p.tok.tokType == tkComment:
     skipComment(p, result)
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   elif p.tok.indent >= 0 and p.tok.indent <= p.currInd or not isExprStart(p):
     # NL terminates:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   else:
     var e = parseExpr(p)
     e = postExprBlocks(p, e)
@@ -1568,7 +1622,7 @@ proc parseBlock(p: var TParser): PNode =
   #| blockExpr = 'block' symbol? colcom stmt
   result = newNodeP(nkBlockStmt, p)
   getTokNoInd(p)
-  if p.tok.tokType == tkColon: addSon(result, ast.emptyNode)
+  if p.tok.tokType == tkColon: addSon(result, p.emptyNode)
   else: addSon(result, parseSymbol(p))
   colcom(p, result)
   addSon(result, parseStmt(p))
@@ -1586,7 +1640,7 @@ proc parseAsm(p: var TParser): PNode =
   result = newNodeP(nkAsmStmt, p)
   getTokNoInd(p)
   if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p))
-  else: addSon(result, ast.emptyNode)
+  else: addSon(result, p.emptyNode)
   case p.tok.tokType
   of tkStrLit: addSon(result, newStrNodeP(nkStrLit, p.tok.literal, p))
   of tkRStrLit: addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p))
@@ -1594,7 +1648,7 @@ proc parseAsm(p: var TParser): PNode =
                             newStrNodeP(nkTripleStrLit, p.tok.literal, p))
   else:
     parMessage(p, "the 'asm' statement takes a string literal")
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
     return
   getTok(p)
 
@@ -1625,13 +1679,13 @@ proc parseGenericParam(p: var TParser): PNode =
     optInd(p, result)
     addSon(result, parseExpr(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkEquals:
     getTok(p)
     optInd(p, result)
     addSon(result, parseExpr(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
 
 proc parseGenericParamList(p: var TParser): PNode =
   #| genericParamList = '[' optInd
@@ -1644,6 +1698,8 @@ proc parseGenericParamList(p: var TParser): PNode =
     var a = parseGenericParam(p)
     addSon(result, a)
     if p.tok.tokType notin {tkComma, tkSemiColon}: break
+    when defined(nimpretty2):
+      commaWasSemicolon(p.em)
     getTok(p)
     skipComment(p, a)
   optPar(p)
@@ -1667,22 +1723,22 @@ proc parseRoutine(p: var TParser, kind: TNodeKind): PNode =
   optInd(p, result)
   addSon(result, identVis(p))
   if p.tok.tokType == tkCurlyLe and p.validInd: addSon(result, p.parsePattern)
-  else: addSon(result, ast.emptyNode)
+  else: addSon(result, p.emptyNode)
   if p.tok.tokType == tkBracketLe and p.validInd:
     result.add(p.parseGenericParamList)
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   addSon(result, p.parseParamList)
   if p.tok.tokType == tkCurlyDotLe and p.validInd: addSon(result, p.parsePragma)
-  else: addSon(result, ast.emptyNode)
+  else: addSon(result, p.emptyNode)
   # empty exception tracking:
-  addSon(result, ast.emptyNode)
+  addSon(result, p.emptyNode)
   if p.tok.tokType == tkEquals and p.validInd:
     getTok(p)
     skipComment(p, result)
     addSon(result, parseStmt(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   indAndComment(p, result)
 
 proc newCommentStmt(p: var TParser): PNode =
@@ -1732,7 +1788,7 @@ proc parseConstant(p: var TParser): PNode =
     optInd(p, result)
     addSon(result, parseTypeDesc(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   eat(p, tkEquals)
   optInd(p, result)
   addSon(result, parseExpr(p))
@@ -1742,7 +1798,7 @@ proc parseEnum(p: var TParser): PNode =
   #| enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+
   result = newNodeP(nkEnumTy, p)
   getTok(p)
-  addSon(result, ast.emptyNode)
+  addSon(result, p.emptyNode)
   optInd(p, result)
   flexComment(p, result)
   # progress guaranteed
@@ -1813,7 +1869,7 @@ proc parseObjectCase(p: var TParser): PNode =
   addSon(a, identWithPragma(p))
   eat(p, tkColon)
   addSon(a, parseTypeDesc(p))
-  addSon(a, ast.emptyNode)
+  addSon(a, p.emptyNode)
   addSon(result, a)
   if p.tok.tokType == tkColon: getTok(p)
   flexComment(p, result)
@@ -1872,7 +1928,7 @@ proc parseObjectPart(p: var TParser): PNode =
       result = newNodeP(nkNilLit, p)
       getTok(p)
     else:
-      result = ast.emptyNode
+      result = p.emptyNode
 
 proc parseObject(p: var TParser): PNode =
   #| object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart
@@ -1881,19 +1937,19 @@ proc parseObject(p: var TParser): PNode =
   if p.tok.tokType == tkCurlyDotLe and p.validInd:
     addSon(result, parsePragma(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkOf and p.tok.indent < 0:
     var a = newNodeP(nkOfInherit, p)
     getTok(p)
     addSon(a, parseTypeDesc(p))
     addSon(result, a)
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkComment:
     skipComment(p, result)
   # an initial IND{>} HAS to follow:
   if not realInd(p):
-    addSon(result, emptyNode)
+    addSon(result, p.emptyNode)
     return
   addSon(result, parseObjectPart(p))
 
@@ -1928,7 +1984,7 @@ proc parseTypeClass(p: var TParser): PNode =
   if p.tok.tokType == tkCurlyDotLe and p.validInd:
     addSon(result, parsePragma(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkOf and p.tok.indent < 0:
     var a = newNodeP(nkOfInherit, p)
     getTok(p)
@@ -1939,12 +1995,12 @@ proc parseTypeClass(p: var TParser): PNode =
       getTok(p)
     addSon(result, a)
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkComment:
     skipComment(p, result)
   # an initial IND{>} HAS to follow:
   if not realInd(p):
-    addSon(result, emptyNode)
+    addSon(result, p.emptyNode)
   else:
     addSon(result, parseStmt(p))
 
@@ -1957,14 +2013,14 @@ proc parseTypeDef(p: var TParser): PNode =
   if p.tok.tokType == tkBracketLe and p.validInd:
     addSon(result, parseGenericParamList(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   if p.tok.tokType == tkEquals:
     result.info = parLineInfo(p)
     getTok(p)
     optInd(p, result)
     addSon(result, parseTypeDefAux(p))
   else:
-    addSon(result, ast.emptyNode)
+    addSon(result, p.emptyNode)
   indAndComment(p, result)    # special extension!
 
 proc parseVarTuple(p: var TParser): PNode =
@@ -1979,7 +2035,7 @@ proc parseVarTuple(p: var TParser): PNode =
     if p.tok.tokType != tkComma: break
     getTok(p)
     skipComment(p, a)
-  addSon(result, ast.emptyNode)         # no type desc
+  addSon(result, p.emptyNode)         # no type desc
   optPar(p)
   eat(p, tkParRi)
   eat(p, tkEquals)
@@ -2040,7 +2096,7 @@ proc simpleStmt(p: var TParser): PNode =
   of tkComment: result = newCommentStmt(p)
   else:
     if isExprStart(p): result = parseExprStmt(p)
-    else: result = ast.emptyNode
+    else: result = p.emptyNode
   if result.kind notin {nkEmpty, nkCommentStmt}: skipComment(p, result)
 
 proc complexOrSimpleStmt(p: var TParser): PNode =
@@ -2136,7 +2192,7 @@ proc parseStmt(p: var TParser): PNode =
     of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc,
        tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar:
       parMessage(p, "complex statement requires indentation")
-      result = ast.emptyNode
+      result = p.emptyNode
     else:
       if p.inSemiStmtList > 0:
         result = simpleStmt(p)
@@ -2173,7 +2229,7 @@ proc parseAll(p: var TParser): PNode =
 proc parseTopLevelStmt(p: var TParser): PNode =
   ## Implements an iterator which, when called repeatedly, returns the next
   ## top-level statement or emptyNode if end of stream.
-  result = ast.emptyNode
+  result = p.emptyNode
   # progress guaranteed
   while true:
     if p.tok.indent != 0:
diff --git a/compiler/passaux.nim b/compiler/passaux.nim
index 568fb4c23..eabce8822 100644
--- a/compiler/passaux.nim
+++ b/compiler/passaux.nim
@@ -10,7 +10,7 @@
 ## implements some little helper passes
 
 import
-  strutils, ast, astalgo, passes, idents, msgs, options, idgen, configuration
+  strutils, ast, astalgo, passes, idents, msgs, options, idgen, lineinfos
 
 from modulegraphs import ModuleGraph
 
@@ -18,7 +18,7 @@ type
   VerboseRef = ref object of TPassContext
     config: ConfigRef
 
-proc verboseOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext =
+proc verboseOpen(graph: ModuleGraph; s: PSym): PPassContext =
   #MessageOut('compiling ' + s.name.s);
   result = VerboseRef(config: graph.config)
   rawMessage(graph.config, hintProcessing, s.name.s)
diff --git a/compiler/passes.nim b/compiler/passes.nim
index 8f9f57f3d..45c726f2a 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -13,24 +13,20 @@
 import
   strutils, options, ast, astalgo, llstream, msgs, platform, os,
   condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
-  nimsets, syntaxes, times, rodread, idgen, modulegraphs, reorder, rod,
-  configuration
+  nimsets, syntaxes, times, idgen, modulegraphs, reorder, rod,
+  lineinfos
 
 
 type
   TPassContext* = object of RootObj # the pass's context
-    rd*: PRodReader  # != nil if created by "openCached"
 
   PPassContext* = ref TPassContext
 
-  TPassOpen* = proc (graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext {.nimcall.}
-  TPassOpenCached* =
-    proc (graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext {.nimcall.}
+  TPassOpen* = proc (graph: ModuleGraph; module: PSym): PPassContext {.nimcall.}
   TPassClose* = proc (graph: ModuleGraph; p: PPassContext, n: PNode): PNode {.nimcall.}
   TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.}
 
-  TPass* = tuple[open: TPassOpen, openCached: TPassOpenCached,
-                 process: TPassProcess, close: TPassClose,
+  TPass* = tuple[open: TPassOpen, process: TPassProcess, close: TPassClose,
                  isFrontend: bool]
 
   TPassData* = tuple[input: PNode, closeOutput: PNode]
@@ -41,23 +37,14 @@ type
 # This mechanism used to be used for the instantiation of generics.
 
 proc makePass*(open: TPassOpen = nil,
-               openCached: TPassOpenCached = nil,
                process: TPassProcess = nil,
                close: TPassClose = nil,
                isFrontend = false): TPass =
   result.open = open
-  result.openCached = openCached
   result.close = close
   result.process = process
   result.isFrontend = isFrontend
 
-# the semantic checker needs these:
-var
-  gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PSym {.nimcall.}
-  gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PNode {.nimcall.}
-
-# implementation
-
 proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} =
   # can be used by codegen passes to determine whether they should do
   # something with `n`. Currently, this ignores `n` and uses the global
@@ -74,44 +61,34 @@ var
   gPasses: array[0..maxPasses - 1, TPass]
   gPassesLen*: int
 
-proc clearPasses* =
+proc clearPasses*(g: ModuleGraph) =
   gPassesLen = 0
 
-proc registerPass*(p: TPass) =
+proc registerPass*(g: ModuleGraph; p: TPass) =
   gPasses[gPassesLen] = p
   inc(gPassesLen)
 
-proc carryPass*(g: ModuleGraph; p: TPass, module: PSym; cache: IdentCache;
+proc carryPass*(g: ModuleGraph; p: TPass, module: PSym;
                 m: TPassData): TPassData =
-  var c = p.open(g, module, cache)
+  var c = p.open(g, module)
   result.input = p.process(c, m.input)
   result.closeOutput = if p.close != nil: p.close(g, c, m.closeOutput)
                        else: m.closeOutput
 
 proc carryPasses*(g: ModuleGraph; nodes: PNode, module: PSym;
-                  cache: IdentCache; passes: TPasses) =
+                  passes: TPasses) =
   var passdata: TPassData
   passdata.input = nodes
   for pass in passes:
-    passdata = carryPass(g, pass, module, cache, passdata)
+    passdata = carryPass(g, pass, module, passdata)
 
 proc openPasses(g: ModuleGraph; a: var TPassContextArray;
-                module: PSym; cache: IdentCache) =
+                module: PSym) =
   for i in countup(0, gPassesLen - 1):
     if not isNil(gPasses[i].open):
-      a[i] = gPasses[i].open(g, module, cache)
+      a[i] = gPasses[i].open(g, module)
     else: a[i] = nil
 
-proc openPassesCached(g: ModuleGraph; a: var TPassContextArray, module: PSym,
-                      rd: PRodReader) =
-  for i in countup(0, gPassesLen - 1):
-    if not isNil(gPasses[i].openCached):
-      a[i] = gPasses[i].openCached(g, module, rd)
-      if a[i] != nil:
-        a[i].rd = rd
-    else:
-      a[i] = nil
-
 proc closePasses(graph: ModuleGraph; a: var TPassContextArray) =
   var m: PNode = nil
   for i in countup(0, gPassesLen - 1):
@@ -127,19 +104,6 @@ proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool =
       if isNil(m): return false
   result = true
 
-proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) =
-  # this implements the code transformation pipeline
-  var m = n
-  for i in countup(0, gPassesLen - 1):
-    if not isNil(gPasses[i].openCached): m = gPasses[i].process(a[i], m)
-
-proc closePassesCached(graph: ModuleGraph; a: var TPassContextArray) =
-  var m: PNode = nil
-  for i in countup(0, gPassesLen - 1):
-    if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close):
-      m = gPasses[i].close(graph, a[i], m)
-    a[i] = nil                # free the memory here
-
 proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex =
   let fullPath = findModule(conf, module, relativeTo)
   if fullPath.len == 0:
@@ -151,7 +115,7 @@ proc processImplicits(conf: ConfigRef; implicits: seq[string], nodeKind: TNodeKi
                       a: var TPassContextArray; m: PSym) =
   # XXX fixme this should actually be relative to the config file!
   let gCmdLineInfo = newLineInfo(FileIndex(0), 1, 1)
-  let relativeTo = m.info.toFullPath
+  let relativeTo = toFullPath(conf, m.info)
   for module in items(implicits):
     # implicit imports should not lead to a module importing itself
     if m.position != resolveMod(conf, module, relativeTo).int32:
@@ -161,8 +125,7 @@ proc processImplicits(conf: ConfigRef; implicits: seq[string], nodeKind: TNodeKi
       importStmt.addSon str
       if not processTopLevelStmt(importStmt, a): break
 
-proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
-                    rd: PRodReader; cache: IdentCache): bool {.discardable.} =
+proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {.discardable.} =
   if graph.stopCompile(): return true
   var
     p: TParsers
@@ -173,24 +136,17 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
     # new module caching mechanism:
     for i in 0..<gPassesLen:
       if not isNil(gPasses[i].open) and not gPasses[i].isFrontend:
-        a[i] = gPasses[i].open(graph, module, cache)
+        a[i] = gPasses[i].open(graph, module)
       else:
         a[i] = nil
 
-    var stmtIndex = 0
-    var doContinue = true
-    while doContinue:
-      let n = loadNode(module, stmtIndex)
-      if n == nil or graph.stopCompile(): break
-      #if n.kind == nkImportStmt:
-      #  echo "yes and it's ", n
-      inc stmtIndex
+    if not graph.stopCompile():
+      let n = loadNode(graph, module)
       var m = n
       for i in 0..<gPassesLen:
         if not isNil(gPasses[i].process) and not gPasses[i].isFrontend:
           m = gPasses[i].process(a[i], m)
           if isNil(m):
-            doContinue = false
             break
 
     var m: PNode = nil
@@ -198,10 +154,10 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
       if not isNil(gPasses[i].close) and not gPasses[i].isFrontend:
         m = gPasses[i].close(graph, a[i], m)
       a[i] = nil
-  elif rd == nil:
-    openPasses(graph, a, module, cache)
+  else:
+    openPasses(graph, a, module)
     if stream == nil:
-      let filename = fileIdx.toFullPathConsiderDirty
+      let filename = toFullPathConsiderDirty(graph.config, fileIdx)
       s = llStreamOpen(filename, fmRead)
       if s == nil:
         rawMessage(graph.config, errCannotOpenFile, filename)
@@ -209,7 +165,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
     else:
       s = stream
     while true:
-      openParsers(p, fileIdx, s, cache, graph.config)
+      openParsers(p, fileIdx, s, graph.cache, graph.config)
 
       if sfSystemModule notin module.flags:
         # XXX what about caching? no processing then? what if I change the
@@ -232,7 +188,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
             if n.kind == nkEmpty: break
             sl.add n
           if sfReorder in module.flags:
-            sl = reorder(graph, sl, module, cache)
+            sl = reorder(graph, sl, module)
           discard processTopLevelStmt(sl, a)
           break
         elif not processTopLevelStmt(n, a): break
@@ -241,11 +197,4 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
     closePasses(graph, a)
     # id synchronization point for more consistent code generation:
     idSynchronizationPoint(1000)
-  else:
-    openPassesCached(graph, a, module, rd)
-    var n = loadInitSection(rd)
-    for i in countup(0, sonsLen(n) - 1):
-      if graph.stopCompile(): break
-      processTopLevelStmtCached(n.sons[i], a)
-    closePassesCached(graph, a)
   result = true
diff --git a/compiler/patterns.nim b/compiler/patterns.nim
index 5409a4811..2d2aeba76 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -77,7 +77,7 @@ proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
   if isNil(n.typ):
     result = p.typ.kind in {tyVoid, tyStmt}
   else:
-    result = sigmatch.argtypeMatches(c.c, p.typ, n.typ)
+    result = sigmatch.argtypeMatches(c.c, p.typ, n.typ, fromHlo = true)
 
 proc isPatternParam(c: PPatternContext, p: PNode): bool {.inline.} =
   result = p.kind == nkSym and p.sym.kind == skParam and p.sym.owner == c.owner
diff --git a/compiler/platform.nim b/compiler/platform.nim
index 8b3bf6b74..0db16f26c 100644
--- a/compiler/platform.nim
+++ b/compiler/platform.nim
@@ -22,7 +22,7 @@ type
     osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
     osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx,
     osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osAndroid, osVxworks
-    osGenode, osJS, osNimrodVM, osStandalone
+    osGenode, osJS, osNimVM, osStandalone
 
 type
   TInfoOSProp* = enum
@@ -162,7 +162,7 @@ const
       pathSep: ":", dirSep: "/",
       scriptExt: ".sh", curDir: ".",
       exeExt: "", extSep: ".", props: {}),
-     (name: "NimrodVM", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
+     (name: "NimVM", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
       objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
       scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {}),
      (name: "Standalone", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
@@ -175,7 +175,7 @@ type
                      # alias conditionals to condsyms (end of module).
     cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64,
     cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel,
-    cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430, cpuSparc64,
+    cpuArm, cpuArm64, cpuJS, cpuNimVM, cpuAVR, cpuMSP430, cpuSparc64,
     cpuMips64, cpuMips64el, cpuRiscV64
 
 type
@@ -202,7 +202,7 @@ const
     (name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
     (name: "arm64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32),
-    (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
+    (name: "nimvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
     (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
     (name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
     (name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
@@ -210,44 +210,40 @@ const
     (name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "riscv64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)]
 
-var
-  targetCPU*, hostCPU*: TSystemCPU
-  targetOS*, hostOS*: TSystemOS
-
-proc nameToOS*(name: string): TSystemOS
-proc nameToCPU*(name: string): TSystemCPU
-
-var
-  intSize*: int
-  floatSize*: int
-  ptrSize*: int
-  tnl*: string                # target newline
-
-proc setTarget*(o: TSystemOS, c: TSystemCPU) =
+type
+  Target* = object
+    targetCPU*, hostCPU*: TSystemCPU
+    targetOS*, hostOS*: TSystemOS
+    intSize*: int
+    floatSize*: int
+    ptrSize*: int
+    tnl*: string                # target newline
+
+proc setTarget*(t: var Target; o: TSystemOS, c: TSystemCPU) =
   assert(c != cpuNone)
   assert(o != osNone)
   #echo "new Target: OS: ", o, " CPU: ", c
-  targetCPU = c
-  targetOS = o
-  intSize = CPU[c].intSize div 8
-  floatSize = CPU[c].floatSize div 8
-  ptrSize = CPU[c].bit div 8
-  tnl = OS[o].newLine
-
-proc nameToOS(name: string): TSystemOS =
+  t.targetCPU = c
+  t.targetOS = o
+  # assume no cross-compiling
+  t.hostCPU = c
+  t.hostOS = o
+  t.intSize = CPU[c].intSize div 8
+  t.floatSize = CPU[c].floatSize div 8
+  t.ptrSize = CPU[c].bit div 8
+  t.tnl = OS[o].newLine
+
+proc nameToOS*(name: string): TSystemOS =
   for i in countup(succ(osNone), high(TSystemOS)):
     if cmpIgnoreStyle(name, OS[i].name) == 0:
       return i
   result = osNone
 
-proc nameToCPU(name: string): TSystemCPU =
+proc nameToCPU*(name: string): TSystemCPU =
   for i in countup(succ(cpuNone), high(TSystemCPU)):
     if cmpIgnoreStyle(name, CPU[i].name) == 0:
       return i
   result = cpuNone
 
-hostCPU = nameToCPU(system.hostCPU)
-hostOS = nameToOS(system.hostOS)
-
-setTarget(hostOS, hostCPU) # assume no cross-compiling
-
+proc setTargetFromSystem*(t: var Target) =
+  t.setTarget(nameToOS(system.hostOS), nameToCPU(system.hostCPU))
diff --git a/compiler/plugins/active.nim b/compiler/plugins/active.nim
index 5da623e49..7b5306f9c 100644
--- a/compiler/plugins/active.nim
+++ b/compiler/plugins/active.nim
@@ -10,4 +10,15 @@
 ## Include file that imports all plugins that are active.
 
 import
-  locals / locals, itersgen
+  "../compiler" / [pluginsupport, idents, ast], locals, itersgen
+
+const
+  plugins: array[2, Plugin] = [
+    ("stdlib", "system", "iterToProc", iterToProcImpl),
+    ("stdlib", "system", "locals", semLocals)
+  ]
+
+proc getPlugin*(ic: IdentCache; fn: PSym): Transformation =
+  for p in plugins:
+    if pluginMatches(ic, p, fn): return p.t
+  return nil
diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim
index ebb65dd4a..440d2e081 100644
--- a/compiler/plugins/itersgen.nim
+++ b/compiler/plugins/itersgen.nim
@@ -9,11 +9,11 @@
 
 ## Plugin to transform an inline iterator into a data structure.
 
-import ".." / [pluginsupport, ast, astalgo,
+import ".." / [ast, astalgo,
   magicsys, lookups, semdata,
-  lambdalifting, rodread, msgs]
+  lambdalifting, msgs]
 
-proc iterToProcImpl(c: PContext, n: PNode): PNode =
+proc iterToProcImpl*(c: PContext, n: PNode): PNode =
   result = newNodeI(nkStmtList, n.info)
   let iter = n[1]
   if iter.kind != nkSym or iter.sym.kind != skIterator:
@@ -40,11 +40,9 @@ proc iterToProcImpl(c: PContext, n: PNode): PNode =
   prc.typ.rawAddSon t
   let orig = iter.sym.ast
   prc.ast = newProcNode(nkProcDef, n.info,
-                        name = newSymNode(prc),
-                        params = orig[paramsPos],
-                        pragmas = orig[pragmasPos],
-                        body = body)
+              body = body, params = orig[paramsPos], name = newSymNode(prc),
+              pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode,
+              pragmas = orig[pragmasPos], exceptions = c.graph.emptyNode)
+
   prc.ast.add iter.sym.ast.sons[resultPos]
   addInterfaceDecl(c, prc)
-
-registerPlugin("stdlib", "system", "iterToProc", iterToProcImpl)
diff --git a/compiler/plugins/locals/locals.nim b/compiler/plugins/locals.nim
index ff7f3be58..0048ff985 100644
--- a/compiler/plugins/locals/locals.nim
+++ b/compiler/plugins/locals.nim
@@ -9,10 +9,10 @@
 
 ## The builtin 'system.locals' implemented as a plugin.
 
-import "../../" / [pluginsupport, ast, astalgo,
+import ".." / [pluginsupport, ast, astalgo,
   magicsys, lookups, semdata, lowerings]
 
-proc semLocals(c: PContext, n: PNode): PNode =
+proc semLocals*(c: PContext, n: PNode): PNode =
   var counter = 0
   var tupleType = newTypeS(tyTuple, c)
   result = newNodeIT(nkPar, n.info, tupleType)
@@ -39,5 +39,3 @@ proc semLocals(c: PContext, n: PNode): PNode =
         var a = newSymNode(it, result.info)
         if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
         result.add(a)
-
-registerPlugin("stdlib", "system", "locals", semLocals)
diff --git a/compiler/pluginsupport.nim b/compiler/pluginsupport.nim
index f67942c97..a44436f11 100644
--- a/compiler/pluginsupport.nim
+++ b/compiler/pluginsupport.nim
@@ -8,40 +8,26 @@
 #
 
 ## Plugin support for the Nim compiler. Right now plugins
-## need to be built with the compiler only: plugins using 
+## need to be built with the compiler only: plugins using
 ## DLLs or the FFI will not work.
 
 import ast, semdata, idents
 
 type
   Transformation* = proc (c: PContext; n: PNode): PNode {.nimcall.}
-  Plugin = ref object
-    fn, module, package: PIdent
+  Plugin* = tuple
+    package, module, fn: string
     t: Transformation
-    next: Plugin
 
-proc pluginMatches(p: Plugin; s: PSym): bool =
-  if s.name.id != p.fn.id:
+proc pluginMatches*(ic: IdentCache; p: Plugin; s: PSym): bool =
+  if s.name.id != ic.getIdent(p.fn).id:
     return false
   let module = s.skipGenericOwner
   if module == nil or module.kind != skModule or
-      module.name.id != p.module.id:
+      module.name.id != ic.getIdent(p.module).id:
     return false
   let package = module.owner
   if package == nil or package.kind != skPackage or
-      package.name.id != p.package.id:
+      package.name.id != ic.getIdent(p.package).id:
     return false
   return true
-
-var head: Plugin
-
-proc getPlugin*(fn: PSym): Transformation =
-  var it = head
-  while it != nil:
-    if pluginMatches(it, fn): return it.t
-    it = it.next
-
-proc registerPlugin*(package, module, fn: string; t: Transformation) =
-  let oldHead = head
-  head = Plugin(fn: getIdent(fn), module: getIdent(module),
-                 package: getIdent(package), t: t, next: oldHead)
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index d3fa506cb..afe60e9dd 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -12,7 +12,7 @@
 import
   os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
   wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees,
-  rodread, types, lookups, configuration
+  types, lookups, lineinfos
 
 const
   FirstCallConv* = wNimcall
@@ -24,8 +24,8 @@ const
     wCompilerProc, wCore, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
     wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
     wAsmNoStackFrame, wError, wDiscardable, wNoInit, wCodegenDecl,
-    wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
-    wOverride, wConstructor, wExportNims, wUsed, wLiftLocals}
+    wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, wOverride,
+    wConstructor, wExportNims, wUsed, wLiftLocals, wStacktrace, wLinetrace}
   converterPragmas* = procPragmas
   methodPragmas* = procPragmas+{wBase}-{wImportCpp}
   templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
@@ -82,7 +82,13 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
       return it[1]
 
 proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
-# implementation
+
+proc recordPragma(c: PContext; n: PNode; key, val: string; val2 = "") =
+  var recorded = newNodeI(nkCommentStmt, n.info)
+  recorded.add newStrNode(key, n.info)
+  recorded.add newStrNode(val, n.info)
+  if val2.len > 0: recorded.add newStrNode(val2, n.info)
+  c.graph.recordStmt(c.graph, c.module, recorded)
 
 const
   errStringLiteralExpected = "string literal expected"
@@ -143,7 +149,7 @@ proc processImportCpp(c: PContext; s: PSym, extname: string, info: TLineInfo) =
   if c.config.cmd == cmdCompileToC:
     let m = s.getModule()
     incl(m.flags, sfCompileToCpp)
-  extccomp.gMixedMode = true
+  incl c.config.globalOptions, optMixedMode
 
 proc processImportObjC(c: PContext; s: PSym, extname: string, info: TLineInfo) =
   setExternName(c, s, extname, info)
@@ -217,9 +223,9 @@ proc isTurnedOn(c: PContext, n: PNode): bool =
     if x.kind == nkIntLit: return x.intVal != 0
   localError(c.config, n.info, "'on' or 'off' expected")
 
-proc onOff(c: PContext, n: PNode, op: TOptions) =
-  if isTurnedOn(c, n): c.config.options = c.config.options + op
-  else: c.config.options = c.config.options - op
+proc onOff(c: PContext, n: PNode, op: TOptions, resOptions: var TOptions) =
+  if isTurnedOn(c, n): resOptions = resOptions + op
+  else: resOptions = resOptions - op
 
 proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
   if isTurnedOn(c, n): incl(c.module.flags, flag)
@@ -227,7 +233,7 @@ proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
 
 proc processCallConv(c: PContext, n: PNode) =
   if n.kind in nkPragmaCallKinds and n.len == 2 and n.sons[1].kind == nkIdent:
-    var sw = whichKeyword(n.sons[1].ident)
+    let sw = whichKeyword(n.sons[1].ident)
     case sw
     of FirstCallConv..LastCallConv:
       c.optionStack[^1].defaultCC = wordToCallConv(sw)
@@ -307,54 +313,68 @@ proc processNote(c: PContext, n: PNode) =
   else:
     invalidPragma(c, n)
 
-proc processOption(c: PContext, n: PNode): bool =
-  if n.kind notin nkPragmaCallKinds or n.len != 2: result = true
+proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} =
+  case w
+  of wChecks: ChecksOptions
+  of wObjChecks: {optObjCheck}
+  of wFieldChecks: {optFieldCheck}
+  of wRangechecks: {optRangeCheck}
+  of wBoundchecks: {optBoundsCheck}
+  of wOverflowchecks: {optOverflowCheck}
+  of wNilchecks: {optNilCheck}
+  of wFloatchecks: {optNaNCheck, optInfCheck}
+  of wNanChecks: {optNaNCheck}
+  of wInfChecks: {optInfCheck}
+  of wMovechecks: {optMoveCheck}
+  of wAssertions: {optAssert}
+  of wWarnings: {optWarns}
+  of wHints: {optHints}
+  of wLinedir: {optLineDir}
+  of wStacktrace: {optStackTrace}
+  of wLinetrace: {optLineTrace}
+  of wDebugger: {optEndb}
+  of wProfiler: {optProfiler, optMemTracker}
+  of wMemTracker: {optMemTracker}
+  of wByRef: {optByRef}
+  of wImplicitStatic: {optImplicitStatic}
+  of wPatterns: {optPatterns}
+  else: {}
+
+proc tryProcessOption(c: PContext, n: PNode, resOptions: var TOptions): bool =
+  result = true
+  if n.kind notin nkPragmaCallKinds or n.len != 2: result = false
   elif n.sons[0].kind == nkBracketExpr: processNote(c, n)
-  elif n.sons[0].kind != nkIdent: result = true
+  elif n.sons[0].kind != nkIdent: result = false
   else:
     let sw = whichKeyword(n.sons[0].ident)
-    case sw
-    of wChecks: onOff(c, n, ChecksOptions)
-    of wObjChecks: onOff(c, n, {optObjCheck})
-    of wFieldChecks: onOff(c, n, {optFieldCheck})
-    of wRangechecks: onOff(c, n, {optRangeCheck})
-    of wBoundchecks: onOff(c, n, {optBoundsCheck})
-    of wOverflowchecks: onOff(c, n, {optOverflowCheck})
-    of wNilchecks: onOff(c, n, {optNilCheck})
-    of wFloatchecks: onOff(c, n, {optNaNCheck, optInfCheck})
-    of wNanChecks: onOff(c, n, {optNaNCheck})
-    of wInfChecks: onOff(c, n, {optInfCheck})
-    of wMovechecks: onOff(c, n, {optMoveCheck})
-    of wAssertions: onOff(c, n, {optAssert})
-    of wWarnings: onOff(c, n, {optWarns})
-    of wHints: onOff(c, n, {optHints})
-    of wCallconv: processCallConv(c, n)
-    of wLinedir: onOff(c, n, {optLineDir})
-    of wStacktrace: onOff(c, n, {optStackTrace})
-    of wLinetrace: onOff(c, n, {optLineTrace})
-    of wDebugger: onOff(c, n, {optEndb})
-    of wProfiler: onOff(c, n, {optProfiler, optMemTracker})
-    of wMemTracker: onOff(c, n, {optMemTracker})
-    of wByRef: onOff(c, n, {optByRef})
-    of wDynlib: processDynLib(c, n, nil)
-    of wOptimization:
-      if n.sons[1].kind != nkIdent:
-        invalidPragma(c, n)
-      else:
-        case n.sons[1].ident.s.normalize
-        of "speed":
-          incl(c.config.options, optOptimizeSpeed)
-          excl(c.config.options, optOptimizeSize)
-        of "size":
-          excl(c.config.options, optOptimizeSpeed)
-          incl(c.config.options, optOptimizeSize)
-        of "none":
-          excl(c.config.options, optOptimizeSpeed)
-          excl(c.config.options, optOptimizeSize)
-        else: localError(c.config, n.info, "'none', 'speed' or 'size' expected")
-    of wImplicitStatic: onOff(c, n, {optImplicitStatic})
-    of wPatterns: onOff(c, n, {optPatterns})
-    else: result = true
+    let opts = pragmaToOptions(sw)
+    if opts != {}:
+      onOff(c, n, opts, resOptions)
+    else:
+      case sw
+      of wCallconv: processCallConv(c, n)
+      of wDynlib: processDynLib(c, n, nil)
+      of wOptimization:
+        if n.sons[1].kind != nkIdent:
+          invalidPragma(c, n)
+        else:
+          case n.sons[1].ident.s.normalize
+          of "speed":
+            incl(resOptions, optOptimizeSpeed)
+            excl(resOptions, optOptimizeSize)
+          of "size":
+            excl(resOptions, optOptimizeSpeed)
+            incl(resOptions, optOptimizeSize)
+          of "none":
+            excl(resOptions, optOptimizeSpeed)
+            excl(resOptions, optOptimizeSize)
+          else: localError(c.config, n.info, "'none', 'speed' or 'size' expected")
+      else: result = false
+
+proc processOption(c: PContext, n: PNode, resOptions: var TOptions) =
+  if not tryProcessOption(c, n, resOptions):
+    # calling conventions (boring...):
+    localError(c.config, n.info, "option expected")
 
 proc processPush(c: PContext, n: PNode, start: int) =
   if n.sons[start-1].kind in nkPragmaCallKinds:
@@ -367,7 +387,7 @@ proc processPush(c: PContext, n: PNode, start: int) =
   x.notes = c.config.notes
   c.optionStack.add(x)
   for i in countup(start, sonsLen(n) - 1):
-    if processOption(c, n.sons[i]):
+    if not tryProcessOption(c, n.sons[i], c.config.options):
       # simply store it somewhere:
       if x.otherPragmas.isNil:
         x.otherPragmas = newNodeI(nkPragma, n.info)
@@ -408,7 +428,7 @@ proc relativeFile(c: PContext; n: PNode; ext=""): string =
   var s = expectStrLit(c, n)
   if ext.len > 0 and splitFile(s).ext == "":
     s = addFileExt(s, ext)
-  result = parentDir(n.info.toFullPath) / s
+  result = parentDir(toFullPath(c.config, n.info)) / s
   if not fileExists(result):
     if isAbsolute(s): result = s
     else:
@@ -416,6 +436,10 @@ proc relativeFile(c: PContext; n: PNode; ext=""): string =
       if result.len == 0: result = s
 
 proc processCompile(c: PContext, n: PNode) =
+  proc docompile(c: PContext; it: PNode; src, dest: string) =
+    var cf = Cfile(cname: src, obj: dest, flags: {CfileFlag.External})
+    extccomp.addExternalFileToCompile(c.config, cf)
+    recordPragma(c, it, "compile", src, dest)
 
   proc getStrLit(c: PContext, n: PNode; i: int): string =
     n.sons[i] = c.semConstExpr(c, n[i])
@@ -430,30 +454,31 @@ proc processCompile(c: PContext, n: PNode) =
   if it.kind in {nkPar, nkTupleConstr} and it.len == 2:
     let s = getStrLit(c, it, 0)
     let dest = getStrLit(c, it, 1)
-    var found = parentDir(n.info.toFullPath) / s
+    var found = parentDir(toFullPath(c.config, n.info)) / s
     for f in os.walkFiles(found):
-      let nameOnly = extractFilename(f)
-      var cf = Cfile(cname: f,
-          obj: completeCFilePath(c.config, dest % nameOnly),
-          flags: {CfileFlag.External})
-      extccomp.addExternalFileToCompile(c.config, cf)
+      let obj = completeCFilePath(c.config, dest % extractFilename(f))
+      docompile(c, it, f, obj)
   else:
     let s = expectStrLit(c, n)
-    var found = parentDir(n.info.toFullPath) / s
+    var found = parentDir(toFullPath(c.config, n.info)) / s
     if not fileExists(found):
       if isAbsolute(s): found = s
       else:
         found = findFile(c.config, s)
         if found.len == 0: found = s
-    extccomp.addExternalFileToCompile(c.config, found)
+    let obj = toObjFile(c.config, completeCFilePath(c.config, found, false))
+    docompile(c, it, found, obj)
 
 proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
-  let found = relativeFile(c, n, CC[cCompiler].objExt)
+  let found = relativeFile(c, n, CC[c.config.cCompiler].objExt)
   case feature
-  of linkNormal: extccomp.addExternalFileToLink(c.config, found)
+  of linkNormal:
+    extccomp.addExternalFileToLink(c.config, found)
+    recordPragma(c, n, "link", found)
   of linkSys:
-    extccomp.addExternalFileToLink(c.config,
-      c.config.libpath / completeCFilePath(c.config, found, false))
+    let dest = c.config.libpath / completeCFilePath(c.config, found, false)
+    extccomp.addExternalFileToLink(c.config, dest)
+    recordPragma(c, n, "link", dest)
   else: internalError(c.config, n.info, "processCommonLink")
 
 proc pragmaBreakpoint(c: PContext, n: PNode) =
@@ -484,9 +509,10 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
       if c < 0: sub = substr(str, b + 1)
       else: sub = substr(str, b + 1, c - 1)
       if sub != "":
-        var e = searchInScopes(con, getIdent(sub))
+        var e = searchInScopes(con, getIdent(con.cache, sub))
         if e != nil:
-          if e.kind == skStub: loadStub(e)
+          when false:
+            if e.kind == skStub: loadStub(e)
           incl(e.flags, sfUsed)
           addSon(result, newSymNode(e))
         else:
@@ -553,7 +579,7 @@ proc pragmaLine(c: PContext, n: PNode) =
       localError(c.config, n.info, "tuple expected")
   else:
     # sensible default:
-    n.info = getInfoContext(-1)
+    n.info = getInfoContext(c.config, -1)
 
 proc processPragma(c: PContext, n: PNode, i: int) =
   let it = n[i]
@@ -638,7 +664,7 @@ proc deprecatedStmt(c: PContext; outerPragma: PNode) =
       let dest = qualifiedLookUp(c, n[1], {checkUndeclared})
       if dest == nil or dest.kind in routineKinds:
         localError(c.config, n.info, warnUser, "the .deprecated pragma is unreliable for routines")
-      let src = considerQuotedIdent(c.config, n[0])
+      let src = considerQuotedIdent(c, n[0])
       let alias = newSym(skAlias, src, dest, n[0].info, c.config.options)
       incl(alias.flags, sfExported)
       if sfCompilerProc in dest.flags: markCompilerProc(c, alias)
@@ -661,7 +687,7 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
       # We return a dummy symbol; later passes over the type will repair it.
       # Generic instantiation needs to know about this too. But we're lazy
       # and perform the lookup on demand instead.
-      result = newSym(skUnknown, considerQuotedIdent(c.config, n), nil, n.info,
+      result = newSym(skUnknown, considerQuotedIdent(c, n), nil, n.info,
         c.config.options)
   else:
     result = qualifiedLookUp(c, n, {checkUndeclared})
@@ -714,7 +740,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
   elif key.kind notin nkIdentKinds:
     n.sons[i] = semCustomPragma(c, it)
     return
-  let ident = considerQuotedIdent(c.config, key)
+  let ident = considerQuotedIdent(c, key)
   var userPragma = strTableGet(c.userPragmas, ident)
   if userPragma != nil:
     # number of pragmas increase/decrease with user pragma expansion
@@ -727,7 +753,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
     i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty
     dec c.instCounter
   else:
-    var k = whichKeyword(ident)
+    let k = whichKeyword(ident)
     if k in validPragmas:
       case k
       of wExportc:
@@ -736,10 +762,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wImportc:
         let name = getOptionalStr(c, it, "$1")
         cppDefine(c.config, name)
+        recordPragma(c, it, "cppdefine", name)
         makeExternImport(c, sym, name, it.info)
       of wImportCompilerProc:
         let name = getOptionalStr(c, it, "$1")
         cppDefine(c.config, name)
+        recordPragma(c, it, "cppdefine", name)
         processImportCompilerProc(c, sym, name, it.info)
       of wExtern: setExternName(c, sym, expectStrLit(c, it), it.info)
       of wImmediate:
@@ -832,6 +860,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wCompilerProc, wCore:
         noVal(c, it)           # compilerproc may not get a string!
         cppDefine(c.graph.config, sym.name.s)
+        recordPragma(c, it, "cppdefine", sym.name.s)
         if sfFromGeneric notin sym.flags: markCompilerProc(c, sym)
       of wProcVar:
         noVal(c, it)
@@ -894,8 +923,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         noVal(c, it)
         if sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfPacked)
-      of wHint: message(c.config, it.info, hintUser, expectStrLit(c, it))
-      of wWarning: message(c.config, it.info, warnUser, expectStrLit(c, it))
+      of wHint:
+        let s = expectStrLit(c, it)
+        recordPragma(c, it, "hint", s)
+        message(c.config, it.info, hintUser, s)
+      of wWarning:
+        let s = expectStrLit(c, it)
+        recordPragma(c, it, "warning", s)
+        message(c.config, it.info, warnUser, s)
       of wError:
         if sym != nil and sym.isRoutine:
           # This is subtle but correct: the error *statement* is only
@@ -905,15 +940,23 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
           noVal(c, it)
           incl(sym.flags, sfError)
         else:
-          localError(c.config, it.info, errUser, expectStrLit(c, it))
+          let s = expectStrLit(c, it)
+          recordPragma(c, it, "error", s)
+          localError(c.config, it.info, errUser, s)
       of wFatal: fatal(c.config, it.info, errUser, expectStrLit(c, it))
       of wDefine: processDefine(c, it)
       of wUndef: processUndef(c, it)
       of wCompile: processCompile(c, it)
       of wLink: processCommonLink(c, it, linkNormal)
       of wLinksys: processCommonLink(c, it, linkSys)
-      of wPassl: extccomp.addLinkOption(c.config, expectStrLit(c, it))
-      of wPassc: extccomp.addCompileOption(c.config, expectStrLit(c, it))
+      of wPassl:
+        let s = expectStrLit(c, it)
+        extccomp.addLinkOption(c.config, s)
+        recordPragma(c, it, "passl", s)
+      of wPassc:
+        let s = expectStrLit(c, it)
+        extccomp.addCompileOption(c.config, s)
+        recordPragma(c, it, "passc", s)
       of wBreakpoint: pragmaBreakpoint(c, it)
       of wWatchPoint: pragmaWatchpoint(c, it)
       of wPush:
@@ -935,13 +978,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wCodegenDecl: processCodegenDecl(c, it, sym)
       of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
          wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
-         wLinedir, wStacktrace, wLinetrace, wOptimization, wMovechecks,
-         wCallconv,
-         wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks,
-         wPatterns:
-        if processOption(c, it):
-          # calling conventions (boring...):
-          localError(c.config, it.info, "option expected")
+         wLinedir, wOptimization, wMovechecks, wCallconv, wDebugger, wProfiler,
+         wFloatchecks, wNanChecks, wInfChecks, wPatterns:
+        processOption(c, it, c.config.options)
+      of wStacktrace, wLinetrace:
+        if sym.kind in {skProc, skMethod, skConverter}:
+          processOption(c, it, sym.options)
+        else:
+          processOption(c, it, c.config.options)
       of FirstCallConv..LastCallConv:
         assert(sym != nil)
         if sym.typ == nil: invalidPragma(c, it)
@@ -971,7 +1015,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wByRef:
         noVal(c, it)
         if sym == nil or sym.typ == nil:
-          if processOption(c, it): localError(c.config, it.info, "option expected")
+          processOption(c, it, c.config.options)
         else:
           incl(sym.typ.flags, tfByRef)
       of wByCopy:
@@ -1021,9 +1065,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         processExperimental(c, it, sym)
       of wThis:
         if it.kind in nkPragmaCallKinds and it.len == 2:
-          c.selfName = considerQuotedIdent(c.config, it[1])
+          c.selfName = considerQuotedIdent(c, it[1])
         elif it.kind == nkIdent or it.len == 1:
-          c.selfName = getIdent("self")
+          c.selfName = getIdent(c.cache, "self")
         else:
           localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments")
       of wNoRewrite:
@@ -1051,13 +1095,13 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
     for it in c.optionStack:
       let o = it.otherPragmas
       if not o.isNil:
-        pushInfoContext(n.info)
+        pushInfoContext(c.config, n.info)
         var i = 0
         while i < o.len():
           if singlePragma(c, sym, o, i, validPragmas):
             internalError(c.config, n.info, "implicitPragmas")
           inc i
-        popInfoContext()
+        popInfoContext(c.config)
 
     if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
       localError(c.config, n.info, ".dynlib requires .exportc")
@@ -1069,8 +1113,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
       if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
 
 proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
-  if n == nil or n.sons == nil:
-    return false
+  if n == nil: return false
 
   for p in n:
     var key = if p.kind in nkPragmaCallKinds and p.len > 1: p[0] else: p
@@ -1082,7 +1125,7 @@ proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
 proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
   if n == nil: return
   var i = 0
-  while i < n.len():
+  while i < n.len:
     if singlePragma(c, sym, n, i, validPragmas): break
     inc i
 
diff --git a/compiler/prefixmatches.nim b/compiler/prefixmatches.nim
index 00e2c537d..246d1ae5e 100644
--- a/compiler/prefixmatches.nim
+++ b/compiler/prefixmatches.nim
@@ -24,7 +24,7 @@ proc prefixMatch*(p, s: string): PrefixMatch =
   # check for prefix/contains:
   while i < L:
     if s[i] == '_': inc i
-    if eq(s[i], p[0]):
+    if i < L and eq(s[i], p[0]):
       var ii = i+1
       var jj = 1
       while ii < L and jj < p.len:
@@ -43,10 +43,10 @@ proc prefixMatch*(p, s: string): PrefixMatch =
     i = 1
     var j = 1
     while i < s.len:
-      if s[i] == '_' and i < s.len-1:
+      if i < s.len-1 and s[i] == '_':
         if j < p.len and eq(p[j], s[i+1]): inc j
         else: return PrefixMatch.None
-      if s[i] in {'A'..'Z'} and s[i-1] notin {'A'..'Z'}:
+      if i < s.len and s[i] in {'A'..'Z'} and s[i-1] notin {'A'..'Z'}:
         if j < p.len and eq(p[j], s[i]): inc j
         else: return PrefixMatch.None
       inc i
diff --git a/compiler/procfind.nim b/compiler/procfind.nim
index 042947e72..3f47e7e8a 100644
--- a/compiler/procfind.nim
+++ b/compiler/procfind.nim
@@ -73,7 +73,7 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
         if (sfExported notin result.flags) and (sfExported in fn.flags):
           let message = ("public implementation '$1' has non-public " &
                          "forward declaration in $2") %
-                        [getProcHeader(result), $result.info]
+                        [getProcHeader(c.config, result), c.config$result.info]
           localError(c.config, fn.info, message)
         return
       of paramsIncompatible:
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 6ac6e797e..3ce2e157d 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -10,7 +10,7 @@
 # This module implements the renderer of the standard Nim representation.
 
 import
-  lexer, options, idents, strutils, ast, msgs, configuration
+  lexer, options, idents, strutils, ast, msgs, lineinfos
 
 type
   TRenderFlag* = enum
@@ -330,14 +330,15 @@ proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
 
 proc atom(g: TSrcGen; n: PNode): string =
   when defined(nimpretty):
+    doAssert g.config != nil, "g.config not initialized!"
     let comment = if n.info.commentOffsetA < n.info.commentOffsetB:
-                    " " & fileSection(g.fid, n.info.commentOffsetA, n.info.commentOffsetB)
+                    " " & fileSection(g.config, g.fid, n.info.commentOffsetA, n.info.commentOffsetB)
                   else:
                     ""
     if n.info.offsetA <= n.info.offsetB:
       # for some constructed tokens this can not be the case and we're better
       # off to not mess with the offset then.
-      return fileSection(g.fid, n.info.offsetA, n.info.offsetB) & comment
+      return fileSection(g.config, g.fid, n.info.offsetA, n.info.offsetB) & comment
   var f: float32
   case n.kind
   of nkEmpty: result = ""
@@ -386,8 +387,10 @@ proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
   assert(theEnd < 0)
   result = 0
   for i in countup(start, sonsLen(n) + theEnd):
-    inc(result, lsub(g, n.sons[i]))
-    inc(result, 2)            # for ``, ``
+    let param = n.sons[i]
+    if nfDefaultParam notin param.flags:
+      inc(result, lsub(g, param))
+      inc(result, 2)          # for ``, ``
   if result > 0:
     dec(result, 2)            # last does not get a comma!
 
@@ -811,8 +814,8 @@ proc gident(g: var TSrcGen, n: PNode) =
 
   var t: TTokType
   var s = atom(g, n)
-  if (s[0] in lexer.SymChars):
-    if (n.kind == nkIdent):
+  if s.len > 0 and s[0] in lexer.SymChars:
+    if n.kind == nkIdent:
       if (n.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
           (n.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
         t = tkSymbol
@@ -1452,11 +1455,12 @@ proc `$`*(n: PNode): string = n.renderTree
 
 proc renderModule*(n: PNode, infile, outfile: string,
                    renderFlags: TRenderFlags = {};
-                   fid = FileIndex(-1)) =
+                   fid = FileIndex(-1);
+                   conf: ConfigRef = nil) =
   var
     f: File
     g: TSrcGen
-  initSrcGen(g, renderFlags, newPartialConfigRef())
+  initSrcGen(g, renderFlags, conf)
   g.fid = fid
   for i in countup(0, sonsLen(n) - 1):
     gsub(g, n.sons[i])
diff --git a/compiler/reorder.nim b/compiler/reorder.nim
index d50be1d99..27b19a373 100644
--- a/compiler/reorder.nim
+++ b/compiler/reorder.nim
@@ -2,7 +2,7 @@
 import
   intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils,
   sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables,
-  configuration
+  lineinfos
 
 type
   DepN = ref object
@@ -11,11 +11,11 @@ type
     onStack: bool
     kids: seq[DepN]
     hAQ, hIS, hB, hCmd: int
-    when not defined(release):
+    when defined(debugReorder):
       expls: seq[string]
   DepG = seq[DepN]
 
-when not defined(release):
+when defined(debugReorder):
   var idNames = newTable[int, string]()
 
 proc newDepN(id: int, pnode: PNode): DepN =
@@ -30,10 +30,10 @@ proc newDepN(id: int, pnode: PNode): DepN =
   result.hIS = -1
   result.hB = -1
   result.hCmd = -1
-  when not defined(release):
+  when defined(debugReorder):
     result.expls = @[]
 
-proc accQuoted(n: PNode): PIdent =
+proc accQuoted(cache: IdentCache; n: PNode): PIdent =
   var id = ""
   for i in 0 ..< n.len:
     let x = n[i]
@@ -41,33 +41,33 @@ proc accQuoted(n: PNode): PIdent =
     of nkIdent: id.add(x.ident.s)
     of nkSym: id.add(x.sym.name.s)
     else: discard
-  result = getIdent(id)
+  result = getIdent(cache, id)
 
-proc addDecl(n: PNode; declares: var IntSet) =
+proc addDecl(cache: IdentCache; n: PNode; declares: var IntSet) =
   case n.kind
-  of nkPostfix: addDecl(n[1], declares)
-  of nkPragmaExpr: addDecl(n[0], declares)
+  of nkPostfix: addDecl(cache, n[1], declares)
+  of nkPragmaExpr: addDecl(cache, n[0], declares)
   of nkIdent:
     declares.incl n.ident.id
-    when not defined(release):
+    when defined(debugReorder):
       idNames[n.ident.id] = n.ident.s
   of nkSym:
     declares.incl n.sym.name.id
-    when not defined(release):
+    when defined(debugReorder):
       idNames[n.sym.name.id] = n.sym.name.s
   of nkAccQuoted:
-    let a = accQuoted(n)
+    let a = accQuoted(cache, n)
     declares.incl a.id
-    when not defined(release):
+    when defined(debugReorder):
       idNames[a.id] = a.s
   of nkEnumFieldDef:
-    addDecl(n[0], declares)
+    addDecl(cache, n[0], declares)
   else: discard
 
-proc computeDeps(n: PNode, declares, uses: var IntSet; topLevel: bool) =
-  template deps(n) = computeDeps(n, declares, uses, false)
+proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLevel: bool) =
+  template deps(n) = computeDeps(cache, n, declares, uses, false)
   template decl(n) =
-    if topLevel: addDecl(n, declares)
+    if topLevel: addDecl(cache, n, declares)
   case n.kind
   of procDefs, nkMacroDef, nkTemplateDef:
     decl(n[0])
@@ -93,11 +93,11 @@ proc computeDeps(n: PNode, declares, uses: var IntSet; topLevel: bool) =
       deps(n[i])
   of nkIdent: uses.incl n.ident.id
   of nkSym: uses.incl n.sym.name.id
-  of nkAccQuoted: uses.incl accQuoted(n).id
+  of nkAccQuoted: uses.incl accQuoted(cache, n).id
   of nkOpenSymChoice, nkClosedSymChoice:
     uses.incl n.sons[0].sym.name.id
   of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
-    for i in 0..<len(n): computeDeps(n[i], declares, uses, topLevel)
+    for i in 0..<len(n): computeDeps(cache, n[i], declares, uses, topLevel)
   of nkPragma:
     let a = n.sons[0]
     if a.kind == nkExprColonExpr and a.sons[0].kind == nkIdent and
@@ -136,15 +136,13 @@ proc hasIncludes(n:PNode): bool =
     if a.kind == nkIncludeStmt:
       return true
 
-proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
-                    cache: IdentCache): PNode {.procvar.} =
-  result = syntaxes.parseFile(fileIdx, cache, graph.config)
+proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode {.procvar.} =
+  result = syntaxes.parseFile(fileIdx, graph.cache, graph.config)
   graph.addDep(s, fileIdx)
   graph.addIncludeDep(FileIndex s.position, fileIdx)
 
 proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
-                    modulePath: string, includedFiles: var IntSet,
-                    cache: IdentCache): PNode =
+                    modulePath: string, includedFiles: var IntSet): PNode =
   # Parses includes and injects them in the current tree
   if not n.hasIncludes:
     return n
@@ -155,11 +153,12 @@ proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
         var f = checkModuleName(graph.config, a.sons[i])
         if f != InvalidFileIDX:
           if containsOrIncl(includedFiles, f.int):
-            localError(graph.config, a.info, "recursive dependency: '$1'" % f.toFilename)
+            localError(graph.config, a.info, "recursive dependency: '$1'" %
+              toFilename(graph.config, f))
           else:
-            let nn = includeModule(graph, module, f, cache)
+            let nn = includeModule(graph, module, f)
             let nnn = expandIncludes(graph, module, nn, modulePath,
-                                      includedFiles, cache)
+                                      includedFiles)
             excl(includedFiles, f.int)
             for b in nnn:
               result.add b
@@ -215,7 +214,7 @@ proc mergeSections(conf: ConfigRef; comps: seq[seq[DepN]], res: PNode) =
           # consecutive type and const sections
           var wmsg = "Circular dependency detected. reorder pragma may not be able to" &
             " reorder some nodes properely"
-          when not defined(release):
+          when defined(debugReorder):
             wmsg &= ":\n"
             for i in 0..<cs.len-1:
                 for j in i..<cs.len:
@@ -354,13 +353,13 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
       if j < i and nj.hasCommand and niHasCmd:
         # Preserve order for commands and calls
         ni.kids.add nj
-        when not defined(release):
+        when defined(debugReorder):
           ni.expls.add "both have commands and one comes after the other"
       elif j < i and nj.hasImportStmt:
         # Every node that comes after an import statement must
         # depend on that import
         ni.kids.add nj
-        when not defined(release):
+        when defined(debugReorder):
           ni.expls.add "parent is, or contains, an import statement and child comes after it"
       elif j < i and niHasBody and nj.hasAccQuotedDef:
         # Every function, macro, template... with a body depends
@@ -368,13 +367,13 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
         # That's because it is hard to detect the use of functions
         # like "[]=", "[]", "or" ... in their bodies.
         ni.kids.add nj
-        when not defined(release):
+        when defined(debugReorder):
           ni.expls.add "one declares a quoted identifier and the other has a body and comes after it"
       elif j < i and niHasBody and not nj.hasBody and
         intersects(deps[i][0], declares):
           # Keep function declaration before function definition
           ni.kids.add nj
-          when not defined(release):
+          when defined(debugReorder):
             for dep in deps[i][0]:
               if dep in declares:
                 ni.expls.add "one declares \"" & idNames[dep] & "\" and the other defines it"
@@ -382,7 +381,7 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
         for d in declares:
           if uses.contains(d):
             ni.kids.add nj
-            when not defined(release):
+            when defined(debugReorder):
               ni.expls.add "one declares \"" & idNames[d] & "\" and the other uses it"
 
 proc strongConnect(v: var DepN, idx: var int, s: var seq[DepN],
@@ -426,19 +425,19 @@ proc hasForbiddenPragma(n: PNode): bool =
         a[0].ident.s == "push":
           return true
 
-proc reorder*(graph: ModuleGraph, n: PNode, module: PSym, cache: IdentCache): PNode =
+proc reorder*(graph: ModuleGraph, n: PNode, module: PSym): PNode =
   if n.hasForbiddenPragma:
     return n
   var includedFiles = initIntSet()
-  let mpath = module.fileIdx.toFullPath
+  let mpath = toFullPath(graph.config, module.fileIdx)
   let n = expandIncludes(graph, module, n, mpath,
-                          includedFiles, cache).splitSections
+                          includedFiles).splitSections
   result = newNodeI(nkStmtList, n.info)
   var deps = newSeq[(IntSet, IntSet)](n.len)
   for i in 0..<n.len:
     deps[i][0] = initIntSet()
     deps[i][1] = initIntSet()
-    computeDeps(n[i], deps[i][0], deps[i][1], true)
+    computeDeps(graph.cache, n[i], deps[i][0], deps[i][1], true)
 
   var g = buildGraph(n, deps)
   let comps = getStrongComponents(g)
diff --git a/compiler/rod.nim b/compiler/rod.nim
index c144f15ef..f9208f5dc 100644
--- a/compiler/rod.nim
+++ b/compiler/rod.nim
@@ -9,18 +9,21 @@
 
 ## This module implements the canonalization for the various caching mechanisms.
 
-import ast, idgen, msgs
+import ast, idgen, lineinfos, msgs, incremental, modulegraphs
 
-when not defined(nimSymbolfiles):
-  template setupModuleCache* = discard
-  template storeNode*(module: PSym; n: PNode) = discard
-  template loadNode*(module: PSym; index: var int): PNode = PNode(nil)
+when not nimIncremental:
+  template setupModuleCache*(g: ModuleGraph) = discard
+  template storeNode*(g: ModuleGraph; module: PSym; n: PNode) = discard
+  template loadNode*(g: ModuleGraph; module: PSym): PNode = newNode(nkStmtList)
 
-  template getModuleId*(fileIdx: FileIndex; fullpath: string): int = getID()
+  template getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: string): int = getID()
 
-  template addModuleDep*(module, fileIdx: FileIndex; isIncludeFile: bool) = discard
+  template addModuleDep*(g: ModuleGraph; module, fileIdx: FileIndex; isIncludeFile: bool) = discard
 
-  template storeRemaining*(module: PSym) = discard
+  template storeRemaining*(g: ModuleGraph; module: PSym) = discard
 
 else:
   include rodimpl
+
+  # idea for testing all this logic: *Always* load the AST from the DB, whether
+  # we already have it in RAM or not!
diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim
index aff4f6909..7d24e4e67 100644
--- a/compiler/rodimpl.nim
+++ b/compiler/rodimpl.nim
@@ -10,59 +10,67 @@
 ## This module implements the new compilation cache.
 
 import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types,
-  renderer, rodutils, std / sha1, idents, astalgo, magicsys
+  renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp,
+  btrees, trees, condsyms, nversion
 
 ## Todo:
-## - Implement the 'import' replay logic so that the codegen runs over
-##   dependent modules.
-## - Make conditional symbols and the configuration part of a module's
-##   dependencies.
-## - Test multi methods.
-## - Implement the limited VM support based on sets.
-## - Depencency computation should use signature hashes in order to
+## - Dependency computation should use *signature* hashes in order to
 ##   avoid recompiling dependent modules.
+## - Patch the rest of the compiler to do lazy loading of proc bodies.
+## - Patch the C codegen to cache proc bodies and maybe types.
 
-var db: DbConn
+template db(): DbConn = g.incr.db
 
-proc hashFileCached(fileIdx: int32; fullpath: string): string =
-  result = msgs.getHash(fileIdx)
-  if result.len == 0:
-    result = $secureHashFile(fullpath)
-    msgs.setHash(fileIdx, result)
+proc encodeConfig(g: ModuleGraph): string =
+  result = newStringOfCap(100)
+  result.add RodFileVersion
+  for d in definedSymbolNames(g.config.symbols):
+    result.add ' '
+    result.add d
 
-proc needsRecompile(fileIdx: int32; fullpath: string; cycleCheck: var IntSet): bool =
+  template serialize(field) =
+    result.add ' '
+    result.add($g.config.field)
+
+  depConfigFields(serialize)
+
+proc needsRecompile(g: ModuleGraph; fileIdx: FileIndex; fullpath: string;
+                    cycleCheck: var IntSet): bool =
   let root = db.getRow(sql"select id, fullhash from filenames where fullpath = ?",
     fullpath)
   if root[0].len == 0: return true
-  if root[1] != hashFileCached(fileIdx, fullpath):
+  if root[1] != hashFileCached(g.config, fileIdx, fullpath):
     return true
   # cycle detection: assume "not changed" is correct.
-  if cycleCheck.containsOrIncl(fileIdx):
+  if cycleCheck.containsOrIncl(int fileIdx):
     return false
   # check dependencies (recursively):
   for row in db.fastRows(sql"select fullpath from filenames where id in (select dependency from deps where module = ?)",
                          root[0]):
     let dep = row[0]
-    if needsRecompile(dep.fileInfoIdx, dep, cycleCheck):
+    if needsRecompile(g, g.config.fileInfoIdx(dep), dep, cycleCheck):
       return true
   return false
 
-proc getModuleId*(fileIdx: int32; fullpath: string): int =
-  if gSymbolFiles != v2Sf: return getID()
-  let module = db.getRow(
-    sql"select id, fullHash from modules where fullpath = ?", fullpath)
-  let currentFullhash = hashFileCached(fileIdx, fullpath)
+proc getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: string): int =
+  if g.config.symbolFiles in {disabledSf, writeOnlySf} or
+     g.incr.configChanged:
+    return getID()
+  let module = g.incr.db.getRow(
+    sql"select id, fullHash, nimid from modules where fullpath = ?", fullpath)
+  let currentFullhash = hashFileCached(g.config, fileIdx, fullpath)
   if module[0].len == 0:
-    result = int db.insertID(sql"insert into modules(fullpath, interfHash, fullHash) values (?, ?, ?)",
-      fullpath, "", currentFullhash)
+    result = getID()
+    db.exec(sql"insert into modules(fullpath, interfHash, fullHash, nimid) values (?, ?, ?, ?)",
+      fullpath, "", currentFullhash, result)
   else:
-    result = parseInt(module[0])
+    result = parseInt(module[2])
     if currentFullhash == module[1]:
-      # not changed, so use the cached AST (even if it might be wrong
-      # due to its dependencies):
+      # not changed, so use the cached AST:
       doAssert(result != 0)
       var cycleCheck = initIntSet()
-      if not needsRecompile(fileIdx, fullpath, cycleCheck):
+      if not needsRecompile(g, fileIdx, fullpath, cycleCheck):
+        echo "cached successfully! ", fullpath
         return -result
     db.exec(sql"update modules set fullHash = ? where id = ?", currentFullhash, module[0])
     db.exec(sql"delete from deps where module = ?", module[0])
@@ -71,60 +79,17 @@ proc getModuleId*(fileIdx: int32; fullpath: string): int =
     db.exec(sql"delete from toplevelstmts where module = ?", module[0])
     db.exec(sql"delete from statics where module = ?", module[0])
 
-type
-  TRodWriter = object
-    module: PSym
-    sstack: seq[PSym]          # a stack of symbols to process
-    tstack: seq[PType]         # a stack of types to process
-    tmarks, smarks: IntSet
-    forwardedSyms: seq[PSym]
-
-  PRodWriter = var TRodWriter
-
-proc initRodWriter(module: PSym): TRodWriter =
-  result = TRodWriter(module: module, sstack: @[], tstack: @[],
-    tmarks: initIntSet(), smarks: initIntSet(), forwardedSyms: @[])
-
-when false:
-  proc getDefines(): string =
-    result = ""
-    for d in definedSymbolNames():
-      if result.len != 0: add(result, " ")
-      add(result, d)
-
-const
-  rodNL = "\L"
-
-proc pushType(w: PRodWriter, t: PType) =
+proc pushType(w: var Writer, t: PType) =
   if not containsOrIncl(w.tmarks, t.id):
     w.tstack.add(t)
 
-proc pushSym(w: PRodWriter, s: PSym) =
+proc pushSym(w: var Writer, s: PSym) =
   if not containsOrIncl(w.smarks, s.id):
     w.sstack.add(s)
 
-proc toDbFileId(fileIdx: int32): int =
-  if fileIdx == -1: return -1
-  let fullpath = fileIdx.toFullPath
-  let row = db.getRow(sql"select id, fullhash from filenames where fullpath = ?",
-    fullpath)
-  let id = row[0]
-  let fullhash = hashFileCached(fileIdx, fullpath)
-  if id.len == 0:
-    result = int db.insertID(sql"insert into filenames(fullpath, fullhash) values (?, ?)",
-      fullpath, fullhash)
-  else:
-    if row[1] != fullhash:
-      db.exec(sql"update filenames set fullhash = ? where fullpath = ?", fullhash, fullpath)
-    result = parseInt(id)
-
-proc fromDbFileId(dbId: int): int32 =
-  if dbId == -1: return -1
-  let fullpath = db.getValue(sql"select fullpath from filenames where id = ?", dbId)
-  doAssert fullpath.len > 0, "cannot find file name for DB ID " & $dbId
-  result = fileInfoIdx(fullpath)
+template w: untyped = g.incr.w
 
-proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
+proc encodeNode(g: ModuleGraph; fInfo: TLineInfo, n: PNode,
                 result: var string) =
   if n == nil:
     # nil nodes have to be stored too:
@@ -139,14 +104,14 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
     result.add('?')
     encodeVInt(n.info.col, result)
     result.add(',')
-    encodeVInt(n.info.line, result)
+    encodeVInt(int n.info.line, result)
     result.add(',')
-    encodeVInt(toDbFileId(n.info.fileIndex), result)
+    encodeVInt(toDbFileId(g.incr, g.config, n.info.fileIndex), result)
   elif fInfo.line != n.info.line:
     result.add('?')
     encodeVInt(n.info.col, result)
     result.add(',')
-    encodeVInt(n.info.line, result)
+    encodeVInt(int n.info.line, result)
   elif fInfo.col != n.info.col:
     result.add('?')
     encodeVInt(n.info.col, result)
@@ -182,10 +147,10 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
     pushSym(w, n.sym)
   else:
     for i in countup(0, sonsLen(n) - 1):
-      encodeNode(w, n.info, n.sons[i], result)
+      encodeNode(g, n.info, n.sons[i], result)
   add(result, ')')
 
-proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
+proc encodeLoc(g: ModuleGraph; loc: TLoc, result: var string) =
   var oldLen = result.len
   result.add('<')
   if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
@@ -197,9 +162,7 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
     encodeVInt(cast[int32](loc.flags), result)
   if loc.lode != nil:
     add(result, '^')
-    encodeNode(w, unknownLineInfo(), loc.lode, result)
-    #encodeVInt(cast[int32](loc.t.id), result)
-    #pushType(w, loc.t)
+    encodeNode(g, unknownLineInfo(), loc.lode, result)
   if loc.r != nil:
     add(result, '!')
     encodeStr($loc.r, result)
@@ -209,13 +172,13 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
   else:
     add(result, '>')
 
-proc encodeType(w: PRodWriter, t: PType, result: var string) =
+proc encodeType(g: ModuleGraph, t: PType, result: var string) =
   if t == nil:
     # nil nodes have to be stored too:
     result.add("[]")
     return
   # we need no surrounding [] here because the type is in a line of its own
-  if t.kind == tyForward: internalError("encodeType: tyForward")
+  if t.kind == tyForward: internalError(g.config, "encodeType: tyForward")
   # for the new rodfile viewer we use a preceding [ so that the data section
   # can easily be disambiguated:
   add(result, '[')
@@ -223,7 +186,7 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
   add(result, '+')
   encodeVInt(t.id, result)
   if t.n != nil:
-    encodeNode(w, w.module.info, t.n, result)
+    encodeNode(g, unknownLineInfo(), t.n, result)
   if t.flags != {}:
     add(result, '$')
     encodeVInt(cast[int32](t.flags), result)
@@ -269,7 +232,7 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
     add(result, '\20')
     encodeVInt(s.id, result)
     pushSym(w, s)
-  encodeLoc(w, t.loc, result)
+  encodeLoc(g, t.loc, result)
   for i in countup(0, sonsLen(t) - 1):
     if t.sons[i] == nil:
       add(result, "^()")
@@ -278,15 +241,15 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
       encodeVInt(t.sons[i].id, result)
       pushType(w, t.sons[i])
 
-proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
+proc encodeLib(g: ModuleGraph, lib: PLib, info: TLineInfo, result: var string) =
   add(result, '|')
   encodeVInt(ord(lib.kind), result)
   add(result, '|')
   encodeStr($lib.name, result)
   add(result, '|')
-  encodeNode(w, info, lib.path, result)
+  encodeNode(g, info, lib.path, result)
 
-proc encodeInstantiations(w: PRodWriter; s: seq[PInstantiation];
+proc encodeInstantiations(g: ModuleGraph; s: seq[PInstantiation];
                           result: var string) =
   for t in s:
     result.add('\15')
@@ -299,7 +262,7 @@ proc encodeInstantiations(w: PRodWriter; s: seq[PInstantiation];
     result.add('\20')
     encodeVInt(t.compilesId, result)
 
-proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
+proc encodeSym(g: ModuleGraph, s: PSym, result: var string) =
   if s == nil:
     # nil nodes have to be stored too:
     result.add("{}")
@@ -317,9 +280,9 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
   result.add('?')
   if s.info.col != -1'i16: encodeVInt(s.info.col, result)
   result.add(',')
-  if s.info.line != -1'i16: encodeVInt(s.info.line, result)
+  encodeVInt(int s.info.line, result)
   result.add(',')
-  encodeVInt(toDbFileId(s.info.fileIndex), result)
+  encodeVInt(toDbFileId(g.incr, g.config, s.info.fileIndex), result)
   if s.owner != nil:
     result.add('*')
     encodeVInt(s.owner.id, result)
@@ -338,11 +301,11 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
   if s.offset != - 1:
     result.add('`')
     encodeVInt(s.offset, result)
-  encodeLoc(w, s.loc, result)
-  if s.annex != nil: encodeLib(w, s.annex, s.info, result)
+  encodeLoc(g, s.loc, result)
+  if s.annex != nil: encodeLib(g, s.annex, s.info, result)
   if s.constraint != nil:
     add(result, '#')
-    encodeNode(w, unknownLineInfo(), s.constraint, result)
+    encodeNode(g, unknownLineInfo(), s.constraint, result)
   case s.kind
   of skType, skGenericParam:
     for t in s.typeInstCache:
@@ -350,13 +313,13 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
       encodeVInt(t.id, result)
       pushType(w, t)
   of routineKinds:
-    encodeInstantiations(w, s.procInstCache, result)
+    encodeInstantiations(g, s.procInstCache, result)
     if s.gcUnsafetyReason != nil:
       result.add('\16')
       encodeVInt(s.gcUnsafetyReason.id, result)
       pushSym(w, s.gcUnsafetyReason)
   of skModule, skPackage:
-    encodeInstantiations(w, s.usedGenerics, result)
+    encodeInstantiations(g, s.usedGenerics, result)
     # we don't serialize:
     #tab*: TStrTable         # interface table for modules
   of skLet, skVar, skField, skForVar:
@@ -374,105 +337,93 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
     # we used to attempt to save space here by only storing a dummy AST if
     # it is not necessary, but Nim's heavy compile-time evaluation features
     # make that unfeasible nowadays:
-    encodeNode(w, s.info, s.ast, result)
+    encodeNode(g, s.info, s.ast, result)
 
-proc storeSym(w: PRodWriter; s: PSym) =
+proc storeSym(g: ModuleGraph; s: PSym) =
   if sfForward in s.flags and s.kind != skModule:
     w.forwardedSyms.add s
     return
   var buf = newStringOfCap(160)
-  encodeSym(w, s, buf)
+  encodeSym(g, s, buf)
   # XXX only store the name for exported symbols in order to speed up lookup
   # times once we enable the skStub logic.
+  let m = getModule(s)
+  let mid = if m == nil: 0 else: abs(m.id)
   db.exec(sql"insert into syms(nimid, module, name, data, exported) values (?, ?, ?, ?, ?)",
-    s.id, abs(w.module.id), s.name.s, buf, ord(sfExported in s.flags))
+    s.id, mid, s.name.s, buf, ord(sfExported in s.flags))
 
-proc storeType(w: PRodWriter; t: PType) =
+proc storeType(g: ModuleGraph; t: PType) =
   var buf = newStringOfCap(160)
-  encodeType(w, t, buf)
+  encodeType(g, t, buf)
+  let m = if t.owner != nil: getModule(t.owner) else: nil
+  let mid = if m == nil: 0 else: abs(m.id)
   db.exec(sql"insert into types(nimid, module, data) values (?, ?, ?)",
-    t.id, abs(w.module.id), buf)
-
-var w = initRodWriter(nil)
+    t.id, mid, buf)
 
-proc storeNode*(module: PSym; n: PNode) =
-  if gSymbolFiles != v2Sf: return
-  w.module = module
+proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) =
+  if g.config.symbolFiles == disabledSf: return
   var buf = newStringOfCap(160)
-  encodeNode(w, module.info, n, buf)
+  encodeNode(g, module.info, n, buf)
   db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)",
     abs(module.id), module.offset, buf)
   inc module.offset
   var i = 0
   while true:
     if i > 10_000:
-      quit "loop never ends!"
+      doAssert false, "loop never ends!"
     if w.sstack.len > 0:
       let s = w.sstack.pop()
       when false:
         echo "popped ", s.name.s, " ", s.id
-      storeSym(w, s)
+      storeSym(g, s)
     elif w.tstack.len > 0:
       let t = w.tstack.pop()
-      storeType(w, t)
+      storeType(g, t)
       when false:
         echo "popped type ", typeToString(t), " ", t.id
     else:
       break
     inc i
 
-proc storeRemaining*(module: PSym) =
-  if gSymbolFiles != v2Sf: return
-  w.module = module
+proc recordStmt*(g: ModuleGraph; module: PSym; n: PNode) =
+  storeNode(g, module, n)
+
+proc storeRemaining*(g: ModuleGraph; module: PSym) =
+  if g.config.symbolFiles == disabledSf: return
+  var stillForwarded: seq[PSym] = @[]
   for s in w.forwardedSyms:
-    assert sfForward notin s.flags
-    storeSym(w, s)
-  w.forwardedSyms.setLen 0
+    if sfForward notin s.flags:
+      storeSym(g, s)
+    else:
+      stillForwarded.add s
+  swap w.forwardedSyms, stillForwarded
 
 # ---------------- decoder -----------------------------------
-type
-  TRodReader = object
-    module: PSym
-    #sstack: seq[(PSym, ptr PSym)]       # a stack of symbols to process
-    #tstack: seq[(PType, ptr PType)]     # a stack of types to process
-
-    #tmarks, smarks: IntSet
-    syms: Table[int, PSym] ## XXX make this more efficients
-    types: Table[int, PType]
-    cache: IdentCache
 
+type
   BlobReader = object
     s: string
     pos: int
 
-  PRodReader = var TRodReader
-
-proc initRodReader(cache: IdentCache): TRodReader =
-  TRodReader(module: nil,
-    syms: initTable[int, PSym](), types: initTable[int, PType](),
-    cache: cache)
-
-var gr = initRodReader(newIdentCache())
-
 using
-  r: PRodReader
   b: var BlobReader
+  g: ModuleGraph
 
-proc loadSym(r; id: int, info: TLineInfo): PSym
-proc loadType(r; id: int, info: TLineInfo): PType
+proc loadSym(g; id: int, info: TLineInfo): PSym
+proc loadType(g; id: int, info: TLineInfo): PType
 
-proc decodeLineInfo(r; b; info: var TLineInfo) =
+proc decodeLineInfo(g; b; info: var TLineInfo) =
   if b.s[b.pos] == '?':
     inc(b.pos)
     if b.s[b.pos] == ',': info.col = -1'i16
     else: info.col = int16(decodeVInt(b.s, b.pos))
     if b.s[b.pos] == ',':
       inc(b.pos)
-      if b.s[b.pos] == ',': info.line = -1'i16
-      else: info.line = int16(decodeVInt(b.s, b.pos))
+      if b.s[b.pos] == ',': info.line = 0'u16
+      else: info.line = uint16(decodeVInt(b.s, b.pos))
       if b.s[b.pos] == ',':
         inc(b.pos)
-        info.fileIndex = fromDbFileId(decodeVInt(b.s, b.pos))
+        info.fileIndex = fromDbFileId(g.incr, g.config, decodeVInt(b.s, b.pos))
 
 proc skipNode(b) =
   assert b.s[b.pos] == '('
@@ -488,7 +439,7 @@ proc skipNode(b) =
     inc pos
   b.pos = pos+1 # skip ')'
 
-proc decodeNodeLazyBody(r; b; fInfo: TLineInfo,
+proc decodeNodeLazyBody(g; b; fInfo: TLineInfo,
                         belongsTo: PSym): PNode =
   result = nil
   if b.s[b.pos] == '(':
@@ -497,14 +448,14 @@ proc decodeNodeLazyBody(r; b; fInfo: TLineInfo,
       inc(b.pos)
       return                  # nil node
     result = newNodeI(TNodeKind(decodeVInt(b.s, b.pos)), fInfo)
-    decodeLineInfo(r, b, result.info)
+    decodeLineInfo(g, b, result.info)
     if b.s[b.pos] == '$':
       inc(b.pos)
       result.flags = cast[TNodeFlags](int32(decodeVInt(b.s, b.pos)))
     if b.s[b.pos] == '^':
       inc(b.pos)
       var id = decodeVInt(b.s, b.pos)
-      result.typ = loadType(r, id, result.info)
+      result.typ = loadType(g, id, result.info)
     case result.kind
     of nkCharLit..nkUInt64Lit:
       if b.s[b.pos] == '!':
@@ -525,16 +476,16 @@ proc decodeNodeLazyBody(r; b; fInfo: TLineInfo,
       if b.s[b.pos] == '!':
         inc(b.pos)
         var fl = decodeStr(b.s, b.pos)
-        result.ident = r.cache.getIdent(fl)
+        result.ident = g.cache.getIdent(fl)
       else:
-        internalError(result.info, "decodeNode: nkIdent")
+        internalError(g.config, result.info, "decodeNode: nkIdent")
     of nkSym:
       if b.s[b.pos] == '!':
         inc(b.pos)
         var id = decodeVInt(b.s, b.pos)
-        result.sym = loadSym(r, id, result.info)
+        result.sym = loadSym(g, id, result.info)
       else:
-        internalError(result.info, "decodeNode: nkSym")
+        internalError(g.config, result.info, "decodeNode: nkSym")
     else:
       var i = 0
       while b.s[b.pos] != ')':
@@ -545,17 +496,17 @@ proc decodeNodeLazyBody(r; b; fInfo: TLineInfo,
             skipNode(b)
           else:
             discard
-        addSonNilAllowed(result, decodeNodeLazyBody(r, b, result.info, nil))
+        addSonNilAllowed(result, decodeNodeLazyBody(g, b, result.info, nil))
         inc i
     if b.s[b.pos] == ')': inc(b.pos)
-    else: internalError(result.info, "decodeNode: ')' missing")
+    else: internalError(g.config, result.info, "decodeNode: ')' missing")
   else:
-    internalError(fInfo, "decodeNode: '(' missing " & $b.pos)
+    internalError(g.config, fInfo, "decodeNode: '(' missing " & $b.pos)
 
-proc decodeNode(r; b; fInfo: TLineInfo): PNode =
-  result = decodeNodeLazyBody(r, b, fInfo, nil)
+proc decodeNode(g; b; fInfo: TLineInfo): PNode =
+  result = decodeNodeLazyBody(g, b, fInfo, nil)
 
-proc decodeLoc(r; b; loc: var TLoc, info: TLineInfo) =
+proc decodeLoc(g; b; loc: var TLoc, info: TLineInfo) =
   if b.s[b.pos] == '<':
     inc(b.pos)
     if b.s[b.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}:
@@ -574,7 +525,7 @@ proc decodeLoc(r; b; loc: var TLoc, info: TLineInfo) =
       loc.flags = {}
     if b.s[b.pos] == '^':
       inc(b.pos)
-      loc.lode = decodeNode(r, b, info)
+      loc.lode = decodeNode(g, b, info)
       # rrGetType(b, decodeVInt(b.s, b.pos), info)
     else:
       loc.lode = nil
@@ -584,19 +535,21 @@ proc decodeLoc(r; b; loc: var TLoc, info: TLineInfo) =
     else:
       loc.r = nil
     if b.s[b.pos] == '>': inc(b.pos)
-    else: internalError(info, "decodeLoc " & b.s[b.pos])
+    else: internalError(g.config, info, "decodeLoc " & b.s[b.pos])
 
-proc loadBlob(query: SqlQuery; id: int): BlobReader =
+proc loadBlob(g; query: SqlQuery; id: int): BlobReader =
   let blob = db.getValue(query, id)
   if blob.len == 0:
-    internalError("symbolfiles: cannot find ID " & $ id)
+    internalError(g.config, "symbolfiles: cannot find ID " & $ id)
   result = BlobReader(pos: 0)
   shallowCopy(result.s, blob)
+  # ensure we can read without index checks:
+  result.s.add '\0'
 
-proc loadType(r; id: int; info: TLineInfo): PType =
-  result = r.types.getOrDefault(id)
+proc loadType(g; id: int; info: TLineInfo): PType =
+  result = g.incr.r.types.getOrDefault(id)
   if result != nil: return result
-  var b = loadBlob(sql"select data from types where nimid = ?", id)
+  var b = loadBlob(g, sql"select data from types where nimid = ?", id)
 
   if b.s[b.pos] == '[':
     inc(b.pos)
@@ -611,10 +564,10 @@ proc loadType(r; id: int; info: TLineInfo): PType =
     setId(result.id)
     #if debugIds: registerID(result)
   else:
-    internalError(info, "decodeType: no id")
+    internalError(g.config, info, "decodeType: no id")
   # here this also avoids endless recursion for recursive type
-  r.types[result.id] = result
-  if b.s[b.pos] == '(': result.n = decodeNode(r, b, unknownLineInfo())
+  g.incr.r.types.add(result.id, result)
+  if b.s[b.pos] == '(': result.n = decodeNode(g, b, unknownLineInfo())
   if b.s[b.pos] == '$':
     inc(b.pos)
     result.flags = cast[TTypeFlags](int32(decodeVInt(b.s, b.pos)))
@@ -623,10 +576,10 @@ proc loadType(r; id: int; info: TLineInfo): PType =
     result.callConv = TCallingConvention(decodeVInt(b.s, b.pos))
   if b.s[b.pos] == '*':
     inc(b.pos)
-    result.owner = loadSym(r, decodeVInt(b.s, b.pos), info)
+    result.owner = loadSym(g, decodeVInt(b.s, b.pos), info)
   if b.s[b.pos] == '&':
     inc(b.pos)
-    result.sym = loadSym(r, decodeVInt(b.s, b.pos), info)
+    result.sym = loadSym(g, decodeVInt(b.s, b.pos), info)
   if b.s[b.pos] == '/':
     inc(b.pos)
     result.size = decodeVInt(b.s, b.pos)
@@ -646,65 +599,65 @@ proc loadType(r; id: int; info: TLineInfo): PType =
 
   if b.s[b.pos] == '\15':
     inc(b.pos)
-    result.destructor = loadSym(r, decodeVInt(b.s, b.pos), info)
+    result.destructor = loadSym(g, decodeVInt(b.s, b.pos), info)
   if b.s[b.pos] == '\16':
     inc(b.pos)
-    result.deepCopy = loadSym(r, decodeVInt(b.s, b.pos), info)
+    result.deepCopy = loadSym(g, decodeVInt(b.s, b.pos), info)
   if b.s[b.pos] == '\17':
     inc(b.pos)
-    result.assignment = loadSym(r, decodeVInt(b.s, b.pos), info)
+    result.assignment = loadSym(g, decodeVInt(b.s, b.pos), info)
   if b.s[b.pos] == '\18':
     inc(b.pos)
-    result.sink = loadSym(r, decodeVInt(b.s, b.pos), info)
+    result.sink = loadSym(g, decodeVInt(b.s, b.pos), info)
   while b.s[b.pos] == '\19':
     inc(b.pos)
     let x = decodeVInt(b.s, b.pos)
     doAssert b.s[b.pos] == '\20'
     inc(b.pos)
-    let y = loadSym(r, decodeVInt(b.s, b.pos), info)
+    let y = loadSym(g, decodeVInt(b.s, b.pos), info)
     result.methods.safeAdd((x, y))
-  decodeLoc(r, b, result.loc, info)
+  decodeLoc(g, b, result.loc, info)
   while b.s[b.pos] == '^':
     inc(b.pos)
     if b.s[b.pos] == '(':
       inc(b.pos)
       if b.s[b.pos] == ')': inc(b.pos)
-      else: internalError(info, "decodeType ^(" & b.s[b.pos])
+      else: internalError(g.config, info, "decodeType ^(" & b.s[b.pos])
       rawAddSon(result, nil)
     else:
-      var d = decodeVInt(b.s, b.pos)
-      rawAddSon(result, loadType(r, d, info))
+      let d = decodeVInt(b.s, b.pos)
+      rawAddSon(result, loadType(g, d, info))
 
-proc decodeLib(r; b; info: TLineInfo): PLib =
+proc decodeLib(g; b; info: TLineInfo): PLib =
   result = nil
   if b.s[b.pos] == '|':
     new(result)
     inc(b.pos)
     result.kind = TLibKind(decodeVInt(b.s, b.pos))
-    if b.s[b.pos] != '|': internalError("decodeLib: 1")
+    if b.s[b.pos] != '|': internalError(g.config, "decodeLib: 1")
     inc(b.pos)
     result.name = rope(decodeStr(b.s, b.pos))
-    if b.s[b.pos] != '|': internalError("decodeLib: 2")
+    if b.s[b.pos] != '|': internalError(g.config, "decodeLib: 2")
     inc(b.pos)
-    result.path = decodeNode(r, b, info)
+    result.path = decodeNode(g, b, info)
 
-proc decodeInstantiations(r; b; info: TLineInfo;
+proc decodeInstantiations(g; b; info: TLineInfo;
                           s: var seq[PInstantiation]) =
   while b.s[b.pos] == '\15':
     inc(b.pos)
     var ii: PInstantiation
     new ii
-    ii.sym = loadSym(r, decodeVInt(b.s, b.pos), info)
+    ii.sym = loadSym(g, decodeVInt(b.s, b.pos), info)
     ii.concreteTypes = @[]
     while b.s[b.pos] == '\17':
       inc(b.pos)
-      ii.concreteTypes.add loadType(r, decodeVInt(b.s, b.pos), info)
+      ii.concreteTypes.add loadType(g, decodeVInt(b.s, b.pos), info)
     if b.s[b.pos] == '\20':
       inc(b.pos)
       ii.compilesId = decodeVInt(b.s, b.pos)
     s.safeAdd ii
 
-proc loadSymFromBlob(r; b; info: TLineInfo): PSym =
+proc loadSymFromBlob(g; b; info: TLineInfo): PSym =
   if b.s[b.pos] == '{':
     inc(b.pos)
     if b.s[b.pos] == '}':
@@ -717,26 +670,26 @@ proc loadSymFromBlob(r; b; info: TLineInfo): PSym =
     id = decodeVInt(b.s, b.pos)
     setId(id)
   else:
-    internalError(info, "decodeSym: no id")
+    internalError(g.config, info, "decodeSym: no id")
   var ident: PIdent
   if b.s[b.pos] == '&':
     inc(b.pos)
-    ident = r.cache.getIdent(decodeStr(b.s, b.pos))
+    ident = g.cache.getIdent(decodeStr(b.s, b.pos))
   else:
-    internalError(info, "decodeSym: no ident")
+    internalError(g.config, info, "decodeSym: no ident")
   #echo "decoding: {", ident.s
   new(result)
   result.id = id
   result.kind = k
   result.name = ident         # read the rest of the symbol description:
-  r.syms[result.id] = result
+  g.incr.r.syms.add(result.id, result)
   if b.s[b.pos] == '^':
     inc(b.pos)
-    result.typ = loadType(r, decodeVInt(b.s, b.pos), info)
-  decodeLineInfo(r, b, result.info)
+    result.typ = loadType(g, decodeVInt(b.s, b.pos), info)
+  decodeLineInfo(g, b, result.info)
   if b.s[b.pos] == '*':
     inc(b.pos)
-    result.owner = loadSym(r, decodeVInt(b.s, b.pos), result.info)
+    result.owner = loadSym(g, decodeVInt(b.s, b.pos), result.info)
   if b.s[b.pos] == '$':
     inc(b.pos)
     result.flags = cast[TSymFlags](int32(decodeVInt(b.s, b.pos)))
@@ -746,8 +699,6 @@ proc loadSymFromBlob(r; b; info: TLineInfo): PSym =
   if b.s[b.pos] == '!':
     inc(b.pos)
     result.options = cast[TOptions](int32(decodeVInt(b.s, b.pos)))
-  else:
-    result.options = r.module.options
   if b.s[b.pos] == '%':
     inc(b.pos)
     result.position = decodeVInt(b.s, b.pos)
@@ -755,28 +706,28 @@ proc loadSymFromBlob(r; b; info: TLineInfo): PSym =
     inc(b.pos)
     result.offset = decodeVInt(b.s, b.pos)
   else:
-    result.offset = - 1
-  decodeLoc(r, b, result.loc, result.info)
-  result.annex = decodeLib(r, b, info)
+    result.offset = -1
+  decodeLoc(g, b, result.loc, result.info)
+  result.annex = decodeLib(g, b, info)
   if b.s[b.pos] == '#':
     inc(b.pos)
-    result.constraint = decodeNode(r, b, unknownLineInfo())
+    result.constraint = decodeNode(g, b, unknownLineInfo())
   case result.kind
   of skType, skGenericParam:
     while b.s[b.pos] == '\14':
       inc(b.pos)
-      result.typeInstCache.safeAdd loadType(r, decodeVInt(b.s, b.pos), result.info)
+      result.typeInstCache.safeAdd loadType(g, decodeVInt(b.s, b.pos), result.info)
   of routineKinds:
-    decodeInstantiations(r, b, result.info, result.procInstCache)
+    decodeInstantiations(g, b, result.info, result.procInstCache)
     if b.s[b.pos] == '\16':
       inc(b.pos)
-      result.gcUnsafetyReason = loadSym(r, decodeVInt(b.s, b.pos), result.info)
+      result.gcUnsafetyReason = loadSym(g, decodeVInt(b.s, b.pos), result.info)
   of skModule, skPackage:
-    decodeInstantiations(r, b, result.info, result.usedGenerics)
+    decodeInstantiations(g, b, result.info, result.usedGenerics)
   of skLet, skVar, skField, skForVar:
     if b.s[b.pos] == '\18':
       inc(b.pos)
-      result.guard = loadSym(r, decodeVInt(b.s, b.pos), result.info)
+      result.guard = loadSym(g, decodeVInt(b.s, b.pos), result.info)
     if b.s[b.pos] == '\19':
       inc(b.pos)
       result.bitsize = decodeVInt(b.s, b.pos).int16
@@ -786,159 +737,146 @@ proc loadSymFromBlob(r; b; info: TLineInfo): PSym =
     #if result.kind in routineKinds:
     #  result.ast = decodeNodeLazyBody(b, result.info, result)
     #else:
-    result.ast = decodeNode(r, b, result.info)
+    result.ast = decodeNode(g, b, result.info)
   if sfCompilerProc in result.flags:
-    registerCompilerProc(result)
+    registerCompilerProc(g, result)
     #echo "loading ", result.name.s
 
-proc loadSym(r; id: int; info: TLineInfo): PSym =
-  result = r.syms.getOrDefault(id)
+proc loadSym(g; id: int; info: TLineInfo): PSym =
+  result = g.incr.r.syms.getOrDefault(id)
   if result != nil: return result
-  var b = loadBlob(sql"select data from syms where nimid = ?", id)
-  result = loadSymFromBlob(r, b, info)
+  var b = loadBlob(g, sql"select data from syms where nimid = ?", id)
+  result = loadSymFromBlob(g, b, info)
   doAssert id == result.id, "symbol ID is not consistent!"
 
-proc loadModuleSymTab(r; module: PSym) =
+proc loadModuleSymTab(g; module: PSym) =
   ## goal: fill  module.tab
-  gr.syms[module.id] = module
+  g.incr.r.syms.add(module.id, module)
   for row in db.fastRows(sql"select nimid, data from syms where module = ? and exported = 1", abs(module.id)):
     let id = parseInt(row[0])
-    var s = r.syms.getOrDefault(id)
+    var s = g.incr.r.syms.getOrDefault(id)
     if s == nil:
       var b = BlobReader(pos: 0)
       shallowCopy(b.s, row[1])
-      s = loadSymFromBlob(r, b, module.info)
+      # ensure we can read without index checks:
+      b.s.add '\0'
+      s = loadSymFromBlob(g, b, module.info)
     assert s != nil
     strTableAdd(module.tab, s)
   if sfSystemModule in module.flags:
-    magicsys.systemModule = module
-
-proc loadNode*(module: PSym; index: int): PNode =
-  assert gSymbolFiles == v2Sf
-  if index == 0:
-    loadModuleSymTab(gr, module)
-    #index = parseInt db.getValue(
-    #  sql"select min(id) from toplevelstmts where module = ?", abs module.id)
-  var b = BlobReader(pos: 0)
-  b.s = db.getValue(sql"select data from toplevelstmts where position = ? and module = ?",
-                    index, abs module.id)
-  if b.s.len == 0:
-    db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId)
-    return nil # end marker
-  gr.module = module
-  result = decodeNode(gr, b, module.info)
-
-proc addModuleDep*(module, fileIdx: int32; isIncludeFile: bool) =
-  if gSymbolFiles != v2Sf: return
-
-  let a = toDbFileId(module)
-  let b = toDbFileId(fileIdx)
-
-  db.exec(sql"insert into deps(module, dependency, isIncludeFile) values (?, ?, ?)",
-    a, b, ord(isIncludeFile))
-
-# --------------- Database model ---------------------------------------------
-
-proc createDb() =
-  db.exec(sql"""
-    create table if not exists controlblock(
-      idgen integer not null
-    );
-  """)
-
-  db.exec(sql"""
-    create table if not exists filenames(
-      id integer primary key,
-      fullpath varchar(8000) not null,
-      fullHash varchar(256) not null
-    );
-  """)
-  db.exec sql"create index if not exists FilenameIx on filenames(fullpath);"
-
-  db.exec(sql"""
-    create table if not exists modules(
-      id integer primary key,
-      fullpath varchar(8000) not null,
-      interfHash varchar(256) not null,
-      fullHash varchar(256) not null,
-
-      created timestamp not null default (DATETIME('now'))
-    );""")
-  db.exec(sql"""create unique index if not exists SymNameIx on modules(fullpath);""")
-
-  db.exec(sql"""
-    create table if not exists deps(
-      id integer primary key,
-      module integer not null,
-      dependency integer not null,
-      isIncludeFile integer not null,
-      foreign key (module) references filenames(id),
-      foreign key (dependency) references filenames(id)
-    );""")
-  db.exec(sql"""create index if not exists DepsIx on deps(module);""")
-
-  db.exec(sql"""
-    create table if not exists types(
-      id integer primary key,
-      nimid integer not null,
-      module integer not null,
-      data blob not null,
-      foreign key (module) references module(id)
-    );
-  """)
-  db.exec sql"create index TypeByModuleIdx on types(module);"
-  db.exec sql"create index TypeByNimIdIdx on types(nimid);"
-
-  db.exec(sql"""
-    create table if not exists syms(
-      id integer primary key,
-      nimid integer not null,
-      module integer not null,
-      name varchar(256) not null,
-      data blob not null,
-      exported int not null,
-      foreign key (module) references module(id)
-    );
-  """)
-  db.exec sql"create index if not exists SymNameIx on syms(name);"
-  db.exec sql"create index SymByNameAndModuleIdx on syms(name, module);"
-  db.exec sql"create index SymByModuleIdx on syms(module);"
-  db.exec sql"create index SymByNimIdIdx on syms(nimid);"
-
-
-  db.exec(sql"""
-    create table if not exists toplevelstmts(
-      id integer primary key,
-      position integer not null,
-      module integer not null,
-      data blob not null,
-      foreign key (module) references module(id)
-    );
-  """)
-  db.exec sql"create index TopLevelStmtByModuleIdx on toplevelstmts(module);"
-  db.exec sql"create index TopLevelStmtByPositionIdx on toplevelstmts(position);"
-
-  db.exec(sql"""
-    create table if not exists statics(
-      id integer primary key,
-      module integer not null,
-      data blob not null,
-      foreign key (module) references module(id)
-    );
-  """)
-  db.exec sql"create index StaticsByModuleIdx on toplevelstmts(module);"
-  db.exec sql"insert into controlblock(idgen) values (0)"
-
-proc setupModuleCache* =
-  if gSymbolFiles != v2Sf: return
-  let dbfile = getNimcacheDir() / "rodfiles.db"
+    g.systemModule = module
+
+proc replay(g: ModuleGraph; module: PSym; n: PNode) =
+  # XXX check if we need to replay nkStaticStmt here.
+  case n.kind
+  #of nkStaticStmt:
+    #evalStaticStmt(module, g, n[0], module)
+    #of nkVarSection, nkLetSection:
+    #  nkVarSections are already covered by the vmgen which produces nkStaticStmt
+  of nkMethodDef:
+    methodDef(g, n[namePos].sym, fromCache=true)
+  of nkCommentStmt:
+    # pragmas are complex and can be user-overriden via templates. So
+    # instead of using the original ``nkPragma`` nodes, we rely on the
+    # fact that pragmas.nim was patched to produce specialized recorded
+    # statements for us in the form of ``nkCommentStmt`` with (key, value)
+    # pairs. Ordinary nkCommentStmt nodes never have children so this is
+    # not ambiguous.
+    # Fortunately only a tiny subset of the available pragmas need to
+    # be replayed here. This is always a subset of ``pragmas.stmtPragmas``.
+    if n.len >= 2:
+      internalAssert g.config, n[0].kind == nkStrLit and n[1].kind == nkStrLit
+      case n[0].strVal
+      of "hint": message(g.config, n.info, hintUser, n[1].strVal)
+      of "warning": message(g.config, n.info, warnUser, n[1].strVal)
+      of "error": localError(g.config, n.info, errUser, n[1].strVal)
+      of "compile":
+        internalAssert g.config, n.len == 3 and n[2].kind == nkStrLit
+        var cf = Cfile(cname: n[1].strVal, obj: n[2].strVal,
+                       flags: {CfileFlag.External})
+        extccomp.addExternalFileToCompile(g.config, cf)
+      of "link":
+        extccomp.addExternalFileToLink(g.config, n[1].strVal)
+      of "passl":
+        extccomp.addLinkOption(g.config, n[1].strVal)
+      of "passc":
+        extccomp.addCompileOption(g.config, n[1].strVal)
+      of "cppdefine":
+        options.cppDefine(g.config, n[1].strVal)
+      of "inc":
+        let destKey = n[1].strVal
+        let by = n[2].intVal
+        let v = getOrDefault(g.cacheCounters, destKey)
+        g.cacheCounters[destKey] = v+by
+      of "put":
+        let destKey = n[1].strVal
+        let key = n[2].strVal
+        let val = n[3]
+        if not contains(g.cacheTables, destKey):
+          g.cacheTables[destKey] = initBTree[string, PNode]()
+        if not contains(g.cacheTables[destKey], key):
+          g.cacheTables[destKey].add(key, val)
+        else:
+          internalError(g.config, n.info, "key already exists: " & key)
+      of "incl":
+        let destKey = n[1].strVal
+        let val = n[2]
+        if not contains(g.cacheSeqs, destKey):
+          g.cacheSeqs[destKey] = newTree(nkStmtList, val)
+        else:
+          block search:
+            for existing in g.cacheSeqs[destKey]:
+              if exprStructuralEquivalent(existing, val, strictSymEquality=true):
+                break search
+            g.cacheSeqs[destKey].add val
+      of "add":
+        let destKey = n[1].strVal
+        let val = n[2]
+        if not contains(g.cacheSeqs, destKey):
+          g.cacheSeqs[destKey] = newTree(nkStmtList, val)
+        else:
+          g.cacheSeqs[destKey].add val
+      else:
+        internalAssert g.config, false
+  of nkImportStmt:
+    for x in n:
+      internalAssert g.config, x.kind == nkStrLit
+      let imported = g.importModuleCallback(g, module, fileInfoIdx(g.config, n[0].strVal))
+      internalAssert g.config, imported.id < 0
+  of nkStmtList, nkStmtListExpr:
+    for x in n: replay(g, module, x)
+  else: discard "nothing to do for this node"
+
+proc loadNode*(g: ModuleGraph; module: PSym): PNode =
+  loadModuleSymTab(g, module)
+  result = newNodeI(nkStmtList, module.info)
+  for row in db.rows(sql"select data from toplevelstmts where module = ? order by position asc",
+                        abs module.id):
+
+    var b = BlobReader(pos: 0)
+    # ensure we can read without index checks:
+    b.s = row[0] & '\0'
+    result.add decodeNode(g, b, module.info)
+
+  db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId)
+  replay(g, module, result)
+
+proc setupModuleCache*(g: ModuleGraph) =
+  if g.config.symbolFiles == disabledSf: return
+  g.recordStmt = recordStmt
+  let dbfile = getNimcacheDir(g.config) / "rodfiles.db"
+  if g.config.symbolFiles == writeOnlySf:
+    removeFile(dbfile)
   if not fileExists(dbfile):
     db = open(connection=dbfile, user="nim", password="",
               database="nim")
-    createDb()
+    createDb(db)
+    db.exec(sql"insert into config(config) values (?)", encodeConfig(g))
   else:
     db = open(connection=dbfile, user="nim", password="",
               database="nim")
+    let oldConfig = db.getValue(sql"select config from config")
+    g.incr.configChanged = oldConfig != encodeConfig(g)
   db.exec(sql"pragma journal_mode=off")
   db.exec(sql"pragma SYNCHRONOUS=off")
   db.exec(sql"pragma LOCKING_MODE=exclusive")
diff --git a/compiler/rodread.nim b/compiler/rodread.nim
deleted file mode 100644
index 52e7a924c..000000000
--- a/compiler/rodread.nim
+++ /dev/null
@@ -1,1244 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2013 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-# This module is responsible for loading of rod files.
-#
-# Reading and writing binary files are really hard to debug. Therefore we use
-# a "creative" text/binary hybrid format. ROD-files are more efficient
-# to process because symbols can be loaded on demand.
-#
-# A ROD file consists of:
-#
-#  - a header:
-#    NIM:$fileversion\n
-#  - the module's id (even if the module changed, its ID will not!):
-#    ID:Ax3\n
-#  - HASH value of this module:
-#    HASH:HASH-val\n
-#  - a section containing the compiler options and defines this
-#    module has been compiled with:
-#    OPTIONS:options\n
-#    GOPTIONS:options\n # global options
-#    CMD:command\n
-#    DEFINES:defines\n
-#  - FILES(
-#    myfile.inc
-#    lib/mymodA
-#    )
-#  - an include file dependency section:
-#    INCLUDES(
-#    <fileidx> <Hash of myfile.inc>\n # fileidx is the LINE in the file section!
-#    )
-#  - a module dependency section:
-#    DEPS: <fileidx> <fileidx>\n
-#  - an interface section:
-#    INTERF(
-#    identifier1 id\n # id is the symbol's id
-#    identifier2 id\n
-#    )
-#  - a compiler proc section:
-#    COMPILERPROCS(
-#    identifier1 id\n # id is the symbol's id
-#    )
-#  - an index consisting of (ID, linenumber)-pairs:
-#    INDEX(
-#    id-diff idx-diff\n
-#    id-diff idx-diff\n
-#    )
-#
-#    Since the whole index has to be read in advance, we compress it by
-#    storing the integer differences to the last entry instead of using the
-#    real numbers.
-#
-#  - an import index consisting of (ID, moduleID)-pairs:
-#    IMPORTS(
-#    id-diff moduleID-diff\n
-#    id-diff moduleID-diff\n
-#    )
-#  - a list of all exported type converters because they are needed for correct
-#    semantic checking:
-#    CONVERTERS:id id\n   # symbol ID
-#
-#    This is a misnomer now; it's really a "load unconditionally" section as
-#    it is also used for pattern templates.
-#
-#  - a list of all (private or exported) methods because they are needed for
-#    correct dispatcher generation:
-#    METHODS: id id\n   # symbol ID
-#  - an AST section that contains the module's AST:
-#    INIT(
-#    idx\n  # position of the node in the DATA section
-#    idx\n
-#    )
-#  - a data section, where each type, symbol or AST is stored.
-#    DATA(
-#    type
-#    (node)
-#    sym
-#    )
-#
-#    The data section MUST be the last section of the file, because processing
-#    stops immediately after ``DATA(`` and the rest is only loaded on demand
-#    by using a mem'mapped file.
-#
-
-import
-  os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms,
-  ropes, idents, std / sha1, idgen, types, rodutils, memfiles, tables,
-  configuration
-
-type
-  TReasonForRecompile* = enum ## all the reasons that can trigger recompilation
-    rrEmpty,                  # dependencies not yet computed
-    rrNone,                   # no need to recompile
-    rrRodDoesNotExist,        # rod file does not exist
-    rrRodInvalid,             # rod file is invalid
-    rrHashChange,             # file has been edited since last recompilation
-    rrDefines,                # defines have changed
-    rrOptions,                # options have changed
-    rrInclDeps,               # an include has changed
-    rrModDeps                 # a module this module depends on has been changed
-
-const
-  reasonToFrmt*: array[TReasonForRecompile, string] = ["",
-    "no need to recompile: $1", "symbol file for $1 does not exist",
-    "symbol file for $1 has the wrong version",
-    "file edited since last compilation: $1",
-    "list of conditional symbols changed for: $1",
-    "list of options changed for: $1",
-    "an include file edited: $1",
-    "a module $1 depends on has changed"]
-
-type
-  TIndex*{.final.} = object   # an index with compression
-    lastIdxKey*, lastIdxVal*: int
-    tab*: TIITable
-    r*: string                # writers use this
-    offset*: int              # readers use this
-
-  TRodReader* = object of RootObj
-    pos: int                 # position; used for parsing
-    s: cstring               # mmap'ed file contents
-    options: TOptions
-    reason: TReasonForRecompile
-    modDeps: seq[FileIndex]
-    files: seq[FileIndex]
-    dataIdx: int             # offset of start of data section
-    convertersIdx: int       # offset of start of converters section
-    initIdx, interfIdx, compilerProcsIdx, methodsIdx: int
-    filename: string
-    index, imports: TIndex
-    readerIndex: int
-    line: int            # only used for debugging, but is always in the code
-    moduleID: int
-    syms: Table[int, PSym]       # already processed symbols
-    memfile: MemFile     # unfortunately there is no point in time where we
-                         # can close this! XXX
-    methods*: TSymSeq
-    origFile: string
-    inViewMode: bool
-    cache*: IdentCache
-    config: ConfigRef
-
-  PRodReader* = ref TRodReader
-
-var rodCompilerprocs*: TStrTable # global because this is needed by magicsys
-
-proc rawLoadStub(s: PSym)
-
-var gTypeTable: TIdTable
-
-proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym
-  # `info` is only used for debugging purposes
-proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType
-
-proc decodeLineInfo(r: PRodReader, info: var TLineInfo) =
-  if r.s[r.pos] == '?':
-    inc(r.pos)
-    if r.s[r.pos] == ',': info.col = -1'i16
-    else: info.col = int16(decodeVInt(r.s, r.pos))
-    if r.s[r.pos] == ',':
-      inc(r.pos)
-      if r.s[r.pos] == ',': info.line = 0'u16
-      else: info.line = uint16(decodeVInt(r.s, r.pos))
-      if r.s[r.pos] == ',':
-        inc(r.pos)
-        info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], int info.line, info.col)
-
-proc skipNode(r: PRodReader) =
-  assert r.s[r.pos] == '('
-  var par = 0
-  var pos = r.pos+1
-  while true:
-    case r.s[pos]
-    of ')':
-      if par == 0: break
-      dec par
-    of '(': inc par
-    else: discard
-    inc pos
-  r.pos = pos+1 # skip ')'
-
-proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo,
-                        belongsTo: PSym): PNode =
-  result = nil
-  if r.s[r.pos] == '(':
-    inc(r.pos)
-    if r.s[r.pos] == ')':
-      inc(r.pos)
-      return                  # nil node
-    result = newNodeI(TNodeKind(decodeVInt(r.s, r.pos)), fInfo)
-    decodeLineInfo(r, result.info)
-    if r.s[r.pos] == '$':
-      inc(r.pos)
-      result.flags = cast[TNodeFlags](int32(decodeVInt(r.s, r.pos)))
-    if r.s[r.pos] == '^':
-      inc(r.pos)
-      var id = decodeVInt(r.s, r.pos)
-      result.typ = rrGetType(r, id, result.info)
-    case result.kind
-    of nkCharLit..nkUInt64Lit:
-      if r.s[r.pos] == '!':
-        inc(r.pos)
-        result.intVal = decodeVBiggestInt(r.s, r.pos)
-    of nkFloatLit..nkFloat64Lit:
-      if r.s[r.pos] == '!':
-        inc(r.pos)
-        var fl = decodeStr(r.s, r.pos)
-        result.floatVal = parseFloat(fl)
-    of nkStrLit..nkTripleStrLit:
-      if r.s[r.pos] == '!':
-        inc(r.pos)
-        result.strVal = decodeStr(r.s, r.pos)
-      else:
-        result.strVal = ""    # BUGFIX
-    of nkIdent:
-      if r.s[r.pos] == '!':
-        inc(r.pos)
-        var fl = decodeStr(r.s, r.pos)
-        result.ident = r.cache.getIdent(fl)
-      else:
-        internalError(r.config, result.info, "decodeNode: nkIdent")
-    of nkSym:
-      if r.s[r.pos] == '!':
-        inc(r.pos)
-        var id = decodeVInt(r.s, r.pos)
-        result.sym = rrGetSym(r, id, result.info)
-      else:
-        internalError(r.config, result.info, "decodeNode: nkSym")
-    else:
-      var i = 0
-      while r.s[r.pos] != ')':
-        if belongsTo != nil and i == bodyPos:
-          addSonNilAllowed(result, nil)
-          belongsTo.offset = r.pos
-          skipNode(r)
-        else:
-          addSonNilAllowed(result, decodeNodeLazyBody(r, result.info, nil))
-        inc i
-    if r.s[r.pos] == ')': inc(r.pos)
-    else: internalError(r.config, result.info, "decodeNode: ')' missing")
-  else:
-    internalError(r.config, fInfo, "decodeNode: '(' missing " & $r.pos)
-
-proc decodeNode(r: PRodReader, fInfo: TLineInfo): PNode =
-  result = decodeNodeLazyBody(r, fInfo, nil)
-
-proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
-  if r.s[r.pos] == '<':
-    inc(r.pos)
-    if r.s[r.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}:
-      loc.k = TLocKind(decodeVInt(r.s, r.pos))
-    else:
-      loc.k = low(loc.k)
-    if r.s[r.pos] == '*':
-      inc(r.pos)
-      loc.storage = TStorageLoc(decodeVInt(r.s, r.pos))
-    else:
-      loc.storage = low(loc.storage)
-    if r.s[r.pos] == '$':
-      inc(r.pos)
-      loc.flags = cast[TLocFlags](int32(decodeVInt(r.s, r.pos)))
-    else:
-      loc.flags = {}
-    if r.s[r.pos] == '^':
-      inc(r.pos)
-      loc.lode = decodeNode(r, info)
-      # rrGetType(r, decodeVInt(r.s, r.pos), info)
-    else:
-      loc.lode = nil
-    if r.s[r.pos] == '!':
-      inc(r.pos)
-      loc.r = rope(decodeStr(r.s, r.pos))
-    else:
-      loc.r = nil
-    if r.s[r.pos] == '>': inc(r.pos)
-    else: internalError(r.config, info, "decodeLoc " & r.s[r.pos])
-
-proc decodeType(r: PRodReader, info: TLineInfo): PType =
-  result = nil
-  if r.s[r.pos] == '[':
-    inc(r.pos)
-    if r.s[r.pos] == ']':
-      inc(r.pos)
-      return                  # nil type
-  new(result)
-  result.kind = TTypeKind(decodeVInt(r.s, r.pos))
-  if r.s[r.pos] == '+':
-    inc(r.pos)
-    result.id = decodeVInt(r.s, r.pos)
-    setId(result.id)
-    if debugIds: registerID(result)
-  else:
-    internalError(r.config, info, "decodeType: no id")
-  # here this also avoids endless recursion for recursive type
-  idTablePut(gTypeTable, result, result)
-  if r.s[r.pos] == '(': result.n = decodeNode(r, unknownLineInfo())
-  if r.s[r.pos] == '$':
-    inc(r.pos)
-    result.flags = cast[TTypeFlags](int32(decodeVInt(r.s, r.pos)))
-  if r.s[r.pos] == '?':
-    inc(r.pos)
-    result.callConv = TCallingConvention(decodeVInt(r.s, r.pos))
-  if r.s[r.pos] == '*':
-    inc(r.pos)
-    result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-  if r.s[r.pos] == '&':
-    inc(r.pos)
-    result.sym = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-  if r.s[r.pos] == '/':
-    inc(r.pos)
-    result.size = decodeVInt(r.s, r.pos)
-  else:
-    result.size = - 1
-  if r.s[r.pos] == '=':
-    inc(r.pos)
-    result.align = decodeVInt(r.s, r.pos).int16
-  else:
-    result.align = 2
-
-  if r.s[r.pos] == '\14':
-    inc(r.pos)
-    result.lockLevel = decodeVInt(r.s, r.pos).TLockLevel
-  else:
-    result.lockLevel = UnspecifiedLockLevel
-
-  if r.s[r.pos] == '\15':
-    inc(r.pos)
-    result.destructor = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-  if r.s[r.pos] == '\16':
-    inc(r.pos)
-    result.deepCopy = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-  if r.s[r.pos] == '\17':
-    inc(r.pos)
-    result.assignment = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-  if r.s[r.pos] == '\18':
-    inc(r.pos)
-    result.sink = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-  while r.s[r.pos] == '\19':
-    inc(r.pos)
-    let x = decodeVInt(r.s, r.pos)
-    doAssert r.s[r.pos] == '\20'
-    inc(r.pos)
-    let y = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-    result.methods.add((x, y))
-  decodeLoc(r, result.loc, info)
-  while r.s[r.pos] == '^':
-    inc(r.pos)
-    if r.s[r.pos] == '(':
-      inc(r.pos)
-      if r.s[r.pos] == ')': inc(r.pos)
-      else: internalError(r.config, info, "decodeType ^(" & r.s[r.pos])
-      rawAddSon(result, nil)
-    else:
-      var d = decodeVInt(r.s, r.pos)
-      rawAddSon(result, rrGetType(r, d, info))
-
-proc decodeLib(r: PRodReader, info: TLineInfo): PLib =
-  result = nil
-  if r.s[r.pos] == '|':
-    new(result)
-    inc(r.pos)
-    result.kind = TLibKind(decodeVInt(r.s, r.pos))
-    if r.s[r.pos] != '|': internalError(r.config, "decodeLib: 1")
-    inc(r.pos)
-    result.name = rope(decodeStr(r.s, r.pos))
-    if r.s[r.pos] != '|': internalError(r.config, "decodeLib: 2")
-    inc(r.pos)
-    result.path = decodeNode(r, info)
-
-proc decodeInstantiations(r: PRodReader; info: TLineInfo;
-                          s: var seq[PInstantiation]) =
-  while r.s[r.pos] == '\15':
-    inc(r.pos)
-    var ii: PInstantiation
-    new ii
-    ii.sym = rrGetSym(r, decodeVInt(r.s, r.pos), info)
-    ii.concreteTypes = @[]
-    while r.s[r.pos] == '\17':
-      inc(r.pos)
-      ii.concreteTypes.add rrGetType(r, decodeVInt(r.s, r.pos), info)
-    if r.s[r.pos] == '\20':
-      inc(r.pos)
-      ii.compilesId = decodeVInt(r.s, r.pos)
-    s.add ii
-
-proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
-  var
-    id: int
-    ident: PIdent
-  result = nil
-  if r.s[r.pos] == '{':
-    inc(r.pos)
-    if r.s[r.pos] == '}':
-      inc(r.pos)
-      return                  # nil sym
-  var k = TSymKind(decodeVInt(r.s, r.pos))
-  if r.s[r.pos] == '+':
-    inc(r.pos)
-    id = decodeVInt(r.s, r.pos)
-    setId(id)
-  else:
-    internalError(r.config, info, "decodeSym: no id")
-  if r.s[r.pos] == '&':
-    inc(r.pos)
-    ident = r.cache.getIdent(decodeStr(r.s, r.pos))
-  else:
-    internalError(r.config, info, "decodeSym: no ident")
-  #echo "decoding: {", ident.s
-  result = r.syms.getOrDefault(id)
-  if result == nil:
-    new(result)
-    result.id = id
-    r.syms[result.id] = result
-    if debugIds: registerID(result)
-  elif result.id != id:
-    internalError(r.config, info, "decodeSym: wrong id")
-  elif result.kind != skStub and not r.inViewMode:
-    # we already loaded the symbol
-    return
-  else:
-    reset(result[])
-    result.id = id
-  result.kind = k
-  result.name = ident         # read the rest of the symbol description:
-  if r.s[r.pos] == '^':
-    inc(r.pos)
-    result.typ = rrGetType(r, decodeVInt(r.s, r.pos), info)
-  decodeLineInfo(r, result.info)
-  if r.s[r.pos] == '*':
-    inc(r.pos)
-    result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), result.info)
-  if r.s[r.pos] == '$':
-    inc(r.pos)
-    result.flags = cast[TSymFlags](int32(decodeVInt(r.s, r.pos)))
-  if r.s[r.pos] == '@':
-    inc(r.pos)
-    result.magic = TMagic(decodeVInt(r.s, r.pos))
-  if r.s[r.pos] == '!':
-    inc(r.pos)
-    result.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
-  else:
-    result.options = r.options
-  if r.s[r.pos] == '%':
-    inc(r.pos)
-    result.position = decodeVInt(r.s, r.pos)
-  elif result.kind notin routineKinds + {skModule}:
-    result.position = 0
-    # this may have been misused as reader index! But we still
-    # need it for routines as the body is loaded lazily.
-  if r.s[r.pos] == '`':
-    inc(r.pos)
-    result.offset = decodeVInt(r.s, r.pos)
-  else:
-    result.offset = - 1
-  decodeLoc(r, result.loc, result.info)
-  result.annex = decodeLib(r, info)
-  if r.s[r.pos] == '#':
-    inc(r.pos)
-    result.constraint = decodeNode(r, unknownLineInfo())
-  case result.kind
-  of skType, skGenericParam:
-    while r.s[r.pos] == '\14':
-      inc(r.pos)
-      result.typeInstCache.add rrGetType(r, decodeVInt(r.s, r.pos), result.info)
-  of routineKinds:
-    decodeInstantiations(r, result.info, result.procInstCache)
-    if r.s[r.pos] == '\16':
-      inc(r.pos)
-      result.gcUnsafetyReason = rrGetSym(r, decodeVInt(r.s, r.pos), result.info)
-  of skModule, skPackage:
-    decodeInstantiations(r, result.info, result.usedGenerics)
-  of skLet, skVar, skField, skForVar:
-    if r.s[r.pos] == '\18':
-      inc(r.pos)
-      result.guard = rrGetSym(r, decodeVInt(r.s, r.pos), result.info)
-    if r.s[r.pos] == '\19':
-      inc(r.pos)
-      result.bitsize = decodeVInt(r.s, r.pos).int16
-  else: discard
-
-  if r.s[r.pos] == '(':
-    if result.kind in routineKinds:
-      result.ast = decodeNodeLazyBody(r, result.info, result)
-      # since we load the body lazily, we need to set the reader to
-      # be able to reload:
-      result.position = r.readerIndex
-    else:
-      result.ast = decodeNode(r, result.info)
-  #echo "decoded: ", ident.s, "}"
-
-proc skipSection(r: PRodReader) =
-  if r.s[r.pos] == ':':
-    while r.s[r.pos] > '\x0A': inc(r.pos)
-  elif r.s[r.pos] == '(':
-    var c = 0                 # count () pairs
-    inc(r.pos)
-    while true:
-      case r.s[r.pos]
-      of '\x0A': inc(r.line)
-      of '(': inc(c)
-      of ')':
-        if c == 0:
-          inc(r.pos)
-          break
-        elif c > 0:
-          dec(c)
-      of '\0': break          # end of file
-      else: discard
-      inc(r.pos)
-  else:
-    internalError(r.config, "skipSection " & $r.line)
-
-proc rdWord(r: PRodReader): string =
-  result = ""
-  while r.s[r.pos] in {'A'..'Z', '_', 'a'..'z', '0'..'9'}:
-    add(result, r.s[r.pos])
-    inc(r.pos)
-
-proc newStub(r: PRodReader, name: string, id: int): PSym =
-  new(result)
-  result.kind = skStub
-  result.id = id
-  result.name = r.cache.getIdent(name)
-  result.position = r.readerIndex
-  setId(id)                   #MessageOut(result.name.s);
-  if debugIds: registerID(result)
-
-proc processInterf(r: PRodReader, module: PSym) =
-  if r.interfIdx == 0: internalError(r.config, "processInterf")
-  r.pos = r.interfIdx
-  while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
-    var w = decodeStr(r.s, r.pos)
-    inc(r.pos)
-    var key = decodeVInt(r.s, r.pos)
-    inc(r.pos)                # #10
-    var s = newStub(r, w, key)
-    s.owner = module
-    strTableAdd(module.tab, s)
-    r.syms[s.id] = s
-
-proc processCompilerProcs(r: PRodReader, module: PSym) =
-  if r.compilerProcsIdx == 0: internalError(r.config, "processCompilerProcs")
-  r.pos = r.compilerProcsIdx
-  while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
-    var w = decodeStr(r.s, r.pos)
-    inc(r.pos)
-    var key = decodeVInt(r.s, r.pos)
-    inc(r.pos)                # #10
-    var s = r.syms.getOrDefault(key)
-    if s == nil:
-      s = newStub(r, w, key)
-      s.owner = module
-      r.syms[s.id] = s
-    strTableAdd(rodCompilerprocs, s)
-
-proc processIndex(r: PRodReader; idx: var TIndex; outf: File = nil) =
-  var key, val, tmp: int
-  inc(r.pos, 2)               # skip "(\10"
-  inc(r.line)
-  while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
-    tmp = decodeVInt(r.s, r.pos)
-    if r.s[r.pos] == ' ':
-      inc(r.pos)
-      key = idx.lastIdxKey + tmp
-      val = decodeVInt(r.s, r.pos) + idx.lastIdxVal
-    else:
-      key = idx.lastIdxKey + 1
-      val = tmp + idx.lastIdxVal
-    iiTablePut(idx.tab, key, val)
-    if not outf.isNil: outf.write(key, " ", val, "\n")
-    idx.lastIdxKey = key
-    idx.lastIdxVal = val
-    setId(key)                # ensure that this id will not be used
-    if r.s[r.pos] == '\x0A':
-      inc(r.pos)
-      inc(r.line)
-  if r.s[r.pos] == ')': inc(r.pos)
-
-proc cmdChangeTriggersRecompilation(old, new: TCommands): bool =
-  if old == new: return false
-  # we use a 'case' statement without 'else' so that addition of a
-  # new command forces us to consider it here :-)
-  case old
-  of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
-      cmdCompileToJS, cmdCompileToLLVM:
-    if new in {cmdDoc, cmdCheck, cmdIdeTools, cmdPretty, cmdDef,
-               cmdInteractive}:
-      return false
-  of cmdNone, cmdDoc, cmdInterpret, cmdPretty, cmdGenDepend, cmdDump,
-      cmdCheck, cmdParse, cmdScan, cmdIdeTools, cmdDef,
-      cmdRst2html, cmdRst2tex, cmdInteractive, cmdRun, cmdJsonScript:
-    discard
-  # else: trigger recompilation:
-  result = true
-
-proc processRodFile(r: PRodReader, hash: SecureHash) =
-  var
-    w: string
-    d: int
-  var inclHash: SecureHash
-  while r.s[r.pos] != '\0':
-    var section = rdWord(r)
-    if r.reason != rrNone:
-      break                   # no need to process this file further
-    case section
-    of "HASH":
-      inc(r.pos)              # skip ':'
-      if hash != parseSecureHash(decodeStr(r.s, r.pos)):
-        r.reason = rrHashChange
-    of "ID":
-      inc(r.pos)              # skip ':'
-      r.moduleID = decodeVInt(r.s, r.pos)
-      setId(r.moduleID)
-    of "ORIGFILE":
-      inc(r.pos)
-      r.origFile = decodeStr(r.s, r.pos)
-    of "OPTIONS":
-      inc(r.pos)              # skip ':'
-      r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
-      if r.config.options != r.options: r.reason = rrOptions
-    of "GOPTIONS":
-      inc(r.pos)              # skip ':'
-      var dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos)))
-      if r.config.globalOptions-harmlessOptions != dep-harmlessOptions:
-        r.reason = rrOptions
-    of "CMD":
-      inc(r.pos)              # skip ':'
-      var dep = cast[TCommands](int32(decodeVInt(r.s, r.pos)))
-      if cmdChangeTriggersRecompilation(dep, r.config.cmd): r.reason = rrOptions
-    of "DEFINES":
-      inc(r.pos)              # skip ':'
-      d = 0
-      while r.s[r.pos] > '\x0A':
-        w = decodeStr(r.s, r.pos)
-        inc(d)
-        if not isDefined(r.config, w):
-          r.reason = rrDefines #MessageOut('not defined, but should: ' + w);
-        if r.s[r.pos] == ' ': inc(r.pos)
-      if d != countDefinedSymbols(r.config.symbols): r.reason = rrDefines
-    of "FILES":
-      inc(r.pos, 2)           # skip "(\10"
-      inc(r.line)
-      while r.s[r.pos] != ')':
-        let finalPath = decodeStr(r.s, r.pos)
-        #let resolvedPath = relativePath.findModule(r.origFile)
-        #let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath
-        r.files.add(fileInfoIdx(r.config, finalPath))
-        inc(r.pos)            # skip #10
-        inc(r.line)
-      if r.s[r.pos] == ')': inc(r.pos)
-    of "INCLUDES":
-      inc(r.pos, 2)           # skip "(\10"
-      inc(r.line)
-      while r.s[r.pos] != ')':
-        w = r.files[decodeVInt(r.s, r.pos)].toFullPath
-        inc(r.pos)            # skip ' '
-        inclHash = parseSecureHash(decodeStr(r.s, r.pos))
-        if r.reason == rrNone:
-          if not existsFile(w) or (inclHash != secureHashFile(w)):
-            r.reason = rrInclDeps
-        if r.s[r.pos] == '\x0A':
-          inc(r.pos)
-          inc(r.line)
-      if r.s[r.pos] == ')': inc(r.pos)
-    of "DEPS":
-      inc(r.pos)              # skip ':'
-      while r.s[r.pos] > '\x0A':
-        r.modDeps.add(r.files[int32(decodeVInt(r.s, r.pos))])
-        if r.s[r.pos] == ' ': inc(r.pos)
-    of "INTERF":
-      r.interfIdx = r.pos + 2
-      skipSection(r)
-    of "COMPILERPROCS":
-      r.compilerProcsIdx = r.pos + 2
-      skipSection(r)
-    of "INDEX":
-      processIndex(r, r.index)
-    of "IMPORTS":
-      processIndex(r, r.imports)
-    of "CONVERTERS":
-      r.convertersIdx = r.pos + 1
-      skipSection(r)
-    of "METHODS":
-      r.methodsIdx = r.pos + 1
-      skipSection(r)
-    of "DATA":
-      r.dataIdx = r.pos + 2 # "(\10"
-      # We do not read the DATA section here! We read the needed objects on
-      # demand. And the DATA section comes last in the file, so we stop here:
-      break
-    of "INIT":
-      r.initIdx = r.pos + 2   # "(\10"
-      skipSection(r)
-    else:
-      internalError(r.config, "invalid section: '" & section &
-                    "' at " & $r.line & " in " & r.filename)
-      #MsgWriteln("skipping section: " & section &
-      #           " at " & $r.line & " in " & r.filename)
-      skipSection(r)
-    if r.s[r.pos] == '\x0A':
-      inc(r.pos)
-      inc(r.line)
-
-
-proc startsWith(buf: cstring, token: string, pos = 0): bool =
-  var s = 0
-  while s < token.len and buf[pos+s] == token[s]: inc s
-  result = s == token.len
-
-proc newRodReader(modfilename: string, hash: SecureHash,
-                  readerIndex: int; cache: IdentCache;
-                  config: ConfigRef): PRodReader =
-  new(result)
-  result.cache = cache
-  result.config = config
-  try:
-    result.memfile = memfiles.open(modfilename)
-  except OSError:
-    return nil
-  result.files = @[]
-  result.modDeps = @[]
-  result.methods = @[]
-  var r = result
-  r.reason = rrNone
-  r.pos = 0
-  r.line = 1
-  r.readerIndex = readerIndex
-  r.filename = modfilename
-  r.syms = initTable[int, PSym]()
-  # we terminate the file explicitly with ``\0``, so the cast to `cstring`
-  # is safe:
-  r.s = cast[cstring](r.memfile.mem)
-  if startsWith(r.s, "NIM:"):
-    initIiTable(r.index.tab)
-    initIiTable(r.imports.tab) # looks like a ROD file
-    inc(r.pos, 4)
-    var version = ""
-    while r.s[r.pos] notin {'\0', '\x0A'}:
-      add(version, r.s[r.pos])
-      inc(r.pos)
-    if r.s[r.pos] == '\x0A': inc(r.pos)
-    if version != RodFileVersion:
-      # since ROD files are only for caching, no backwards compatibility is
-      # needed
-      #echo "expected version ", version, " ", RodFileVersion
-      result.memfile.close
-      result = nil
-  else:
-    result.memfile.close
-    result = nil
-
-proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType =
-  result = PType(idTableGet(gTypeTable, id))
-  if result == nil:
-    # load the type:
-    var oldPos = r.pos
-    var d = iiTableGet(r.index.tab, id)
-    if d == InvalidKey: internalError(r.config, info, "rrGetType")
-    r.pos = d + r.dataIdx
-    result = decodeType(r, info)
-    r.pos = oldPos
-
-type
-  TFileModuleRec = object
-    filename*: string
-    reason*: TReasonForRecompile
-    rd*: PRodReader
-    hash*: SecureHash
-    hashDone*: bool
-
-  TFileModuleMap = seq[TFileModuleRec]
-
-var gMods*: TFileModuleMap = @[]
-
-proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym =
-  # all compiled modules
-  if rd.dataIdx == 0: internalError(rd.config, info, "dataIdx == 0")
-  var oldPos = rd.pos
-  rd.pos = offset + rd.dataIdx
-  result = decodeSym(rd, info)
-  rd.pos = oldPos
-
-proc findSomeWhere(id: int) =
-  for i in countup(0, high(gMods)):
-    var rd = gMods[i].rd
-    if rd != nil:
-      var d = iiTableGet(rd.index.tab, id)
-      if d != InvalidKey:
-        echo "found id ", id, " in ", gMods[i].filename
-
-proc getReader(moduleId: int): PRodReader =
-  # we can't index 'gMods' here as it's indexed by a *file index* which is not
-  # the module ID! We could introduce a mapping ID->PRodReader but I'll leave
-  # this for later versions if benchmarking shows the linear search causes
-  # problems:
-  for i in 0 ..< gMods.len:
-    result = gMods[i].rd
-    if result != nil and result.moduleID == moduleId: return result
-  return nil
-
-proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
-  result = r.syms.getOrDefault(id)
-  if result == nil:
-    # load the symbol:
-    var d = iiTableGet(r.index.tab, id)
-    if d == InvalidKey:
-      # import from other module:
-      var moduleID = iiTableGet(r.imports.tab, id)
-      if moduleID < 0:
-        var x = ""
-        encodeVInt(id, x)
-        internalError(r.config, info, "missing from both indexes: +" & x)
-      var rd = getReader(moduleID)
-      doAssert rd != nil
-      d = iiTableGet(rd.index.tab, id)
-      if d != InvalidKey:
-        result = decodeSymSafePos(rd, d, info)
-      else:
-        var x = ""
-        encodeVInt(id, x)
-        when false: findSomeWhere(id)
-        internalError(r.config, info, "rrGetSym: no reader found: +" & x)
-    else:
-      # own symbol:
-      result = decodeSymSafePos(r, d, info)
-  if result != nil and result.kind == skStub: rawLoadStub(result)
-
-proc loadInitSection*(r: PRodReader): PNode =
-  if r.initIdx == 0 or r.dataIdx == 0: internalError(r.config, "loadInitSection")
-  var oldPos = r.pos
-  r.pos = r.initIdx
-  result = newNode(nkStmtList)
-  while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')':
-    var d = decodeVInt(r.s, r.pos)
-    inc(r.pos)                # #10
-    var p = r.pos
-    r.pos = d + r.dataIdx
-    addSon(result, decodeNode(r, unknownLineInfo()))
-    r.pos = p
-  r.pos = oldPos
-
-proc loadConverters(r: PRodReader) =
-  # We have to ensure that no exported converter is a stub anymore, and the
-  # import mechanism takes care of the rest.
-  if r.convertersIdx == 0 or r.dataIdx == 0:
-    internalError(r.config, "importConverters")
-  r.pos = r.convertersIdx
-  while r.s[r.pos] > '\x0A':
-    var d = decodeVInt(r.s, r.pos)
-    discard rrGetSym(r, d, unknownLineInfo())
-    if r.s[r.pos] == ' ': inc(r.pos)
-
-proc loadMethods(r: PRodReader) =
-  if r.methodsIdx == 0 or r.dataIdx == 0:
-    internalError(r.config, "loadMethods")
-  r.pos = r.methodsIdx
-  while r.s[r.pos] > '\x0A':
-    var d = decodeVInt(r.s, r.pos)
-    r.methods.add(rrGetSym(r, d, unknownLineInfo()))
-    if r.s[r.pos] == ' ': inc(r.pos)
-
-proc getHash*(fileIdx: FileIndex): SecureHash =
-  if fileIdx.int32 <% gMods.len and gMods[fileIdx.int32].hashDone:
-    return gMods[fileIdx.int32].hash
-
-  result = secureHashFile(fileIdx.toFullPath)
-  if fileIdx.int32 >= gMods.len: setLen(gMods, fileIdx.int32+1)
-  gMods[fileIdx.int32].hash = result
-
-template growCache*(cache, pos) =
-  if cache.len <= pos: cache.setLen(pos+1)
-
-proc checkDep(fileIdx: FileIndex; cache: IdentCache; conf: ConfigRef): TReasonForRecompile =
-  assert fileIdx != InvalidFileIDX
-  growCache gMods, fileIdx.int32
-  if gMods[fileIdx.int32].reason != rrEmpty:
-    # reason has already been computed for this module:
-    return gMods[fileIdx.int32].reason
-  let filename = fileIdx.toFilename
-  var hash = getHash(fileIdx)
-  gMods[fileIdx.int32].reason = rrNone  # we need to set it here to avoid cycles
-  result = rrNone
-  var rodfile = toGeneratedFile(conf, conf.withPackageName(filename), RodExt)
-  var r = newRodReader(rodfile, hash, fileIdx.int32, cache, conf)
-  if r == nil:
-    result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist)
-  else:
-    processRodFile(r, hash)
-    result = r.reason
-    if result == rrNone:
-      # check modules it depends on
-      # NOTE: we need to process the entire module graph so that no ID will
-      # be used twice! However, compilation speed does not suffer much from
-      # this, since results are cached.
-      var res = checkDep(systemFileIdx, cache, conf)
-      if res != rrNone: result = rrModDeps
-      for i in countup(0, high(r.modDeps)):
-        res = checkDep(r.modDeps[i], cache, conf)
-        if res != rrNone:
-          result = rrModDeps
-          # we cannot break here, because of side-effects of `checkDep`
-  if result != rrNone:
-    rawMessage(conf, hintProcessing, reasonToFrmt[result] % filename)
-  if result != rrNone or optForceFullMake in conf.globalOptions:
-    # recompilation is necessary:
-    if r != nil: memfiles.close(r.memfile)
-    r = nil
-  gMods[fileIdx.int32].rd = r
-  gMods[fileIdx.int32].reason = result  # now we know better
-
-proc handleSymbolFile*(module: PSym; cache: IdentCache; conf: ConfigRef): PRodReader =
-  if conf.symbolFiles in {disabledSf, writeOnlySf, v2Sf}:
-    module.id = getID()
-    return nil
-  idgen.loadMaxIds(conf, conf.projectPath / conf.projectName)
-  let fileIdx = module.fileIdx
-  discard checkDep(fileIdx, cache, conf)
-  #if gMods[fileIdx.int32].reason == rrEmpty: internalError("handleSymbolFile")
-  result = gMods[fileIdx.int32].rd
-  if result != nil:
-    module.id = result.moduleID
-    result.syms[module.id] = module
-    processInterf(result, module)
-    processCompilerProcs(result, module)
-    loadConverters(result)
-    loadMethods(result)
-  else:
-    module.id = getID()
-
-proc rawLoadStub(s: PSym) =
-  assert s.kind == skStub
-  #if s.kind != skStub: internalError("loadStub")
-  var rd = gMods[s.position].rd
-  var theId = s.id                # used for later check
-  var d = iiTableGet(rd.index.tab, s.id)
-  #if d == InvalidKey: internalError("loadStub: invalid key")
-  var rs = decodeSymSafePos(rd, d, unknownLineInfo())
-  when false:
-    if rs != s:
-      #echo "rs: ", toHex(cast[int](rs.position), int.sizeof * 2),
-      #     "\ns:  ", toHex(cast[int](s.position), int.sizeof * 2)
-      internalError(rs.info, "loadStub: wrong symbol")
-    elif rs.id != theId:
-      internalError(rs.info, "loadStub: wrong ID")
-
-proc loadStub*(s: PSym) =
-  ## loads the stub symbol `s`.
-
-  # deactivate the GC here because we do a deep recursion and generate no
-  # garbage when restoring parts of the object graph anyway.
-  # Since we die with internal errors if this fails, no try-finally is
-  # necessary.
-  GC_disable()
-  rawLoadStub(s)
-  GC_enable()
-
-proc getBody*(s: PSym): PNode =
-  ## retrieves the AST's body of `s`. If `s` has been loaded from a rod-file
-  ## it may perform an expensive reload operation. Otherwise it's a simple
-  ## accessor.
-  assert s.kind in routineKinds
-  # prevent crashes due to incorrect macro transformations (bug #2377)
-  if s.ast.isNil or bodyPos >= s.ast.len: return ast.emptyNode
-  result = s.ast.sons[bodyPos]
-  if result == nil:
-    assert s.offset != 0
-    var r = gMods[s.position].rd
-    var oldPos = r.pos
-    r.pos = s.offset
-    result = decodeNode(r, s.info)
-    r.pos = oldPos
-    s.ast.sons[bodyPos] = result
-    s.offset = 0
-
-initIdTable(gTypeTable)
-initStrTable(rodCompilerprocs)
-
-# viewer:
-proc writeNode(f: File; n: PNode) =
-  f.write("(")
-  if n != nil:
-    f.write($n.kind)
-    if n.typ != nil:
-      f.write('^')
-      f.write(n.typ.id)
-    case n.kind
-    of nkCharLit..nkUInt64Lit:
-      if n.intVal != 0:
-        f.write('!')
-        f.write(n.intVal)
-    of nkFloatLit..nkFloat64Lit:
-      if n.floatVal != 0.0:
-        f.write('!')
-        f.write($n.floatVal)
-    of nkStrLit..nkTripleStrLit:
-      if n.strVal != "":
-        f.write('!')
-        f.write(n.strVal.escape)
-    of nkIdent:
-      f.write('!')
-      f.write(n.ident.s)
-    of nkSym:
-      f.write('!')
-      f.write(n.sym.id)
-    else:
-      for i in countup(0, sonsLen(n) - 1):
-        writeNode(f, n.sons[i])
-  f.write(")")
-
-proc writeSym(f: File; s: PSym) =
-  if s == nil:
-    f.write("{}\n")
-    return
-  f.write("{")
-  f.write($s.kind)
-  f.write('+')
-  f.write(s.id)
-  f.write('&')
-  f.write(s.name.s)
-  if s.typ != nil:
-    f.write('^')
-    f.write(s.typ.id)
-  if s.owner != nil:
-    f.write('*')
-    f.write(s.owner.id)
-  if s.flags != {}:
-    f.write('$')
-    f.write($s.flags)
-  if s.magic != mNone:
-    f.write('@')
-    f.write($s.magic)
-  f.write('!')
-  f.write($s.options)
-  if s.position != 0:
-    f.write('%')
-    f.write($s.position)
-  if s.offset != -1:
-    f.write('`')
-    f.write($s.offset)
-  if s.constraint != nil:
-    f.write('#')
-    f.writeNode(s.constraint)
-  if s.ast != nil:
-    f.writeNode(s.ast)
-  f.write("}\n")
-
-proc writeType(f: File; t: PType) =
-  if t == nil:
-    f.write("[]\n")
-    return
-  f.write('[')
-  f.write($t.kind)
-  f.write('+')
-  f.write($t.id)
-  if t.n != nil:
-    f.writeNode(t.n)
-  if t.flags != {}:
-    f.write('$')
-    f.write($t.flags)
-  if t.callConv != low(t.callConv):
-    f.write('?')
-    f.write($t.callConv)
-  if t.owner != nil:
-    f.write('*')
-    f.write($t.owner.id)
-  if t.sym != nil:
-    f.write('&')
-    f.write(t.sym.id)
-  if t.size != -1:
-    f.write('/')
-    f.write($t.size)
-  if t.align != 2:
-    f.write('=')
-    f.write($t.align)
-  for i in countup(0, sonsLen(t) - 1):
-    if t.sons[i] == nil:
-      f.write("^()")
-    else:
-      f.write('^')
-      f.write($t.sons[i].id)
-  f.write("]\n")
-
-proc viewFile(rodfile: string) =
-  let conf = newConfigRef()
-  var r = newRodReader(rodfile, secureHash(""), 0, newIdentCache(), conf)
-  if r == nil:
-    rawMessage(conf, errGenerated, "cannot open file (or maybe wrong version):" &
-       rodfile)
-    return
-  r.inViewMode = true
-  var outf = system.open(rodfile.changeFileExt(".rod.txt"), fmWrite)
-  while r.s[r.pos] != '\0':
-    let section = rdWord(r)
-    case section
-    of "HASH":
-      inc(r.pos)              # skip ':'
-      outf.writeLine("HASH:", $decodeVInt(r.s, r.pos))
-    of "ID":
-      inc(r.pos)              # skip ':'
-      r.moduleID = decodeVInt(r.s, r.pos)
-      setId(r.moduleID)
-      outf.writeLine("ID:", $r.moduleID)
-    of "ORIGFILE":
-      inc(r.pos)
-      r.origFile = decodeStr(r.s, r.pos)
-      outf.writeLine("ORIGFILE:", r.origFile)
-    of "OPTIONS":
-      inc(r.pos)              # skip ':'
-      r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
-      outf.writeLine("OPTIONS:", $r.options)
-    of "GOPTIONS":
-      inc(r.pos)              # skip ':'
-      let dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos)))
-      outf.writeLine("GOPTIONS:", $dep)
-    of "CMD":
-      inc(r.pos)              # skip ':'
-      let dep = cast[TCommands](int32(decodeVInt(r.s, r.pos)))
-      outf.writeLine("CMD:", $dep)
-    of "DEFINES":
-      inc(r.pos)              # skip ':'
-      var d = 0
-      outf.write("DEFINES:")
-      while r.s[r.pos] > '\x0A':
-        let w = decodeStr(r.s, r.pos)
-        inc(d)
-        outf.write(" ", w)
-        if r.s[r.pos] == ' ': inc(r.pos)
-      outf.write("\n")
-    of "FILES":
-      inc(r.pos, 2)           # skip "(\10"
-      inc(r.line)
-      outf.write("FILES(\n")
-      while r.s[r.pos] != ')':
-        let relativePath = decodeStr(r.s, r.pos)
-        let resolvedPath = findModule(conf, relativePath, r.origFile)
-        let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath
-        r.files.add(fileInfoIdx(conf, finalPath))
-        inc(r.pos)            # skip #10
-        inc(r.line)
-        outf.writeLine finalPath
-      if r.s[r.pos] == ')': inc(r.pos)
-      outf.write(")\n")
-    of "INCLUDES":
-      inc(r.pos, 2)           # skip "(\10"
-      inc(r.line)
-      outf.write("INCLUDES(\n")
-      while r.s[r.pos] != ')':
-        let w = r.files[decodeVInt(r.s, r.pos)]
-        inc(r.pos)            # skip ' '
-        let inclHash = decodeVInt(r.s, r.pos)
-        if r.s[r.pos] == '\x0A':
-          inc(r.pos)
-          inc(r.line)
-        outf.write(w.int32, " ", inclHash, "\n")
-      if r.s[r.pos] == ')': inc(r.pos)
-      outf.write(")\n")
-    of "DEPS":
-      inc(r.pos)              # skip ':'
-      outf.write("DEPS:")
-      while r.s[r.pos] > '\x0A':
-        let v = int32(decodeVInt(r.s, r.pos))
-        r.modDeps.add(r.files[v])
-        if r.s[r.pos] == ' ': inc(r.pos)
-        outf.write(" ", r.files[v].int32)
-      outf.write("\n")
-    of "INTERF",  "COMPILERPROCS":
-      inc r.pos, 2
-      if section == "INTERF": r.interfIdx = r.pos
-      else: r.compilerProcsIdx = r.pos
-      outf.write(section, "(\n")
-      while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
-        let w = decodeStr(r.s, r.pos)
-        inc(r.pos)
-        let key = decodeVInt(r.s, r.pos)
-        inc(r.pos)                # #10
-        outf.write(w, " ", key, "\n")
-      if r.s[r.pos] == ')': inc r.pos
-      outf.write(")\n")
-    of "INDEX":
-      outf.write(section, "(\n")
-      processIndex(r, r.index, outf)
-      outf.write(")\n")
-    of "IMPORTS":
-      outf.write(section, "(\n")
-      processIndex(r, r.imports, outf)
-      outf.write(")\n")
-    of "CONVERTERS",  "METHODS":
-      inc r.pos
-      if section == "METHODS": r.methodsIdx = r.pos
-      else: r.convertersIdx = r.pos
-      outf.write(section, ":")
-      while r.s[r.pos] > '\x0A':
-        let d = decodeVInt(r.s, r.pos)
-        outf.write(" ", $d)
-        if r.s[r.pos] == ' ': inc(r.pos)
-      outf.write("\n")
-    of "DATA":
-      inc(r.pos, 2)
-      r.dataIdx = r.pos
-      outf.write("DATA(\n")
-      while r.s[r.pos] != ')':
-        if r.s[r.pos] == '(':
-          outf.writeNode decodeNode(r, unknownLineInfo())
-          outf.write("\n")
-        elif r.s[r.pos] == '[':
-          outf.writeType decodeType(r, unknownLineInfo())
-        else:
-          outf.writeSym decodeSym(r, unknownLineInfo())
-        if r.s[r.pos] == '\x0A':
-          inc(r.pos)
-          inc(r.line)
-      if r.s[r.pos] == ')': inc r.pos
-      outf.write(")\n")
-    of "INIT":
-      outf.write("INIT(\n")
-      inc r.pos, 2
-      r.initIdx = r.pos
-      while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')':
-        let d = decodeVInt(r.s, r.pos)
-        inc(r.pos)                # #10
-        #let p = r.pos
-        #r.pos = d + r.dataIdx
-        #outf.writeNode decodeNode(r, UnknownLineInfo())
-        #outf.write("\n")
-        #r.pos = p
-      if r.s[r.pos] == ')': inc r.pos
-      outf.write("<not supported by viewer>)\n")
-    else:
-      internalError(r.config, "invalid section: '" & section &
-                    "' at " & $r.line & " in " & r.filename)
-      skipSection(r)
-    if r.s[r.pos] == '\x0A':
-      inc(r.pos)
-      inc(r.line)
-  outf.close
-
-when isMainModule:
-  viewFile(paramStr(1).addFileExt(RodExt))
diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim
deleted file mode 100644
index 4686baf2b..000000000
--- a/compiler/rodwrite.nim
+++ /dev/null
@@ -1,659 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-# This module is responsible for writing of rod files. Note that writing of
-# rod files is a pass, reading of rod files is not! This is why reading and
-# writing of rod files is split into two different modules.
-
-import
-  intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform,
-  condsyms, ropes, idents, std / sha1, rodread, passes, idgen,
-  rodutils, modulepaths
-
-from modulegraphs import ModuleGraph
-
-type
-  TRodWriter = object of TPassContext
-    module: PSym
-    hash: SecureHash
-    options: TOptions
-    defines: string
-    inclDeps: string
-    modDeps: string
-    interf: string
-    compilerProcs: string
-    index, imports: TIndex
-    converters, methods: string
-    init: string
-    data: string
-    sstack: TSymSeq          # a stack of symbols to process
-    tstack: TTypeSeq         # a stack of types to process
-    files: TStringSeq
-    origFile: string
-    cache: IdentCache
-    config: ConfigRef
-
-  PRodWriter = ref TRodWriter
-
-proc getDefines(conf: ConfigRef): string =
-  result = ""
-  for d in definedSymbolNames(conf.symbols):
-    if result.len != 0: add(result, " ")
-    add(result, d)
-
-proc fileIdx(w: PRodWriter, filename: string): int =
-  for i in countup(0, high(w.files)):
-    if w.files[i] == filename:
-      return i
-  result = len(w.files)
-  setLen(w.files, result + 1)
-  w.files[result] = filename
-
-template filename*(w: PRodWriter): string =
-  toFilename(FileIndex w.module.position)
-
-proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache;
-                  config: ConfigRef): PRodWriter =
-  new(result)
-  result.config = config
-  result.sstack = @[]
-  result.tstack = @[]
-  initIiTable(result.index.tab)
-  initIiTable(result.imports.tab)
-  result.index.r = ""
-  result.imports.r = ""
-  result.hash = hash
-  result.module = module
-  result.defines = getDefines(config)
-  result.options = config.options
-  result.files = @[]
-  result.inclDeps = ""
-  result.modDeps = ""
-  result.interf = newStringOfCap(2_000)
-  result.compilerProcs = ""
-  result.converters = ""
-  result.methods = ""
-  result.init = ""
-  result.origFile = module.info.toFullPath
-  result.data = newStringOfCap(12_000)
-  result.cache = cache
-
-proc addModDep(w: PRodWriter, dep: string; info: TLineInfo) =
-  if w.modDeps.len != 0: add(w.modDeps, ' ')
-  let resolved = findModule(w.config, dep, info.toFullPath)
-  encodeVInt(fileIdx(w, resolved), w.modDeps)
-
-const
-  rodNL = "\x0A"
-
-proc addInclDep(w: PRodWriter, dep: string; info: TLineInfo) =
-  let resolved = findModule(w.config, dep, info.toFullPath)
-  encodeVInt(fileIdx(w, resolved), w.inclDeps)
-  add(w.inclDeps, " ")
-  encodeStr($secureHashFile(resolved), w.inclDeps)
-  add(w.inclDeps, rodNL)
-
-proc pushType(w: PRodWriter, t: PType) =
-  # check so that the stack does not grow too large:
-  if iiTableGet(w.index.tab, t.id) == InvalidKey:
-    w.tstack.add(t)
-
-proc pushSym(w: PRodWriter, s: PSym) =
-  # check so that the stack does not grow too large:
-  if iiTableGet(w.index.tab, s.id) == InvalidKey:
-    when false:
-      if s.kind == skMethod:
-        echo "encoding ", s.id, " ", s.name.s
-        writeStackTrace()
-    w.sstack.add(s)
-
-proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
-                result: var string) =
-  if n == nil:
-    # nil nodes have to be stored too:
-    result.add("()")
-    return
-  result.add('(')
-  encodeVInt(ord(n.kind), result)
-  # we do not write comments for now
-  # Line information takes easily 20% or more of the filesize! Therefore we
-  # omit line information if it is the same as the father's line information:
-  if fInfo.fileIndex != n.info.fileIndex:
-    result.add('?')
-    encodeVInt(n.info.col, result)
-    result.add(',')
-    encodeVInt(int n.info.line, result)
-    result.add(',')
-    encodeVInt(fileIdx(w, toFullPath(n.info)), result)
-  elif fInfo.line != n.info.line:
-    result.add('?')
-    encodeVInt(n.info.col, result)
-    result.add(',')
-    encodeVInt(int n.info.line, result)
-  elif fInfo.col != n.info.col:
-    result.add('?')
-    encodeVInt(n.info.col, result)
-  # No need to output the file index, as this is the serialization of one
-  # file.
-  var f = n.flags * PersistentNodeFlags
-  if f != {}:
-    result.add('$')
-    encodeVInt(cast[int32](f), result)
-  if n.typ != nil:
-    result.add('^')
-    encodeVInt(n.typ.id, result)
-    pushType(w, n.typ)
-  case n.kind
-  of nkCharLit..nkUInt64Lit:
-    if n.intVal != 0:
-      result.add('!')
-      encodeVBiggestInt(n.intVal, result)
-  of nkFloatLit..nkFloat64Lit:
-    if n.floatVal != 0.0:
-      result.add('!')
-      encodeStr($n.floatVal, result)
-  of nkStrLit..nkTripleStrLit:
-    if n.strVal != "":
-      result.add('!')
-      encodeStr(n.strVal, result)
-  of nkIdent:
-    result.add('!')
-    encodeStr(n.ident.s, result)
-  of nkSym:
-    result.add('!')
-    encodeVInt(n.sym.id, result)
-    pushSym(w, n.sym)
-  else:
-    for i in countup(0, sonsLen(n) - 1):
-      encodeNode(w, n.info, n.sons[i], result)
-  add(result, ')')
-
-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.storage != low(loc.storage):
-    add(result, '*')
-    encodeVInt(ord(loc.storage), result)
-  if loc.flags != {}:
-    add(result, '$')
-    encodeVInt(cast[int32](loc.flags), result)
-  if loc.lode != nil:
-    add(result, '^')
-    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)
-  if oldLen + 1 == result.len:
-    # no data was necessary, so remove the '<' again:
-    setLen(result, oldLen)
-  else:
-    add(result, '>')
-
-proc encodeType(w: PRodWriter, t: PType, result: var string) =
-  if t == nil:
-    # nil nodes have to be stored too:
-    result.add("[]")
-    return
-  # we need no surrounding [] here because the type is in a line of its own
-  if t.kind == tyForward: internalError(w.config, "encodeType: tyForward")
-  # for the new rodfile viewer we use a preceding [ so that the data section
-  # can easily be disambiguated:
-  add(result, '[')
-  encodeVInt(ord(t.kind), result)
-  add(result, '+')
-  encodeVInt(t.id, result)
-  if t.n != nil:
-    encodeNode(w, unknownLineInfo(), t.n, result)
-  if t.flags != {}:
-    add(result, '$')
-    encodeVInt(cast[int32](t.flags), result)
-  if t.callConv != low(t.callConv):
-    add(result, '?')
-    encodeVInt(ord(t.callConv), result)
-  if t.owner != nil:
-    add(result, '*')
-    encodeVInt(t.owner.id, result)
-    pushSym(w, t.owner)
-  if t.sym != nil:
-    add(result, '&')
-    encodeVInt(t.sym.id, result)
-    pushSym(w, t.sym)
-  if t.size != - 1:
-    add(result, '/')
-    encodeVBiggestInt(t.size, result)
-  if t.align != 2:
-    add(result, '=')
-    encodeVInt(t.align, result)
-  if t.lockLevel.ord != UnspecifiedLockLevel.ord:
-    add(result, '\14')
-    encodeVInt(t.lockLevel.int16, result)
-  if t.destructor != nil and t.destructor.id != 0:
-    add(result, '\15')
-    encodeVInt(t.destructor.id, result)
-    pushSym(w, t.destructor)
-  if t.deepCopy != nil:
-    add(result, '\16')
-    encodeVInt(t.deepcopy.id, result)
-    pushSym(w, t.deepcopy)
-  if t.assignment != nil:
-    add(result, '\17')
-    encodeVInt(t.assignment.id, result)
-    pushSym(w, t.assignment)
-  if t.sink != nil:
-    add(result, '\18')
-    encodeVInt(t.sink.id, result)
-    pushSym(w, t.sink)
-  for i, s in items(t.methods):
-    add(result, '\19')
-    encodeVInt(i, result)
-    add(result, '\20')
-    encodeVInt(s.id, result)
-    pushSym(w, s)
-  encodeLoc(w, t.loc, result)
-  for i in countup(0, sonsLen(t) - 1):
-    if t.sons[i] == nil:
-      add(result, "^()")
-    else:
-      add(result, '^')
-      encodeVInt(t.sons[i].id, result)
-      pushType(w, t.sons[i])
-
-proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
-  add(result, '|')
-  encodeVInt(ord(lib.kind), result)
-  add(result, '|')
-  encodeStr($lib.name, result)
-  add(result, '|')
-  encodeNode(w, info, lib.path, result)
-
-proc encodeInstantiations(w: PRodWriter; s: seq[PInstantiation];
-                          result: var string) =
-  for t in s:
-    result.add('\15')
-    encodeVInt(t.sym.id, result)
-    pushSym(w, t.sym)
-    for tt in t.concreteTypes:
-      result.add('\17')
-      encodeVInt(tt.id, result)
-      pushType(w, tt)
-    result.add('\20')
-    encodeVInt(t.compilesId, result)
-
-proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
-  if s == nil:
-    # nil nodes have to be stored too:
-    result.add("{}")
-    return
-  # we need no surrounding {} here because the symbol is in a line of its own
-  encodeVInt(ord(s.kind), result)
-  result.add('+')
-  encodeVInt(s.id, result)
-  result.add('&')
-  encodeStr(s.name.s, result)
-  if s.typ != nil:
-    result.add('^')
-    encodeVInt(s.typ.id, result)
-    pushType(w, s.typ)
-  result.add('?')
-  if s.info.col != -1'i16: encodeVInt(s.info.col, result)
-  result.add(',')
-  if s.info.line != 0'u16: encodeVInt(int s.info.line, result)
-  result.add(',')
-  encodeVInt(fileIdx(w, toFullPath(s.info)), result)
-  if s.owner != nil:
-    result.add('*')
-    encodeVInt(s.owner.id, result)
-    pushSym(w, s.owner)
-  if s.flags != {}:
-    result.add('$')
-    encodeVInt(cast[int32](s.flags), result)
-  if s.magic != mNone:
-    result.add('@')
-    encodeVInt(ord(s.magic), result)
-  if s.options != w.options:
-    result.add('!')
-    encodeVInt(cast[int32](s.options), result)
-  if s.position != 0:
-    result.add('%')
-    encodeVInt(s.position, result)
-  if s.offset != - 1:
-    result.add('`')
-    encodeVInt(s.offset, result)
-  encodeLoc(w, s.loc, result)
-  if s.annex != nil: encodeLib(w, s.annex, s.info, result)
-  if s.constraint != nil:
-    add(result, '#')
-    encodeNode(w, unknownLineInfo(), s.constraint, result)
-  case s.kind
-  of skType, skGenericParam:
-    for t in s.typeInstCache:
-      result.add('\14')
-      encodeVInt(t.id, result)
-      pushType(w, t)
-  of routineKinds:
-    encodeInstantiations(w, s.procInstCache, result)
-    if s.gcUnsafetyReason != nil:
-      result.add('\16')
-      encodeVInt(s.gcUnsafetyReason.id, result)
-      pushSym(w, s.gcUnsafetyReason)
-  of skModule, skPackage:
-    encodeInstantiations(w, s.usedGenerics, result)
-    # we don't serialize:
-    #tab*: TStrTable         # interface table for modules
-  of skLet, skVar, skField, skForVar:
-    if s.guard != nil:
-      result.add('\18')
-      encodeVInt(s.guard.id, result)
-      pushSym(w, s.guard)
-    if s.bitsize != 0:
-      result.add('\19')
-      encodeVInt(s.bitsize, result)
-  else: discard
-  # lazy loading will soon reload the ast lazily, so the ast needs to be
-  # the last entry of a symbol:
-  if s.ast != nil:
-    # we used to attempt to save space here by only storing a dummy AST if
-    # it is not necessary, but Nim's heavy compile-time evaluation features
-    # make that unfeasible nowadays:
-    encodeNode(w, s.info, s.ast, result)
-
-proc addToIndex(w: var TIndex, key, val: int) =
-  if key - w.lastIdxKey == 1:
-    # we do not store a key-diff of 1 to safe space
-    encodeVInt(val - w.lastIdxVal, w.r)
-  else:
-    encodeVInt(key - w.lastIdxKey, w.r)
-    add(w.r, ' ')
-    encodeVInt(val - w.lastIdxVal, w.r)
-  add(w.r, rodNL)
-  w.lastIdxKey = key
-  w.lastIdxVal = val
-  iiTablePut(w.tab, key, val)
-
-const debugWrittenIds = false
-
-when debugWrittenIds:
-  var debugWritten = initIntSet()
-
-proc symStack(w: PRodWriter): int =
-  var i = 0
-  while i < len(w.sstack):
-    var s = w.sstack[i]
-    if sfForward in s.flags:
-      w.sstack[result] = s
-      inc result
-    elif iiTableGet(w.index.tab, s.id) == InvalidKey:
-      var m = getModule(s)
-      #if m == nil and s.kind != skPackage and sfGenSym notin s.flags:
-      #  internalError("symStack: module nil: " & s.name.s & " " & $s.kind & " ID " & $s.id)
-      if m == nil or s.kind == skPackage or {sfFromGeneric, sfGenSym} * s.flags != {} or m.id == w.module.id:
-        # put definition in here
-        var L = w.data.len
-        addToIndex(w.index, s.id, L)
-        when debugWrittenIds: incl(debugWritten, s.id)
-        encodeSym(w, s, w.data)
-        add(w.data, rodNL)
-        # put into interface section if appropriate:
-        if {sfExported, sfFromGeneric} * s.flags == {sfExported} and
-            s.kind in ExportableSymKinds:
-          encodeStr(s.name.s, w.interf)
-          add(w.interf, ' ')
-          encodeVInt(s.id, w.interf)
-          add(w.interf, rodNL)
-        if sfCompilerProc in s.flags:
-          encodeStr(s.name.s, w.compilerProcs)
-          add(w.compilerProcs, ' ')
-          encodeVInt(s.id, w.compilerProcs)
-          add(w.compilerProcs, rodNL)
-        if s.kind == skConverter or (s.ast != nil and hasPattern(s)):
-          if w.converters.len != 0: add(w.converters, ' ')
-          encodeVInt(s.id, w.converters)
-        if s.kind == skMethod and sfDispatcher notin s.flags:
-          if w.methods.len != 0: add(w.methods, ' ')
-          encodeVInt(s.id, w.methods)
-      elif iiTableGet(w.imports.tab, s.id) == InvalidKey:
-        addToIndex(w.imports, s.id, m.id)
-        when debugWrittenIds:
-          if not contains(debugWritten, s.id):
-            echo(w.filename)
-            debug(s)
-            debug(s.owner)
-            debug(m)
-            internalError("Symbol referred to but never written")
-    inc(i)
-  setLen(w.sstack, result)
-
-proc typeStack(w: PRodWriter): int =
-  var i = 0
-  while i < len(w.tstack):
-    var t = w.tstack[i]
-    if t.kind == tyForward:
-      w.tstack[result] = t
-      inc result
-    elif iiTableGet(w.index.tab, t.id) == InvalidKey:
-      var L = w.data.len
-      addToIndex(w.index, t.id, L)
-      encodeType(w, t, w.data)
-      add(w.data, rodNL)
-    inc(i)
-  setLen(w.tstack, result)
-
-proc processStacks(w: PRodWriter, finalPass: bool) =
-  var oldS = 0
-  var oldT = 0
-  while true:
-    var slen = symStack(w)
-    var tlen = typeStack(w)
-    if slen == oldS and tlen == oldT: break
-    oldS = slen
-    oldT = tlen
-  if finalPass and (oldS != 0 or oldT != 0):
-    internalError(w.config, "could not serialize some forwarded symbols/types")
-
-proc rawAddInterfaceSym(w: PRodWriter, s: PSym) =
-  pushSym(w, s)
-  processStacks(w, false)
-
-proc addInterfaceSym(w: PRodWriter, s: PSym) =
-  if w == nil: return
-  if s.kind in ExportableSymKinds and
-      {sfExported, sfCompilerProc} * s.flags != {}:
-    rawAddInterfaceSym(w, s)
-
-proc addStmt(w: PRodWriter, n: PNode) =
-  encodeVInt(w.data.len, w.init)
-  add(w.init, rodNL)
-  encodeNode(w, unknownLineInfo(), n, w.data)
-  add(w.data, rodNL)
-  processStacks(w, false)
-
-proc writeRod(w: PRodWriter) =
-  processStacks(w, true)
-  var f: File
-  if not open(f, completeGeneratedFilePath(w.config, changeFileExt(
-                      withPackageName(w.config, w.filename), RodExt)),
-              fmWrite):
-    #echo "couldn't write rod file for: ", w.filename
-    return
-  # write header:
-  f.write("NIM:")
-  f.write(RodFileVersion)
-  f.write(rodNL)
-  var id = "ID:"
-  encodeVInt(w.module.id, id)
-  f.write(id)
-  f.write(rodNL)
-
-  var orig = "ORIGFILE:"
-  encodeStr(w.origFile, orig)
-  f.write(orig)
-  f.write(rodNL)
-
-  var hash = "HASH:"
-  encodeStr($w.hash, hash)
-  f.write(hash)
-  f.write(rodNL)
-
-  var options = "OPTIONS:"
-  encodeVInt(cast[int32](w.options), options)
-  f.write(options)
-  f.write(rodNL)
-
-  var goptions = "GOPTIONS:"
-  encodeVInt(cast[int32](w.config.globalOptions), goptions)
-  f.write(goptions)
-  f.write(rodNL)
-
-  var cmd = "CMD:"
-  encodeVInt(cast[int32](w.config.cmd), cmd)
-  f.write(cmd)
-  f.write(rodNL)
-
-  f.write("DEFINES:")
-  f.write(w.defines)
-  f.write(rodNL)
-
-  var files = "FILES(" & rodNL
-  for i in countup(0, high(w.files)):
-    encodeStr(w.files[i], files)
-    files.add(rodNL)
-  f.write(files)
-  f.write(')' & rodNL)
-
-  f.write("INCLUDES(" & rodNL)
-  f.write(w.inclDeps)
-  f.write(')' & rodNL)
-
-  f.write("DEPS:")
-  f.write(w.modDeps)
-  f.write(rodNL)
-
-  f.write("INTERF(" & rodNL)
-  f.write(w.interf)
-  f.write(')' & rodNL)
-
-  f.write("COMPILERPROCS(" & rodNL)
-  f.write(w.compilerProcs)
-  f.write(')' & rodNL)
-
-  f.write("INDEX(" & rodNL)
-  f.write(w.index.r)
-  f.write(')' & rodNL)
-
-  f.write("IMPORTS(" & rodNL)
-  f.write(w.imports.r)
-  f.write(')' & rodNL)
-
-  f.write("CONVERTERS:")
-  f.write(w.converters)
-  f.write(rodNL)
-
-  f.write("METHODS:")
-  f.write(w.methods)
-  f.write(rodNL)
-
-  f.write("INIT(" & rodNL)
-  f.write(w.init)
-  f.write(')' & rodNL)
-
-  f.write("DATA(" & rodNL)
-  f.write(w.data)
-  f.write(')' & rodNL)
-  # write trailing zero which is necessary because we use memory mapped files
-  # for reading:
-  f.write("\0")
-  f.close()
-
-  #echo "interf: ", w.interf.len
-  #echo "index:  ", w.index.r.len
-  #echo "init:   ", w.init.len
-  #echo "data:   ", w.data.len
-
-proc process(c: PPassContext, n: PNode): PNode =
-  result = n
-  if c == nil: return
-  var w = PRodWriter(c)
-  case n.kind
-  of nkStmtList:
-    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, nkFuncDef, nkIteratorDef, nkConverterDef,
-      nkTemplateDef, nkMacroDef:
-    let s = n.sons[namePos].sym
-    if s == nil: internalError(w.config, n.info, "rodwrite.process")
-    if n.sons[bodyPos] == nil:
-      internalError(w.config, n.info, "rodwrite.process: body is nil")
-    if n.sons[bodyPos].kind != nkEmpty or s.magic != mNone or
-        sfForward notin s.flags:
-      addInterfaceSym(w, s)
-  of nkMethodDef:
-    let s = n.sons[namePos].sym
-    if s == nil: internalError(w.config, n.info, "rodwrite.process")
-    if n.sons[bodyPos] == nil:
-      internalError(w.config, n.info, "rodwrite.process: body is nil")
-    if n.sons[bodyPos].kind != nkEmpty or s.magic != mNone or
-        sfForward notin s.flags:
-      pushSym(w, s)
-      processStacks(w, false)
-
-  of nkVarSection, nkLetSection, nkConstSection:
-    for i in countup(0, sonsLen(n) - 1):
-      var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue
-      addInterfaceSym(w, a.sons[0].sym)
-  of nkTypeSection:
-    for i in countup(0, sonsLen(n) - 1):
-      var a = n.sons[i]
-      if a.kind == nkCommentStmt: continue
-      if a.sons[0].kind != nkSym: internalError(w.config, a.info, "rodwrite.process")
-      var s = a.sons[0].sym
-      addInterfaceSym(w, s)
-      # this takes care of enum fields too
-      # Note: The check for ``s.typ.kind = tyEnum`` is wrong for enum
-      # type aliasing! Otherwise the same enum symbol would be included
-      # several times!
-      #
-      #        if (a.sons[2] <> nil) and (a.sons[2].kind = nkEnumTy) then begin
-      #          a := s.typ.n;
-      #          for j := 0 to sonsLen(a)-1 do
-      #            addInterfaceSym(w, a.sons[j].sym);
-      #        end
-  of nkImportStmt:
-    for i in countup(0, sonsLen(n) - 1):
-      addModDep(w, getModuleName(w.config, n.sons[i]), n.info)
-    addStmt(w, n)
-  of nkFromStmt, nkImportExceptStmt:
-    addModDep(w, getModuleName(w.config, n.sons[0]), n.info)
-    addStmt(w, n)
-  of nkIncludeStmt:
-    for i in countup(0, sonsLen(n) - 1):
-      addInclDep(w, getModuleName(w.config, n.sons[i]), n.info)
-  of nkPragma:
-    addStmt(w, n)
-  else:
-    discard
-
-proc myOpen(g: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
-  if module.id < 0: internalError(g.config, "rodwrite: module ID not set")
-  var w = newRodWriter(rodread.getHash FileIndex module.position, module, cache, g.config)
-  rawAddInterfaceSym(w, module)
-  result = w
-
-proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode =
-  result = process(c, n)
-  var w = PRodWriter(c)
-  writeRod(w)
-  idgen.saveMaxIds(graph.config, graph.config.projectPath / graph.config.projectName)
-
-const rodwritePass* = makePass(open = myOpen, close = myClose, process = process)
-
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index 05d5e840c..973f16916 100644
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -56,7 +56,7 @@
 #  To cache them they are inserted in a `cache` array.
 
 import
-  platform, hashes
+  hashes
 
 type
   FormatStr* = string  # later we may change it to CString for better
@@ -70,17 +70,6 @@ type
     length*: int
     data*: string             # != nil if a leaf
 
-  RopeSeq* = seq[Rope]
-
-  RopesError* = enum
-    rCannotOpenFile
-    rInvalidFormatStr
-
-# implementation
-
-var errorHandler*: proc(err: RopesError, msg: string, useWarning = false)
-  # avoid dependency on msgs.nim
-
 proc len*(a: Rope): int =
   ## the rope's length
   if a == nil: result = 0
@@ -102,7 +91,7 @@ proc freezeMutableRope*(r: Rope) {.inline.} =
   r.length = r.data.len
 
 var
-  cache: array[0..2048*2 - 1, Rope]
+  cache: array[0..2048*2 - 1, Rope] # XXX Global here!
 
 proc resetRopeCache* =
   for i in low(cache)..high(cache):
@@ -204,13 +193,14 @@ proc writeRope*(f: File, r: Rope) =
   ## writes a rope to a file.
   for s in leaves(r): write(f, s)
 
-proc writeRope*(head: Rope, filename: string, useWarning = false) =
+proc writeRope*(head: Rope, filename: string): bool =
   var f: File
   if open(f, filename, fmWrite):
     if head != nil: writeRope(f, head)
     close(f)
+    result = true
   else:
-    errorHandler(rCannotOpenFile, filename, useWarning)
+    result = false
 
 proc `$`*(r: Rope): string =
   ## converts a rope back to a string.
@@ -225,11 +215,6 @@ proc ropeConcat*(a: varargs[Rope]): Rope =
 proc prepend*(a: var Rope, b: Rope) = a = b & a
 proc prepend*(a: var Rope, b: string) = a = b & a
 
-var
-  rnl* = tnl.newRope
-  softRnl* = tnl.newRope
-  noRnl* = "".newRope
-
 proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
   var i = 0
   var length = len(frmt)
@@ -254,7 +239,7 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
           if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
         num = j
         if j > high(args) + 1:
-          errorHandler(rInvalidFormatStr, $(j))
+          doAssert false, "invalid format string: " & frmt
         else:
           add(result, args[j-1])
       of '{':
@@ -265,20 +250,21 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
           inc(i)
         num = j
         if frmt[i] == '}': inc(i)
-        else: errorHandler(rInvalidFormatStr, $(frmt[i]))
+        else:
+          doAssert false, "invalid format string: " & frmt
 
         if j > high(args) + 1:
-          errorHandler(rInvalidFormatStr, $(j))
+          doAssert false, "invalid format string: " & frmt
         else:
           add(result, args[j-1])
       of 'n':
-        add(result, softRnl)
+        add(result, "\n")
         inc(i)
       of 'N':
-        add(result, rnl)
+        add(result, "\n")
         inc(i)
       else:
-        errorHandler(rInvalidFormatStr, $(frmt[i]))
+        doAssert false, "invalid format string: " & frmt
     var start = i
     while i < length:
       if frmt[i] != '$': inc(i)
@@ -350,7 +336,6 @@ proc equalsFile*(r: Rope, filename: string): bool =
 proc writeRopeIfNotEqual*(r: Rope, filename: string): bool =
   # returns true if overwritten
   if not equalsFile(r, filename):
-    writeRope(r, filename)
-    result = true
+    result = writeRope(r, filename)
   else:
     result = false
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index 30e5e4803..ae7e030b8 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -13,7 +13,7 @@
 import
   ast, modules, idents, passes, passaux, condsyms,
   options, nimconf, sem, semdata, llstream, vm, vmdef, commands, msgs,
-  os, times, osproc, wordrecg, strtabs, modulegraphs, configuration
+  os, times, osproc, wordrecg, strtabs, modulegraphs, lineinfos
 
 # we support 'cmpIgnoreStyle' natively for efficiency:
 from strutils import cmpIgnoreStyle, contains
@@ -152,28 +152,27 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
 proc runNimScript*(cache: IdentCache; scriptName: string;
                    freshDefines=true; conf: ConfigRef) =
   rawMessage(conf, hintConf, scriptName)
-  passes.gIncludeFile = includeModule
-  passes.gImportModule = importModule
-  let graph = newModuleGraph(conf)
+
+  let graph = newModuleGraph(cache, conf)
+  connectCallbacks(graph)
   if freshDefines: initDefines(conf.symbols)
 
   defineSymbol(conf.symbols, "nimscript")
   defineSymbol(conf.symbols, "nimconfig")
-  registerPass(semPass)
-  registerPass(evalPass)
+  registerPass(graph, semPass)
+  registerPass(graph, evalPass)
 
   conf.searchPaths.add(conf.libpath)
 
   var m = graph.makeModule(scriptName)
   incl(m.flags, sfMainModule)
-  vm.globalCtx = setupVM(m, cache, scriptName, graph)
+  graph.vm = setupVM(m, cache, scriptName, graph)
 
-  graph.compileSystemModule(cache)
-  discard graph.processModule(m, llStreamOpen(scriptName, fmRead), nil, cache)
+  graph.compileSystemModule()
+  discard graph.processModule(m, llStreamOpen(scriptName, fmRead))
 
   # ensure we load 'system.nim' again for the real non-config stuff!
   resetSystemArtifacts(graph)
-  vm.globalCtx = nil
   # do not remove the defined symbols
   #initDefines()
   undefSymbol(conf.symbols, "nimscript")
diff --git a/compiler/sem.nim b/compiler/sem.nim
index d56355f14..299286545 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -13,10 +13,10 @@ import
   ast, strutils, hashes, options, lexer, astalgo, trees, treetab,
   wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
   magicsys, parser, nversion, nimsets, semfold, modulepaths, importer,
-  procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
+  procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch,
   intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
-  evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity,
-  semparallel, lowerings, pluginsupport, plugins.active, rod, configuration
+  evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
+  semparallel, lowerings, pluginsupport, plugins.active, rod, lineinfos
 
 from modulegraphs import ModuleGraph
 
@@ -48,15 +48,18 @@ proc semQuoteAst(c: PContext, n: PNode): PNode
 proc finishMethod(c: PContext, s: PSym)
 proc evalAtCompileTime(c: PContext, n: PNode): PNode
 proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
-
+proc semStaticExpr(c: PContext, n: PNode): PNode
+proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType
+proc semTypeOf(c: PContext; n: PNode): PNode
+proc hasUnresolvedArgs(c: PContext, n: PNode): bool
 proc isArrayConstr(n: PNode): bool {.inline.} =
   result = n.kind == nkBracket and
     n.typ.skipTypes(abstractInst).kind == tyArray
 
-template semIdeForTemplateOrGenericCheck(n, requiresCheck) =
+template semIdeForTemplateOrGenericCheck(conf, n, requiresCheck) =
   # we check quickly if the node is where the cursor is
   when defined(nimsuggest):
-    if n.info.fileIndex == gTrackPos.fileIndex and n.info.line == gTrackPos.line:
+    if n.info.fileIndex == conf.m.trackPos.fileIndex and n.info.line == conf.m.trackPos.line:
       requiresCheck = true
 
 template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
@@ -70,6 +73,16 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
       #  echo "passing to safeSemExpr: ", renderTree(n)
       discard safeSemExpr(c, n)
 
+proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
+  result = arg
+  let x = result.skipConv
+  if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr:
+    changeType(c, x, formal, check=true)
+  else:
+    result = skipHiddenSubConv(result)
+    #result.typ = takeType(formal, arg.typ)
+    #echo arg.info, " picked ", result.typ.typeToString
+
 proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
   if arg.typ.isNil:
     localError(c.config, arg.info, "expression has no type: " &
@@ -85,18 +98,12 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
       result = copyTree(arg)
       result.typ = formal
     else:
-      let x = result.skipConv
-      if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr:
-        changeType(c, x, formal, check=true)
-      else:
-        result = skipHiddenSubConv(result)
-        #result.typ = takeType(formal, arg.typ)
-        #echo arg.info, " picked ", result.typ.typeToString
+      result = fitNodePostMatch(c, formal, result)
 
 proc inferWithMetatype(c: PContext, formal: PType,
                        arg: PNode, coerceDistincts = false): PNode
 
-var commonTypeBegin = PType(kind: tyExpr)
+template commonTypeBegin*(): PType = PType(kind: tyExpr)
 
 proc commonType*(x, y: PType): PType =
   # new type relation that is used for array constructors,
@@ -183,7 +190,7 @@ proc commonType*(x: PType, y: PNode): PType =
   commonType(x, y.typ)
 
 proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
-  result = newSym(kind, considerQuotedIdent(c.config, n), getCurrOwner(c), n.info)
+  result = newSym(kind, considerQuotedIdent(c, n), getCurrOwner(c), n.info)
   when defined(nimsuggest):
     suggestDecl(c, n, result)
 
@@ -207,7 +214,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
     # template; we must fix it here: see #909
     result.owner = getCurrOwner(c)
   else:
-    result = newSym(kind, considerQuotedIdent(c.config, n), getCurrOwner(c), n.info)
+    result = newSym(kind, considerQuotedIdent(c, n), getCurrOwner(c), n.info)
   #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
   #  incl(result.flags, sfGlobal)
   when defined(nimsuggest):
@@ -242,14 +249,14 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
                   flags: TExprFlags = {}): PNode
 
-proc symFromType(t: PType, info: TLineInfo): PSym =
+proc symFromType(c: PContext; t: PType, info: TLineInfo): PSym =
   if t.sym != nil: return t.sym
-  result = newSym(skType, getIdent"AnonType", t.owner, info)
+  result = newSym(skType, getIdent(c.cache, "AnonType"), t.owner, info)
   result.flags.incl sfAnon
   result.typ = t
 
 proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode =
-  result = newSymNode(symFromType(t, info), info)
+  result = newSymNode(symFromType(c, t, info), info)
   result.typ = makeTypeDesc(c, t)
 
 when false:
@@ -310,13 +317,13 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
 
   let oldErrorCount = c.config.errorCounter
   let oldErrorMax = c.config.errorMax
-  let oldErrorOutputs = errorOutputs
+  let oldErrorOutputs = c.config.m.errorOutputs
 
-  errorOutputs = {}
+  c.config.m.errorOutputs = {}
   c.config.errorMax = high(int)
 
   try:
-    result = evalConstExpr(c.module, c.cache, c.graph, e)
+    result = evalConstExpr(c.module, c.graph, e)
     if result == nil or result.kind == nkEmpty:
       result = nil
     else:
@@ -327,7 +334,7 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
 
   c.config.errorCounter = oldErrorCount
   c.config.errorMax = oldErrorMax
-  errorOutputs = oldErrorOutputs
+  c.config.m.errorOutputs = oldErrorOutputs
 
 const
   errConstExprExpected = "constant expression expected"
@@ -340,12 +347,12 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
   result = getConstExpr(c.module, e, c.graph)
   if result == nil:
     #if e.kind == nkEmpty: globalError(n.info, errConstExprExpected)
-    result = evalConstExpr(c.module, c.cache, c.graph, e)
+    result = evalConstExpr(c.module, c.graph, e)
     if result == nil or result.kind == nkEmpty:
       if e.info != n.info:
-        pushInfoContext(n.info)
+        pushInfoContext(c.config, n.info)
         localError(c.config, e.info, errConstExprExpected)
-        popInfoContext()
+        popInfoContext(c.config)
       else:
         localError(c.config, e.info, errConstExprExpected)
       # error correction:
@@ -383,8 +390,8 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
   ## coherence, making sure that variables declared with 'let' aren't
   ## reassigned, and binding the unbound identifiers that the macro output
   ## contains.
-  inc(evalTemplateCounter)
-  if evalTemplateCounter > evalTemplateLimit:
+  inc(c.config.evalTemplateCounter)
+  if c.config.evalTemplateCounter > evalTemplateLimit:
     globalError(c.config, s.info, "template instantiation too nested")
   c.friendModules.add(s.owner.getModule)
 
@@ -423,8 +430,8 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
 
       result = semExpr(c, result, flags)
       result = fitNode(c, retType, result, result.info)
-      #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
-  dec(evalTemplateCounter)
+      #globalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
+  dec(c.config.evalTemplateCounter)
   discard c.friendModules.pop()
 
 const
@@ -432,7 +439,7 @@ const
 
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
                   flags: TExprFlags = {}): PNode =
-  pushInfoContext(nOrig.info)
+  pushInfoContext(c.config, nOrig.info)
 
   markUsed(c.config, n.info, sym, c.graph.usageSym)
   styleCheckUse(n.info, sym)
@@ -448,11 +455,11 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
 
   #if c.evalContext == nil:
   #  c.evalContext = c.createEvalContext(emStatic)
-  result = evalMacroCall(c.module, c.cache, c.graph, n, nOrig, sym)
+  result = evalMacroCall(c.module, c.graph, n, nOrig, sym)
   if efNoSemCheck notin flags:
     result = semAfterMacroCall(c, n, result, sym, flags)
   result = wrapInComesFrom(nOrig.info, sym, result)
-  popInfoContext()
+  popInfoContext(c.config)
 
 proc forceBool(c: PContext, n: PNode): PNode =
   result = fitNode(c, getSysType(c.graph, n.info, tyBool), n, n.info)
@@ -484,8 +491,8 @@ proc addCodeForGenerics(c: PContext, n: PNode) =
         addSon(n, prc.ast)
   c.lastGenericIdx = c.generics.len
 
-proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
-  var c = newContext(graph, module, cache)
+proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
+  var c = newContext(graph, module)
   if c.p != nil: internalError(graph.config, module.info, "sem.myOpen")
   c.semConstExpr = semConstExpr
   c.semExpr = semExpr
@@ -507,19 +514,13 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
     graph.systemModule = module
   c.topLevelScope = openScope(c)
   # don't be verbose unless the module belongs to the main package:
-  if module.owner.id == gMainPackageId:
+  if module.owner.id == graph.config.mainPackageId:
     graph.config.notes = graph.config.mainPackageNotes
   else:
     if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes
     graph.config.notes = graph.config.foreignPackageNotes
   result = c
 
-proc myOpenCached(graph: ModuleGraph; module: PSym; rd: PRodReader): PPassContext =
-  result = myOpen(graph, module, rd.cache)
-
-proc replayMethodDefs(graph: ModuleGraph; rd: PRodReader) =
-  for m in items(rd.methods): methodDef(graph, m, true)
-
 proc isImportSystemStmt(g: ModuleGraph; n: PNode): bool =
   if g.systemModule == nil: return false
   case n.kind
@@ -585,31 +586,31 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
   if c.config.errorMax <= 1:
     result = semStmtAndGenerateGenerics(c, n)
   else:
-    let oldContextLen = msgs.getInfoContextLen()
+    let oldContextLen = msgs.getInfoContextLen(c.config)
     let oldInGenericInst = c.inGenericInst
     try:
       result = semStmtAndGenerateGenerics(c, n)
     except ERecoverableError, ESuggestDone:
       recoverContext(c)
       c.inGenericInst = oldInGenericInst
-      msgs.setInfoContextLen(oldContextLen)
+      msgs.setInfoContextLen(c.config, oldContextLen)
       if getCurrentException() of ESuggestDone:
         c.suggestionsMade = true
         result = nil
       else:
-        result = ast.emptyNode
+        result = newNodeI(nkEmpty, n.info)
       #if c.config.cmd == cmdIdeTools: findSuggest(c, n)
-  rod.storeNode(c.module, result)
+  rod.storeNode(c.graph, c.module, result)
 
 proc testExamples(c: PContext) =
-  let inp = toFullPath(c.module.info)
+  let inp = toFullPath(c.config, c.module.info)
   let outp = inp.changeFileExt"" & "_examples.nim"
   renderModule(c.runnableExamples, inp, outp)
   let backend = if isDefined(c.config, "js"): "js"
                 elif isDefined(c.config, "cpp"): "cpp"
                 elif isDefined(c.config, "objc"): "objc"
                 else: "c"
-  if os.execShellCmd("nim " & backend & " -r " & outp) != 0:
+  if os.execShellCmd(os.getAppFilename() & " " & backend & " -r " & outp) != 0:
     quit "[Examples] failed"
   removeFile(outp)
 
@@ -625,12 +626,10 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
   addCodeForGenerics(c, result)
   if c.module.ast != nil:
     result.add(c.module.ast)
-  if c.rd != nil:
-    replayMethodDefs(graph, c.rd)
   popOwner(c)
   popProcCon(c)
-  storeRemaining(c.module)
+  storeRemaining(c.graph, c.module)
   if c.runnableExamples != nil: testExamples(c)
 
-const semPass* = makePass(myOpen, myOpenCached, myProcess, myClose,
+const semPass* = makePass(myOpen, myProcess, myClose,
                           isFrontend = true)
diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim
index f0fde195c..97ff4a7fc 100644
--- a/compiler/semasgn.nim
+++ b/compiler/semasgn.nim
@@ -157,12 +157,12 @@ proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
 proc addVar(father, v, value: PNode) =
   var vpart = newNodeI(nkIdentDefs, v.info, 3)
   vpart.sons[0] = v
-  vpart.sons[1] = ast.emptyNode
+  vpart.sons[1] = newNodeI(nkEmpty, v.info)
   vpart.sons[2] = value
   addSon(father, vpart)
 
 proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
-  var temp = newSym(skTemp, getIdent(lowerings.genPrefix), c.fn, c.info)
+  var temp = newSym(skTemp, getIdent(c.c.cache, lowerings.genPrefix), c.fn, c.info)
   temp.typ = getSysType(c.c.graph, body.info, tyInt)
   incl(temp.flags, sfFromGeneric)
 
@@ -207,7 +207,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       if t.kind == tySequence:
         # XXX add 'nil' handling here
         body.add newSeqCall(c.c, x, y)
-      let i = declareCounter(c, body, firstOrd(t))
+      let i = declareCounter(c, body, firstOrd(c.c.config, t))
       let whileLoop = genWhileLoop(c, i, x)
       let elemType = t.lastSon
       liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
@@ -268,17 +268,17 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
   a.kind = kind
   let body = newNodeI(nkStmtList, info)
   let procname = case kind
-                 of attachedAsgn: getIdent"="
-                 of attachedSink: getIdent"=sink"
-                 of attachedDeepCopy: getIdent"=deepcopy"
-                 of attachedDestructor: getIdent"=destroy"
+                 of attachedAsgn: getIdent(c.cache, "=")
+                 of attachedSink: getIdent(c.cache, "=sink")
+                 of attachedDeepCopy: getIdent(c.cache, "=deepcopy")
+                 of attachedDestructor: getIdent(c.cache, "=destroy")
 
   result = newSym(skProc, procname, typ.owner, info)
   a.fn = result
   a.asgnForType = typ
 
-  let dest = newSym(skParam, getIdent"dest", result, info)
-  let src = newSym(skParam, getIdent"src", result, info)
+  let dest = newSym(skParam, getIdent(c.cache, "dest"), result, info)
+  let src = newSym(skParam, getIdent(c.cache, "src"), result, info)
   dest.typ = makeVarType(c, typ)
   src.typ = typ
 
@@ -297,7 +297,7 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
   of attachedDestructor: typ.destructor = result
 
   var n = newNodeI(nkProcDef, info, bodyPos+1)
-  for i in 0 ..< n.len: n.sons[i] = emptyNode
+  for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info)
   n.sons[namePos] = newSymNode(result)
   n.sons[paramsPos] = result.typ.n
   n.sons[bodyPos] = body
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index df99d6c24..67fe99232 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -170,7 +170,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
       add(candidates, renderTree(err.sym.ast,
             {renderNoBody, renderNoComments, renderNoPragmas}))
     else:
-      add(candidates, err.sym.getProcHeader(prefer))
+      add(candidates, getProcHeader(c.config, err.sym, prefer))
     add(candidates, "\n")
     if err.firstMismatch != 0 and n.len > 1:
       let cond = n.len > 2
@@ -213,7 +213,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
   # Gives a detailed error message; this is separated from semOverloadedCall,
   # as semOverlodedCall is already pretty slow (and we need this information
   # only in case of an error).
-  if errorOutputs == {}:
+  if c.config.m.errorOutputs == {}:
     # fail fast:
     globalError(c.config, n.info, "type mismatch")
   if errors.len == 0:
@@ -293,7 +293,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
       orig.sons[0..1] = [nil, orig[1], f]
 
       template tryOp(x) =
-        let op = newIdentNode(getIdent(x), n.info)
+        let op = newIdentNode(getIdent(c.cache, x), n.info)
         n.sons[0] = op
         orig.sons[0] = op
         pickBest(op)
@@ -306,17 +306,17 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
 
     elif nfDotSetter in n.flags and f.kind == nkIdent and n.len == 3:
       # we need to strip away the trailing '=' here:
-      let calleeName = newIdentNode(getIdent(f.ident.s[0..f.ident.s.len-2]), n.info)
-      let callOp = newIdentNode(getIdent".=", n.info)
+      let calleeName = newIdentNode(getIdent(c.cache, f.ident.s[0..f.ident.s.len-2]), n.info)
+      let callOp = newIdentNode(getIdent(c.cache, ".="), n.info)
       n.sons[0..1] = [callOp, n[1], calleeName]
       orig.sons[0..1] = [callOp, orig[1], calleeName]
       pickBest(callOp)
 
     if overloadsState == csEmpty and result.state == csEmpty:
       if nfDotField in n.flags and nfExplicitCall notin n.flags:
-        localError(c.config, n.info, errUndeclaredField % considerQuotedIdent(c.config, f, n).s)
+        localError(c.config, n.info, errUndeclaredField % considerQuotedIdent(c, f, n).s)
       else:
-        localError(c.config, n.info, errUndeclaredRoutine % considerQuotedIdent(c.config, f, n).s)
+        localError(c.config, n.info, errUndeclaredRoutine % considerQuotedIdent(c, f, n).s)
       return
     elif result.state != csMatch:
       if nfExprCall in n.flags:
@@ -333,7 +333,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
     internalAssert c.config, result.state == csMatch
     #writeMatches(result)
     #writeMatches(alt)
-    if errorOutputs == {}:
+    if c.config.m.errorOutputs == {}:
       # quick error message for performance of 'compiles' built-in:
       globalError(c.config, n.info, errGenerated, "ambiguous call")
     elif c.config.errorCounter == 0:
@@ -345,7 +345,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
       add(args, ")")
 
       localError(c.config, n.info, errAmbiguousCallXYZ % [
-        getProcHeader(result.calleeSym), getProcHeader(alt.calleeSym),
+        getProcHeader(c.config, result.calleeSym),
+        getProcHeader(c.config, alt.calleeSym),
         args])
 
 proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
@@ -390,7 +391,23 @@ proc inferWithMetatype(c: PContext, formal: PType,
     result = copyTree(arg)
     result.typ = formal
 
-proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
+proc updateDefaultParams(call: PNode) =
+  # In generic procs, the default parameter may be unique for each
+  # instantiation (see tlateboundgenericparams).
+  # After a call is resolved, we need to re-assign any default value
+  # that was used during sigmatch. sigmatch is responsible for marking
+  # the default params with `nfDefaultParam` and `instantiateProcType`
+  # computes correctly the default values for each instantiation.
+  let calleeParams = call[0].sym.typ.n
+  for i in countdown(call.len - 1, 1):
+    if nfDefaultParam notin call[i].flags:
+      return
+    let def = calleeParams[i].sym.ast
+    if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam
+    call[i] = def
+
+proc semResolvedCall(c: PContext, x: TCandidate,
+                     n: PNode, flags: TExprFlags): PNode =
   assert x.state == csMatch
   var finalCallee = x.calleeSym
   markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym)
@@ -423,8 +440,9 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
 
   result = x.call
   instGenericConvertersSons(c, result, x)
-  result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
+  result[0] = newSymNode(finalCallee, result[0].info)
   result.typ = finalCallee.typ.sons[0]
+  updateDefaultParams(result)
 
 proc canDeref(n: PNode): bool {.inline.} =
   result = n.len >= 2 and (let t = n[1].typ;
@@ -446,7 +464,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
       message(c.config, n.info, hintUserRaw,
               "Non-matching candidates for " & renderTree(n) & "\n" &
               candidates)
-    result = semResolvedCall(c, n, r)
+    result = semResolvedCall(c, r, n, flags)
   elif implicitDeref in c.features and canDeref(n):
     # try to deref the first argument and then try overloading resolution again:
     #
@@ -457,7 +475,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
     #
     n.sons[1] = n.sons[1].tryDeref
     var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
-    if r.state == csMatch: result = semResolvedCall(c, n, r)
+    if r.state == csMatch: result = semResolvedCall(c, r, n, flags)
     else:
       # get rid of the deref again for a better error message:
       n.sons[1] = n.sons[1].sons[0]
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 12ac19ca3..aa0cb6e8e 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -13,8 +13,8 @@ import
   strutils, intsets, options, lexer, ast, astalgo, trees, treetab,
   wordrecg,
   ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
-  magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef,
-  modulegraphs, configuration
+  magicsys, nversion, nimsets, parser, times, passes, vmdef,
+  modulegraphs, lineinfos
 
 type
   TOptionEntry* = object      # entries to put on a stack for pragma parsing
@@ -75,6 +75,7 @@ type
 
   PContext* = ref TContext
   TContext* = object of TPassContext # a context represents a module
+    enforceVoidContext*: PType
     module*: PSym              # the module sym belonging to the context
     currentScope*: PScope      # current scope
     importTable*: PScope       # scope for all imported symbols
@@ -148,7 +149,7 @@ proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
 
 proc filename*(c: PContext): string =
   # the module's filename
-  return toFilename(FileIndex c.module.position)
+  return toFilename(c.config, FileIndex c.module.position)
 
 proc scopeDepth*(c: PContext): int {.inline.} =
   result = if c.currentScope != nil: c.currentScope.depthLevel
@@ -210,8 +211,9 @@ proc newOptionEntry*(conf: ConfigRef): POptionEntry =
   result.dynlib = nil
   result.notes = conf.notes
 
-proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext =
+proc newContext*(graph: ModuleGraph; module: PSym): PContext =
   new(result)
+  result.enforceVoidContext = PType(kind: tyStmt)
   result.ambiguousSymbols = initIntSet()
   result.optionStack = @[]
   result.libs = @[]
@@ -225,7 +227,7 @@ proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext
   initStrTable(result.userPragmas)
   result.generics = @[]
   result.unknownIdents = initIntSet()
-  result.cache = cache
+  result.cache = graph.cache
   result.graph = graph
   initStrTable(result.signatures)
   result.typesWithOps = @[]
@@ -286,7 +288,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
     result.addSonSkipIntLit(typ)
 
 proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
-  let typedesc = makeTypeDesc(c, typ)
+  let typedesc = newTypeS(tyTypeDesc, c)
+  typedesc.addSonSkipIntLit(assertNotNil(c.config, typ))
   let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
                    c.config.options).linkTo(typedesc)
   return newSymNode(sym, info)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 92b9c365a..5d6eaf652 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -26,10 +26,13 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
                      flags: TExprFlags = {}): PNode =
   markUsed(c.config, n.info, s, c.graph.usageSym)
   styleCheckUse(n.info, s)
-  pushInfoContext(n.info)
+  pushInfoContext(c.config, n.info)
   result = evalTemplate(n, s, getCurrOwner(c), c.config, efFromHlo in flags)
   if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags)
-  popInfoContext()
+  popInfoContext(c.config)
+
+  # XXX: A more elaborate line info rewrite might be needed
+  result.info = n.info
 
 proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
 
@@ -58,7 +61,7 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     # do not produce another redundant error message:
     #raiseRecoverableError("")
     result = errorNode(c, n)
-  if result.typ == nil or result.typ == enforceVoidContext:
+  if result.typ == nil or result.typ == c.enforceVoidContext:
     localError(c.config, n.info, errExprXHasNoType %
                 renderTree(result, {renderNoComments}))
     result.typ = errorType(c)
@@ -137,7 +140,7 @@ proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
     else:
       discard
 
-proc isCastable(dst, src: PType): bool =
+proc isCastable(conf: ConfigRef; dst, src: PType): bool =
   ## Checks whether the source type can be cast to the destination type.
   ## Casting is very unrestrictive; casts are allowed as long as
   ## castDest.size >= src.size, and typeAllowed(dst, skParam)
@@ -152,8 +155,8 @@ proc isCastable(dst, src: PType): bool =
     return false
 
   var dstSize, srcSize: BiggestInt
-  dstSize = computeSize(dst)
-  srcSize = computeSize(src)
+  dstSize = computeSize(conf, dst)
+  srcSize = computeSize(conf, src)
   if dstSize < 0:
     result = false
   elif srcSize < 0:
@@ -167,7 +170,7 @@ proc isCastable(dst, src: PType): bool =
         (skipTypes(dst, abstractInst).kind in IntegralTypes) or
         (skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes)
   if result and src.kind == tyNil:
-    result = dst.size <= platform.ptrSize
+    result = dst.size <= conf.target.ptrSize
 
 proc isSymChoice(n: PNode): bool {.inline.} =
   result = n.kind in nkSymChoices
@@ -188,7 +191,25 @@ proc semConv(c: PContext, n: PNode): PNode =
     return n
 
   result = newNodeI(nkConv, n.info)
-  var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
+
+  var targetType = semTypeNode(c, n.sons[0], nil)
+  if targetType.kind == tyTypeDesc:
+    internalAssert c.config, targetType.len > 0
+    if targetType.base.kind == tyNone:
+      return semTypeOf(c, n[1])
+    else:
+      targetType = targetType.base
+  elif targetType.kind == tyStatic:
+    var evaluated = semStaticExpr(c, n[1])
+    if evaluated.kind == nkType or evaluated.typ.kind == tyTypeDesc:
+      result = n
+      result.typ = c.makeTypeDesc semStaticType(c, evaluated, nil)
+      return
+    elif targetType.base.kind == tyNone:
+      return evaluated
+    else:
+      targetType = targetType.base
+
   maybeLiftType(targetType, c, n[0].info)
 
   if targetType.kind in {tySink, tyLent}:
@@ -251,7 +272,7 @@ proc semCast(c: PContext, n: PNode): PNode =
   let castedExpr = semExprWithType(c, n.sons[1])
   if tfHasMeta in targetType.flags:
     localError(c.config, n.sons[0].info, "cannot cast to a non concrete type: '$1'" % $targetType)
-  if not isCastable(targetType, castedExpr.typ):
+  if not isCastable(c.config, targetType, castedExpr.typ):
     let tar = $targetType
     let alt = typeToString(targetType, preferDesc)
     let msg = if tar != alt: tar & "=" & alt else: tar
@@ -268,7 +289,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
     localError(c.config, n.info, errXExpectsTypeOrValue % opToStr[m])
   else:
     n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
-    var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc})
+    var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc, tyUserTypeClassInst})
     case typ.kind
     of tySequence, tyString, tyCString, tyOpenArray, tyVarargs:
       n.typ = getSysType(c.graph, n.info, tyInt)
@@ -295,57 +316,98 @@ proc semSizeof(c: PContext, n: PNode): PNode =
   n.typ = getSysType(c.graph, n.info, tyInt)
   result = n
 
+proc fixupStaticType(c: PContext, n: PNode) =
+  # This proc can be applied to evaluated expressions to assign
+  # them a static type.
+  #
+  # XXX: with implicit static, this should not be necessary,
+  # because the output type of operations such as `semConstExpr`
+  # should be a static type (as well as the type of any other
+  # expression that can be implicitly evaluated). For now, we
+  # apply this measure only in code that is enlightened to work
+  # with static types.
+  if n.typ.kind != tyStatic:
+    n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ])
+    n.typ.n = n # XXX: cycles like the one here look dangerous.
+                # Consider using `n.copyTree`
+
 proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
-  internalAssert c.config, n.sonsLen == 3 and
-    n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
+  internalAssert c.config,
+    n.sonsLen == 3 and
+    n[1].typ != nil and
     n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
 
-  let t1 = n[1].typ.skipTypes({tyTypeDesc})
+  var
+    res = false
+    t1 = n[1].typ
+    t2 = n[2].typ
+
+  if t1.kind == tyTypeDesc and t2.kind != tyTypeDesc:
+    t1 = t1.base
 
   if n[2].kind in {nkStrLit..nkTripleStrLit}:
     case n[2].strVal.normalize
     of "closure":
       let t = skipTypes(t1, abstractRange)
-      result = newIntNode(nkIntLit, ord(t.kind == tyProc and
-                                        t.callConv == ccClosure and
-                                        tfIterator notin t.flags))
+      res = t.kind == tyProc and
+            t.callConv == ccClosure and
+            tfIterator notin t.flags
     else:
-      result = newIntNode(nkIntLit, 0)
+      res = false
   else:
-    var rhsOrigType = n[2].typ
-    var t2 = rhsOrigType.skipTypes({tyTypeDesc})
     maybeLiftType(t2, c, n.info)
     var m: TCandidate
     initCandidate(c, m, t2)
     if efExplain in flags:
       m.diagnostics = @[]
       m.diagnosticsEnabled = true
-    let match = typeRel(m, t2, t1) >= isSubtype # isNone
-    result = newIntNode(nkIntLit, ord(match))
+    res = typeRel(m, t2, t1) >= isSubtype # isNone
 
+  result = newIntNode(nkIntLit, ord(res))
   result.typ = n.typ
 
 proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
   if sonsLen(n) != 3:
     localError(c.config, n.info, "'is' operator takes 2 arguments")
 
+  let boolType = getSysType(c.graph, n.info, tyBool)
   result = n
-  n.typ = getSysType(c.graph, n.info, tyBool)
+  n.typ = boolType
+  var liftLhs = true
 
   n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
   if n[2].kind notin {nkStrLit..nkTripleStrLit}:
     let t2 = semTypeNode(c, n[2], nil)
     n.sons[2] = newNodeIT(nkType, n[2].info, t2)
+    if t2.kind == tyStatic:
+      let evaluated = tryConstExpr(c, n[1])
+      if evaluated != nil:
+        c.fixupStaticType(evaluated)
+        n[1] = evaluated
+      else:
+        result = newIntNode(nkIntLit, 0)
+        result.typ = boolType
+        return
+    elif t2.kind == tyTypeDesc and
+        (t2.base.kind == tyNone or tfExplicit in t2.flags):
+      # When the right-hand side is an explicit type, we must
+      # not allow regular values to be matched against the type:
+      liftLhs = false
 
-  let lhsType = n[1].typ
+  var lhsType = n[1].typ
   if lhsType.kind != tyTypeDesc:
-    n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info)
-  elif lhsType.base.kind == tyNone:
-    # this is a typedesc variable, leave for evals
-    return
+    if liftLhs:
+      n[1] = makeTypeSymNode(c, lhsType, n[1].info)
+      lhsType = n[1].typ
+  else:
+    if lhsType.base.kind == tyNone:
+      # this is a typedesc variable, leave for evals
+      return
+    if lhsType.base.containsGenericType:
+      # BUGFIX: don't evaluate this too early: ``T is void``
+      return
 
-  # BUGFIX: don't evaluate this too early: ``T is void``
-  if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n, flags)
+  result = isOpImpl(c, n, flags)
 
 proc semOpAux(c: PContext, n: PNode) =
   const flags = {efDetermineType}
@@ -353,7 +415,7 @@ proc semOpAux(c: PContext, n: PNode) =
     var a = n.sons[i]
     if a.kind == nkExprEqExpr and sonsLen(a) == 2:
       let info = a.sons[0].info
-      a.sons[0] = newIdentNode(considerQuotedIdent(c.config, a.sons[0], a), info)
+      a.sons[0] = newIdentNode(considerQuotedIdent(c, a.sons[0], a), info)
       a.sons[1] = semExprWithType(c, a.sons[1], flags)
       a.typ = a.sons[1].typ
     else:
@@ -361,7 +423,7 @@ proc semOpAux(c: PContext, n: PNode) =
 
 proc overloadedCallOpr(c: PContext, n: PNode): PNode =
   # quick check if there is *any* () operator overloaded:
-  var par = getIdent("()")
+  var par = getIdent(c.cache, "()")
   if searchInScopes(c, par) == nil:
     result = nil
   else:
@@ -407,7 +469,7 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
   of nkCharLit..nkUInt64Lit:
     if check and n.kind != nkUInt64Lit:
       let value = n.intVal
-      if value < firstOrd(newType) or value > lastOrd(newType):
+      if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
         localError(c.config, n.info, "cannot convert " & $value &
                                          " to " & typeToString(newType))
   else: discard
@@ -480,6 +542,36 @@ proc fixAbstractType(c: PContext, n: PNode) =
 proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
   result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr)
 
+proc isUnresolvedSym(s: PSym): bool =
+  return s.kind == skGenericParam or
+         tfInferrableStatic in s.typ.flags or
+         (s.kind == skParam and s.typ.isMetaType) or
+         (s.kind == skType and
+          s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
+
+proc hasUnresolvedArgs(c: PContext, n: PNode): bool =
+  # Checks whether an expression depends on generic parameters that
+  # don't have bound values yet. E.g. this could happen in situations
+  # such as:
+  #  type Slot[T] = array[T.size, byte]
+  #  proc foo[T](x: default(T))
+  #
+  # Both static parameter and type parameters can be unresolved.
+  case n.kind
+  of nkSym:
+    return isUnresolvedSym(n.sym)
+  of nkIdent, nkAccQuoted:
+    let ident = considerQuotedIdent(c, n)
+    let sym = searchInScopes(c, ident)
+    if sym != nil:
+      return isUnresolvedSym(sym)
+    else:
+      return false
+  else:
+    for i in 0..<n.safeLen:
+      if hasUnresolvedArgs(c, n.sons[i]): return true
+    return false
+
 proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
   if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or
                                       sfCompileToCpp in c.module.flags):
@@ -617,24 +709,24 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
       call.add(a)
     #echo "NOW evaluating at compile time: ", call.renderTree
     if sfCompileTime in callee.flags:
-      result = evalStaticExpr(c.module, c.cache, c.graph, call, c.p.owner)
+      result = evalStaticExpr(c.module, c.graph, call, c.p.owner)
       if result.isNil:
         localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call))
       else: result = fixupTypeAfterEval(c, result, n)
     else:
-      result = evalConstExpr(c.module, c.cache, c.graph, call)
+      result = evalConstExpr(c.module, c.graph, call)
       if result.isNil: result = n
       else: result = fixupTypeAfterEval(c, result, n)
     #if result != n:
     #  echo "SUCCESS evaluated at compile time: ", call.renderTree
 
 proc semStaticExpr(c: PContext, n: PNode): PNode =
-  let a = semExpr(c, n.sons[0])
+  let a = semExpr(c, n)
   if a.findUnresolvedStatic != nil: return a
-  result = evalStaticExpr(c.module, c.cache, c.graph, a, c.p.owner)
+  result = evalStaticExpr(c.module, c.graph, a, c.p.owner)
   if result.isNil:
     localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n))
-    result = emptyNode
+    result = c.graph.emptyNode
   else:
     result = fixupTypeAfterEval(c, result, a)
 
@@ -742,10 +834,10 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
     # This is a proc variable, apply normal overload resolution
     let m = resolveIndirectCall(c, n, nOrig, t)
     if m.state != csMatch:
-      if errorOutputs == {}:
+      if c.config.m.errorOutputs == {}:
         # speed up error generation:
         globalError(c.config, n.info, "type mismatch")
-        return emptyNode
+        return c.graph.emptyNode
       else:
         var hasErrorType = false
         var msg = "type mismatch: got <"
@@ -765,6 +857,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
     else:
       result = m.call
       instGenericConvertersSons(c, result, m)
+
   elif t != nil and t.kind == tyTypeDesc:
     if n.len == 1: return semObjConstr(c, n, flags)
     return semConv(c, n)
@@ -802,7 +895,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
 proc buildEchoStmt(c: PContext, n: PNode): PNode =
   # we MUST not check 'n' for semantics again here! But for now we give up:
   result = newNodeI(nkCall, n.info)
-  var e = strTableGet(c.graph.systemModule.tab, getIdent"echo")
+  var e = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "echo"))
   if e != nil:
     add(result, newSymNode(e))
   else:
@@ -866,7 +959,7 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
         else:
           if check == nil:
             check = newNodeI(nkCheckedFieldExpr, n.info)
-            addSon(check, ast.emptyNode) # make space for access node
+            addSon(check, c.graph.emptyNode) # make space for access node
           s = newNodeIT(nkCurly, n.info, setType)
           for j in countup(0, sonsLen(it) - 2): addSon(s, copyTree(it.sons[j]))
           var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
@@ -881,7 +974,7 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
         if result != nil:
           if check == nil:
             check = newNodeI(nkCheckedFieldExpr, n.info)
-            addSon(check, ast.emptyNode) # make space for access node
+            addSon(check, c.graph.emptyNode) # make space for access node
           var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool))
           addSon(inExpr, newSymNode(c.graph.opContains, n.info))
           addSon(inExpr, s)
@@ -938,7 +1031,7 @@ proc readTypeParameter(c: PContext, typ: PType,
           if rawTyp.n != nil:
             return rawTyp.n
           else:
-            return emptyNode
+            return c.graph.emptyNode
         else:
           let foundTyp = makeTypeDesc(c, rawTyp)
           return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info)
@@ -1031,7 +1124,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
   of skType:
     markUsed(c.config, n.info, s, c.graph.usageSym)
     styleCheckUse(n.info, s)
-    if s.typ.kind == tyStatic and s.typ.n != nil:
+    if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil:
       return s.typ.n
     result = newSymNode(s, n.info)
     result.typ = makeTypeDesc(c, s.typ)
@@ -1082,7 +1175,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   when defined(nimsuggest):
     if c.config.cmd == cmdIdeTools:
       suggestExpr(c, n)
-      if exactEquals(gTrackPos, n[1].info): suggestExprNoCheck(c, n)
+      if exactEquals(c.config.m.trackPos, n[1].info): suggestExprNoCheck(c, n)
 
   var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule})
   if s != nil:
@@ -1097,7 +1190,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
 
   n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType})
   #restoreOldStyleType(n.sons[0])
-  var i = considerQuotedIdent(c.config, n.sons[1], n)
+  var i = considerQuotedIdent(c, n.sons[1], n)
   var ty = n.sons[0].typ
   var f: PSym = nil
   result = nil
@@ -1106,7 +1199,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
     case t.kind
     of tyTypeParamsHolders:
       result = readTypeParameter(c, t, i, n.info)
-      if result == emptyNode:
+      if result == c.graph.emptyNode:
         result = n
         n.typ = makeTypeFromExpr(c, n.copyTree)
       return
@@ -1217,7 +1310,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
     addSon(result, n.sons[1])
     addSon(result, copyTree(n[0]))
   else:
-    var i = considerQuotedIdent(c.config, n.sons[1], n)
+    var i = considerQuotedIdent(c, n.sons[1], n)
     result = newNodeI(nkDotCall, n.info)
     result.flags.incl nfDotField
     addSon(result, newIdentNode(i, n[1].info))
@@ -1258,8 +1351,18 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
   # make sure we don't evaluate generic macros/templates
   n.sons[0] = semExprWithType(c, n.sons[0],
                               {efNoEvaluateGeneric})
-  let arr = skipTypes(n.sons[0].typ, {tyGenericInst,
+  var arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyUserTypeClassInst,
                                       tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
+  if arr.kind == tyStatic:
+    if arr.base.kind == tyNone:
+      result = n
+      result.typ = semStaticType(c, n[1], nil)
+      return
+    elif arr.n != nil:
+      return semSubscript(c, arr.n, flags)
+    else:
+      arr = arr.base
+
   case arr.kind
   of tyArray, tyOpenArray, tyVarargs, tySequence, tyString,
      tyCString:
@@ -1328,11 +1431,11 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   result = semSubscript(c, n, flags)
   if result == nil:
     # overloaded [] operator:
-    result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]"))
+    result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "[]")))
 
 proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
-  var id = considerQuotedIdent(c.config, a[1], a)
-  var setterId = newIdentNode(getIdent(id.s & '='), n.info)
+  var id = considerQuotedIdent(c, a[1], a)
+  var setterId = newIdentNode(getIdent(c.cache, id.s & '='), n.info)
   # a[0] is already checked for semantics, that does ``builtinFieldAccess``
   # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
   # nodes?
@@ -1409,7 +1512,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
     # --> `[]=`(a, i, x)
     a = semSubscript(c, a, {efLValue})
     if a == nil:
-      result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=")
+      result = buildOverloadedSubscripts(n.sons[0], getIdent(c.cache, "[]="))
       add(result, n[1])
       if mode == noOverloadedSubscript:
         bracketNotFoundError(c, result)
@@ -1419,7 +1522,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
         return result
   of nkCurlyExpr:
     # a{i} = x -->  `{}=`(a, i, x)
-    result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=")
+    result = buildOverloadedSubscripts(n.sons[0], getIdent(c.cache, "{}="))
     add(result, n[1])
     return semExprNoType(c, result)
   of nkPar, nkTupleConstr:
@@ -1427,7 +1530,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
       # unfortunately we need to rewrite ``(x, y) = foo()`` already here so
       # that overloading of the assignment operator still works. Usually we
       # prefer to do these rewritings in transf.nim:
-      return semStmt(c, lowerTupleUnpackingForAsgn(n, c.p.owner))
+      return semStmt(c, lowerTupleUnpackingForAsgn(c.graph, n, c.p.owner))
     else:
       a = semExprWithType(c, a, {efLValue})
   else:
@@ -1450,7 +1553,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
       rhs = semExprWithType(c, n.sons[1],
         if lhsIsResult: {efAllowDestructor} else: {})
     if lhsIsResult:
-      n.typ = enforceVoidContext
+      n.typ = c.enforceVoidContext
       if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ):
         var rhsTyp = rhs.typ
         if rhsTyp.kind in tyUserTypeClasses and rhsTyp.isResolvedUserTypeClass:
@@ -1489,7 +1592,7 @@ proc semReturn(c: PContext, n: PNode): PNode =
         n.sons[0] = semAsgn(c, a)
         # optimize away ``result = result``:
         if n[0][1].kind == nkSym and n[0][1].sym == c.p.resultSym:
-          n.sons[0] = ast.emptyNode
+          n.sons[0] = c.graph.emptyNode
       else:
         localError(c.config, n.info, errNoReturnTypeDeclared)
   else:
@@ -1556,8 +1659,6 @@ proc semYield(c: PContext, n: PNode): PNode =
   checkSonsLen(n, 1, c.config)
   if c.p.owner == nil or c.p.owner.kind != skIterator:
     localError(c.config, n.info, errYieldNotAllowedHere)
-  elif oldIterTransf in c.features and c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline:
-    localError(c.config, n.info, errYieldNotAllowedInTryStmt)
   elif n.sons[0].kind != nkEmpty:
     n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility:
     var iterType = c.p.owner.typ
@@ -1593,13 +1694,13 @@ proc lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
     checkSonsLen(n, 2, c.config)
     var m = lookUpForDefined(c, n.sons[0], onlyCurrentScope)
     if m != nil and m.kind == skModule:
-      let ident = considerQuotedIdent(c.config, n[1], n)
+      let ident = considerQuotedIdent(c, n[1], n)
       if m == c.module:
         result = strTableGet(c.topLevelScope.symbols, ident)
       else:
         result = strTableGet(m.tab, ident)
   of nkAccQuoted:
-    result = lookUpForDefined(c, considerQuotedIdent(c.config, n), onlyCurrentScope)
+    result = lookUpForDefined(c, considerQuotedIdent(c, n), onlyCurrentScope)
   of nkSym:
     result = n.sym
   of nkOpenSymChoice, nkClosedSymChoice:
@@ -1612,11 +1713,9 @@ proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode =
   checkSonsLen(n, 2, c.config)
   # we replace this node by a 'true' or 'false' node:
   result = newIntNode(nkIntLit, 0)
-  if not onlyCurrentScope and considerQuotedIdent(c.config, n[0], n).s == "defined":
-    if n.sons[1].kind != nkIdent:
-      localError(c.config, n.info, "obsolete usage of 'defined', use 'declared' instead")
-    elif isDefined(c.config, n.sons[1].ident.s):
-      result.intVal = 1
+  if not onlyCurrentScope and considerQuotedIdent(c, n[0], n).s == "defined":
+    let d = considerQuotedIdent(c, n[1], n)
+    result.intVal = ord isDefined(c.config, d.s)
   elif lookUpForDefined(c, n.sons[1], onlyCurrentScope) != nil:
     result.intVal = 1
   result.info = n.info
@@ -1711,7 +1810,7 @@ proc processQuotations(c: PContext; n: var PNode, op: string,
                        ids: var seq[PNode]) =
   template returnQuote(q) =
     quotes.add q
-    n = newIdentNode(getIdent($quotes.len), n.info)
+    n = newIdentNode(getIdent(c.cache, $quotes.len), n.info)
     ids.add n
     return
 
@@ -1722,7 +1821,7 @@ proc processQuotations(c: PContext; n: var PNode, op: string,
       if examinedOp == op:
         returnQuote n[1]
       elif examinedOp.startsWith(op):
-        n.sons[0] = newIdentNode(getIdent(examinedOp.substr(op.len)), n.info)
+        n.sons[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), n.info)
   elif n.kind == nkAccQuoted and op == "``":
     returnQuote n[0]
 
@@ -1748,14 +1847,17 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
   processQuotations(c, quotedBlock, op, quotes, ids)
 
   var dummyTemplate = newProcNode(
-    nkTemplateDef, quotedBlock.info, quotedBlock,
-    name = newAnonSym(c, skTemplate, n.info).newSymNode)
+    nkTemplateDef, quotedBlock.info, body = quotedBlock,
+    params = c.graph.emptyNode,
+    name = newAnonSym(c, skTemplate, n.info).newSymNode,
+              pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode,
+              pragmas = c.graph.emptyNode, exceptions = c.graph.emptyNode)
 
   if ids.len > 0:
     dummyTemplate.sons[paramsPos] = newNodeI(nkFormalParams, n.info)
     dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "typed").newSymNode # return type
     ids.add getSysSym(c.graph, n.info, "untyped").newSymNode # params type
-    ids.add emptyNode # no default value
+    ids.add c.graph.emptyNode # no default value
     dummyTemplate[paramsPos].add newNode(nkIdentDefs, n.info, ids)
 
   var tmpl = semTemplateDef(c, dummyTemplate)
@@ -1780,9 +1882,9 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   openScope(c)
   let oldOwnerLen = len(c.graph.owners)
   let oldGenerics = c.generics
-  let oldErrorOutputs = errorOutputs
-  if efExplain notin flags: errorOutputs = {}
-  let oldContextLen = msgs.getInfoContextLen()
+  let oldErrorOutputs = c.config.m.errorOutputs
+  if efExplain notin flags: c.config.m.errorOutputs = {}
+  let oldContextLen = msgs.getInfoContextLen(c.config)
 
   let oldInGenericContext = c.inGenericContext
   let oldInUnrolledContext = c.inUnrolledContext
@@ -1804,10 +1906,10 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   c.inGenericInst = oldInGenericInst
   c.inStaticContext = oldInStaticContext
   c.p = oldProcCon
-  msgs.setInfoContextLen(oldContextLen)
+  msgs.setInfoContextLen(c.config, oldContextLen)
   setLen(c.graph.owners, oldOwnerLen)
   c.currentScope = oldScope
-  errorOutputs = oldErrorOutputs
+  c.config.m.errorOutputs = oldErrorOutputs
   c.config.errorCounter = oldErrorCount
   c.config.errorMax = oldErrorMax
 
@@ -1913,7 +2015,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
         result.typ = typ
       result.add instantiateCreateFlowVarCall(c, typ, n.info).newSymNode
     else:
-      result.add emptyNode
+      result.add c.graph.emptyNode
   of mProcCall:
     result = setMs(n, s)
     result.sons[1] = semExpr(c, n.sons[1])
@@ -1937,17 +2039,17 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   of mRunnableExamples:
     if c.config.cmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList:
       if sfMainModule in c.module.flags:
-        let inp = toFullPath(c.module.info)
+        let inp = toFullPath(c.config, c.module.info)
         if c.runnableExamples == nil:
           c.runnableExamples = newTree(nkStmtList,
             newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
         let imports = newTree(nkStmtList)
         extractImports(n.lastSon, imports)
         for imp in imports: c.runnableExamples.add imp
-        c.runnableExamples.add newTree(nkBlockStmt, emptyNode, copyTree n.lastSon)
+        c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree n.lastSon)
       result = setMs(n, s)
     else:
-      result = emptyNode
+      result = c.graph.emptyNode
   else:
     result = semDirectOp(c, n, flags)
 
@@ -2040,7 +2142,7 @@ proc semSetConstr(c: PContext, n: PNode): PNode =
     if not isOrdinalType(typ):
       localError(c.config, n.info, errOrdinalTypeExpected)
       typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
-    elif lengthOrd(typ) > MaxSetElements:
+    elif lengthOrd(c.config, typ) > MaxSetElements:
       typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
     addSonSkipIntLit(result.typ, typ)
     for i in countup(0, sonsLen(n) - 1):
@@ -2159,7 +2261,7 @@ proc semBlock(c: PContext, n: PNode): PNode =
       addDecl(c, labl)
     n.sons[0] = newSymNode(labl, n.sons[0].info)
     suggestSym(c.config, n.sons[0].info, labl, c.graph.usageSym)
-    styleCheckDef(labl)
+    styleCheckDef(c.config, labl)
   n.sons[1] = semExpr(c, n.sons[1])
   n.typ = n.sons[1].typ
   if isEmptyType(n.typ): n.kind = nkBlockStmt
@@ -2306,7 +2408,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     # check if it is an expression macro:
     checkMinSonsLen(n, 1, c.config)
     #when defined(nimsuggest):
-    #  if gIdeCmd == ideCon and gTrackPos == n.info: suggestExprNoCheck(c, n)
+    #  if gIdeCmd == ideCon and c.config.m.trackPos == n.info: suggestExprNoCheck(c, n)
     let mode = if nfDotField in n.flags: {} else: {checkUndeclared}
     var s = qualifiedLookUp(c, n.sons[0], mode)
     if s != nil:
@@ -2363,12 +2465,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     checkMinSonsLen(n, 1, c.config)
     result = semArrayAccess(c, n, flags)
   of nkCurlyExpr:
-    result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags)
+    result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "{}")), flags)
   of nkPragmaExpr:
     var
       expr = n[0]
       pragma = n[1]
-      pragmaName = considerQuotedIdent(c.config, pragma[0])
+      pragmaName = considerQuotedIdent(c, pragma[0])
       flags = flags
 
     case whichKeyword(pragmaName)
@@ -2424,8 +2526,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     # handling of sym choices is context dependent
     # the node is left intact for now
     discard
-  of nkStaticExpr:
-    result = semStaticExpr(c, n)
+  of nkStaticExpr: result = semStaticExpr(c, n[0])
   of nkAsgn: result = semAsgn(c, n)
   of nkBlockStmt, nkBlockExpr: result = semBlock(c, n)
   of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags)
diff --git a/compiler/semfields.nim b/compiler/semfields.nim
index 16d79c559..869f5ae74 100644
--- a/compiler/semfields.nim
+++ b/compiler/semfields.nim
@@ -23,10 +23,10 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
   of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n
   of nkIdent, nkSym:
     result = n
-    let ident = considerQuotedIdent(c.c.config, n)
+    let ident = considerQuotedIdent(c.c, n)
     var L = sonsLen(forLoop)
     if c.replaceByFieldName:
-      if ident.id == considerQuotedIdent(c.c.config, forLoop[0]).id:
+      if ident.id == considerQuotedIdent(c.c, forLoop[0]).id:
         let fieldName = if c.tupleType.isNil: c.field.name.s
                         elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
                         else: c.tupleType.n.sons[c.tupleIndex].sym.name.s
@@ -34,7 +34,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
         return
     # other fields:
     for i in ord(c.replaceByFieldName)..L-3:
-      if ident.id == considerQuotedIdent(c.c.config, forLoop[i]).id:
+      if ident.id == considerQuotedIdent(c.c, forLoop[i]).id:
         var call = forLoop.sons[L-2]
         var tupl = call.sons[i+1-ord(c.replaceByFieldName)]
         if c.field.isNil:
@@ -107,10 +107,10 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
   # so that 'break' etc. work as expected, we produce
   # a 'while true: stmt; break' loop ...
   result = newNodeI(nkWhileStmt, n.info, 2)
-  var trueSymbol = strTableGet(c.graph.systemModule.tab, getIdent"true")
+  var trueSymbol = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "true"))
   if trueSymbol == nil:
     localError(c.config, n.info, "system needs: 'true'")
-    trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(c), n.info)
+    trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), getCurrOwner(c), n.info)
     trueSymbol.typ = getSysType(c.graph, n.info, tyBool)
 
   result.sons[0] = newSymNode(trueSymbol, n.info)
@@ -162,7 +162,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
   # we avoid it now if we can:
   if containsNode(stmts, {nkBreakStmt}):
     var b = newNodeI(nkBreakStmt, n.info)
-    b.add(ast.emptyNode)
+    b.add(newNodeI(nkEmpty, n.info))
     stmts.add(b)
   else:
     result = stmts
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index daf9ce983..2f495bc7f 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -13,7 +13,7 @@
 import
   strutils, options, ast, astalgo, trees, treetab, nimsets, times,
   nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
-  commands, magicsys, modulegraphs, strtabs
+  commands, magicsys, modulegraphs, strtabs, lineinfos
 
 proc newIntNodeT*(intVal: BiggestInt, n: PNode; g: ModuleGraph): PNode =
   case skipTypes(n.typ, abstractVarRange).kind
@@ -53,24 +53,24 @@ proc getConstExpr*(m: PSym, n: PNode; g: ModuleGraph): PNode
   # expression
 proc evalOp*(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode
 
-proc checkInRange(n: PNode, res: BiggestInt): bool =
-  if res in firstOrd(n.typ)..lastOrd(n.typ):
+proc checkInRange(conf: ConfigRef; n: PNode, res: BiggestInt): bool =
+  if res in firstOrd(conf, n.typ)..lastOrd(conf, n.typ):
     result = true
 
 proc foldAdd(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
   let res = a +% b
   if ((res xor a) >= 0'i64 or (res xor b) >= 0'i64) and
-      checkInRange(n, res):
+      checkInRange(g.config, n, res):
     result = newIntNodeT(res, n, g)
 
 proc foldSub*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
   let res = a -% b
   if ((res xor a) >= 0'i64 or (res xor not b) >= 0'i64) and
-      checkInRange(n, res):
+      checkInRange(g.config, n, res):
     result = newIntNodeT(res, n, g)
 
 proc foldAbs*(a: BiggestInt, n: PNode; g: ModuleGraph): PNode =
-  if a != firstOrd(n.typ):
+  if a != firstOrd(g.config, n.typ):
     result = newIntNodeT(a, n, g)
 
 proc foldMod*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
@@ -82,7 +82,7 @@ proc foldModU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
     result = newIntNodeT(a %% b, n, g)
 
 proc foldDiv*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
-  if b != 0'i64 and (a != firstOrd(n.typ) or b != -1'i64):
+  if b != 0'i64 and (a != firstOrd(g.config, n.typ) or b != -1'i64):
     result = newIntNodeT(a div b, n, g)
 
 proc foldDivU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
@@ -96,7 +96,7 @@ proc foldMul*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
 
   # Fast path for normal case: small multiplicands, and no info
   # is lost in either method.
-  if resAsFloat == floatProd and checkInRange(n, res):
+  if resAsFloat == floatProd and checkInRange(g.config, n, res):
     return newIntNodeT(res, n, g)
 
   # Somebody somewhere lost info. Close enough, or way off? Note
@@ -107,7 +107,7 @@ proc foldMul*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
   # abs(diff)/abs(prod) <= 1/32 iff
   #   32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
   if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd) and
-      checkInRange(n, res):
+      checkInRange(g.config, n, res):
     return newIntNodeT(res, n, g)
 
 proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
@@ -210,9 +210,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
   of mUnaryMinusI, mUnaryMinusI64: result = newIntNodeT(- getInt(a), n, g)
   of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n, g)
   of mNot: result = newIntNodeT(1 - getInt(a), n, g)
-  of mCard: result = newIntNodeT(nimsets.cardSet(a), n, g)
-  of mBitnotI: result = newIntNodeT(not getInt(a), n, g)
-  of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n, g)
+  of mCard: result = newIntNodeT(nimsets.cardSet(g.config, a), n, g)
+  of mBitnotI:
+    case skipTypes(n.typ, abstractRange).kind
+    of tyUInt..tyUInt64:
+      result = newIntNodeT((not getInt(a)) and lastOrd(g.config, a.typ, fixedUnsigned=true), n, g)
+    else:
+      result = newIntNodeT(not getInt(a), n, g)
+  of mLengthArray: result = newIntNodeT(lengthOrd(g.config, a.typ), n, g)
   of mLengthSeq, mLengthOpenArray, mXLenSeq, mLengthStr, mXLenStr:
     if a.kind == nkNilLit:
       result = newIntNodeT(0, n, g)
@@ -229,7 +234,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
   of mAbsI: result = foldAbs(getInt(a), n, g)
   of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
     # byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64
-    result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n, g)
+    result = newIntNodeT(getInt(a) and (`shl`(1, getSize(g.config, a.typ) * 8) - 1), n, g)
   of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n, g)
   of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n, g)
   of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n, g)
@@ -250,8 +255,10 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
     of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n, g)
     of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n, g)
     of tyInt32: result = newIntNodeT(int32(getInt(a)) shl int32(getInt(b)), n, g)
-    of tyInt64, tyInt, tyUInt..tyUInt64:
+    of tyInt64, tyInt:
       result = newIntNodeT(`shl`(getInt(a), getInt(b)), n, g)
+    of tyUInt..tyUInt64:
+      result = newIntNodeT(`shl`(getInt(a), getInt(b)) and lastOrd(g.config, a.typ, fixedUnsigned=true), n, g)
     else: internalError(g.config, n.info, "constant folding for shl")
   of mShrI:
     case skipTypes(n.typ, abstractRange).kind
@@ -304,21 +311,21 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
   of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n, g)
   of mModU: result = foldModU(getInt(a), getInt(b), n, g)
   of mDivU: result = foldDivU(getInt(a), getInt(b), n, g)
-  of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n, g)
-  of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n, g)
+  of mLeSet: result = newIntNodeT(ord(containsSets(g.config, a, b)), n, g)
+  of mEqSet: result = newIntNodeT(ord(equalSets(g.config, a, b)), n, g)
   of mLtSet:
-    result = newIntNodeT(ord(containsSets(a, b) and not equalSets(a, b)), n, g)
+    result = newIntNodeT(ord(containsSets(g.config, a, b) and not equalSets(g.config, a, b)), n, g)
   of mMulSet:
-    result = nimsets.intersectSets(a, b)
+    result = nimsets.intersectSets(g.config, a, b)
     result.info = n.info
   of mPlusSet:
-    result = nimsets.unionSets(a, b)
+    result = nimsets.unionSets(g.config, a, b)
     result.info = n.info
   of mMinusSet:
-    result = nimsets.diffSets(a, b)
+    result = nimsets.diffSets(g.config, a, b)
     result.info = n.info
   of mSymDiffSet:
-    result = nimsets.symdiffSets(a, b)
+    result = nimsets.symdiffSets(g.config, a, b)
     result.info = n.info
   of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n, g)
   of mInSet: result = newIntNodeT(ord(inSet(a, b)), n, g)
@@ -415,9 +422,9 @@ proc getAppType(n: PNode; g: ModuleGraph): PNode =
 proc rangeCheck(n: PNode, value: BiggestInt; g: ModuleGraph) =
   var err = false
   if n.typ.skipTypes({tyRange}).kind in {tyUInt..tyUInt64}:
-    err = value <% firstOrd(n.typ) or value >% lastOrd(n.typ, fixedUnsigned=true)
+    err = value <% firstOrd(g.config, n.typ) or value >% lastOrd(g.config, n.typ, fixedUnsigned=true)
   else:
-    err = value < firstOrd(n.typ) or value > lastOrd(n.typ)
+    err = value < firstOrd(g.config, n.typ) or value > lastOrd(g.config, n.typ)
   if err:
     localError(g.config, n.info, "cannot convert " & $value &
                                      " to " & typeToString(n.typ))
@@ -472,7 +479,7 @@ proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
     else:
       localError(g.config, n.info, "index out of bounds: " & $n)
   of nkBracket:
-    idx = idx - x.typ.firstOrd
+    idx = idx - firstOrd(g.config, x.typ)
     if idx >= 0 and idx < x.len: result = x.sons[int(idx)]
     else: localError(g.config, n.info, "index out of bounds: " & $n)
   of nkStrLit..nkTripleStrLit:
@@ -547,11 +554,11 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
                                                    "yyyy-MM-dd"), n, g)
       of mCompileTime: result = newStrNodeT(format(getSrcTimestamp(),
                                                    "HH:mm:ss"), n, g)
-      of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n, g)
-      of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n, g)
-      of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n, g)
-      of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[platform.hostOS].name), n, g)
-      of mBuildCPU: result = newStrNodeT(platform.CPU[platform.hostCPU].name.toLowerAscii, n, g)
+      of mCpuEndian: result = newIntNodeT(ord(CPU[g.config.target.targetCPU].endian), n, g)
+      of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[g.config.target.targetOS].name), n, g)
+      of mHostCPU: result = newStrNodeT(platform.CPU[g.config.target.targetCPU].name.toLowerAscii, n, g)
+      of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[g.config.target.hostOS].name), n, g)
+      of mBuildCPU: result = newStrNodeT(platform.CPU[g.config.target.hostCPU].name.toLowerAscii, n, g)
       of mAppType: result = getAppType(n, g)
       of mNaN: result = newFloatNodeT(NaN, n, g)
       of mInf: result = newFloatNodeT(Inf, n, g)
@@ -599,22 +606,22 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
         return
       of mSizeOf:
         var a = n.sons[1]
-        if computeSize(a.typ) < 0:
+        if computeSize(g.config, a.typ) < 0:
           localError(g.config, a.info, "cannot evaluate 'sizeof' because its type is not defined completely")
           result = nil
         elif skipTypes(a.typ, typedescInst+{tyRange}).kind in
              IntegralTypes+NilableTypes+{tySet}:
           #{tyArray,tyObject,tyTuple}:
-          result = newIntNodeT(getSize(a.typ), n, g)
+          result = newIntNodeT(getSize(g.config, a.typ), n, g)
         else:
           result = nil
           # XXX: size computation for complex types is still wrong
       of mLow:
-        result = newIntNodeT(firstOrd(n.sons[1].typ), n, g)
+        result = newIntNodeT(firstOrd(g.config, n.sons[1].typ), n, g)
       of mHigh:
-        if skipTypes(n.sons[1].typ, abstractVar).kind notin
+        if skipTypes(n.sons[1].typ, abstractVar+{tyUserTypeClassInst}).kind notin
             {tySequence, tyString, tyCString, tyOpenArray, tyVarargs}:
-          result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n, g)
+          result = newIntNodeT(lastOrd(g.config, skipTypes(n[1].typ, abstractVar)), n, g)
         else:
           var a = getArrayConstr(m, n.sons[1], g)
           if a.kind == nkBracket:
@@ -630,7 +637,7 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
       of mLengthArray:
         # It doesn't matter if the argument is const or not for mLengthArray.
         # This fixes bug #544.
-        result = newIntNodeT(lengthOrd(n.sons[1].typ), n, g)
+        result = newIntNodeT(lengthOrd(g.config, n.sons[1].typ), n, g)
       of mAstToStr:
         result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, g)
       of mConStrStr:
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index 8f06e748e..0218096d6 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -32,9 +32,12 @@ type
     cursorInBody: bool # only for nimsuggest
     bracketExpr: PNode
 
-type
   TSemGenericFlag = enum
-    withinBind, withinTypeDesc, withinMixin, withinConcept
+    withinBind,
+    withinTypeDesc,
+    withinMixin,
+    withinConcept
+
   TSemGenericFlags = set[TSemGenericFlag]
 
 proc semGenericStmt(c: PContext, n: PNode,
@@ -53,9 +56,16 @@ template macroToExpand(s): untyped =
 template macroToExpandSym(s): untyped =
   s.kind in {skMacro, skTemplate} and (s.typ.len == 1) and not fromDotExpr
 
+template isMixedIn(sym): bool =
+  let s = sym
+  s.name.id in ctx.toMixin or (withinConcept in flags and
+                               s.magic == mNone and
+                               s.kind in OverloadableSyms)
+
 proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
-                          ctx: var GenericCtx; fromDotExpr=false): PNode =
-  semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
+                          ctx: var GenericCtx; flags: TSemGenericFlags,
+                          fromDotExpr=false): PNode =
+  semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
   incl(s.flags, sfUsed)
   case s.kind
   of skUnknown:
@@ -103,7 +113,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
 proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
             ctx: var GenericCtx): PNode =
   result = n
-  let ident = considerQuotedIdent(c.config, n)
+  let ident = considerQuotedIdent(c, n)
   var s = searchInScopes(c, ident).skipAlias(n, c.config)
   if s == nil:
     s = strTableGet(c.pureEnumFields, ident)
@@ -115,10 +125,10 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
   else:
     if withinBind in flags:
       result = symChoice(c, n, s, scClosed)
-    elif s.name.id in ctx.toMixin:
+    elif s.isMixedIn:
       result = symChoice(c, n, s, scForceOpen)
     else:
-      result = semGenericStmtSymbol(c, n, s, ctx)
+      result = semGenericStmtSymbol(c, n, s, ctx, flags)
   # else: leave as nkIdent
 
 proc newDot(n, b: PNode): PNode =
@@ -129,27 +139,27 @@ proc newDot(n, b: PNode): PNode =
 proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
                  ctx: var GenericCtx; isMacro: var bool): PNode =
   assert n.kind == nkDotExpr
-  semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
+  semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
 
   let luf = if withinMixin notin flags: {checkUndeclared, checkModule} else: {checkModule}
 
   var s = qualifiedLookUp(c, n, luf)
   if s != nil:
-    result = semGenericStmtSymbol(c, n, s, ctx)
+    result = semGenericStmtSymbol(c, n, s, ctx, flags)
   else:
     n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
     result = n
     let n = n[1]
-    let ident = considerQuotedIdent(c.config, n)
+    let ident = considerQuotedIdent(c, n)
     var s = searchInScopes(c, ident).skipAlias(n, c.config)
     if s != nil and s.kind in routineKinds:
       isMacro = s.kind in {skTemplate, skMacro}
       if withinBind in flags:
         result = newDot(result, symChoice(c, n, s, scClosed))
-      elif s.name.id in ctx.toMixin:
+      elif s.isMixedIn:
         result = newDot(result, symChoice(c, n, s, scForceOpen))
       else:
-        let syms = semGenericStmtSymbol(c, n, s, ctx, fromDotExpr=true)
+        let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)
         if syms.kind == nkSym:
           let choice = symChoice(c, n, s, scForceOpen)
           choice.kind = nkClosedSymChoice
@@ -160,7 +170,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
 proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
   let s = newSymS(skUnknown, getIdentNode(c, n), c)
   addPrelimDecl(c, s)
-  styleCheckDef(n.info, s, kind)
+  styleCheckDef(c.config, n.info, s, kind)
 
 proc semGenericStmt(c: PContext, n: PNode,
                     flags: TSemGenericFlags, ctx: var GenericCtx): PNode =
@@ -170,7 +180,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     if withinTypeDesc in flags: inc c.inTypeContext
 
   #if conf.cmd == cmdIdeTools: suggestStmt(c, n)
-  semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
+  semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
 
   case n.kind
   of nkIdent, nkAccQuoted:
@@ -207,7 +217,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     if  s == nil and
         {withinMixin, withinConcept}*flags == {} and
         fn.kind in {nkIdent, nkAccQuoted} and
-        considerQuotedIdent(c.config, fn).id notin ctx.toMixin:
+        considerQuotedIdent(c, fn).id notin ctx.toMixin:
       errorUndeclaredIdentifier(c, n.info, fn.renderTree)
 
     var first = int ord(withinConcept in flags)
@@ -215,8 +225,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     if s != nil:
       incl(s.flags, sfUsed)
       mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles}
-      let sc = symChoice(c, fn, s,
-            if s.name.id in ctx.toMixin: scForceOpen else: scOpen)
+      let sc = symChoice(c, fn, s, if s.isMixedIn: scForceOpen else: scOpen)
       case s.kind
       of skMacro:
         if macroToExpand(s) and sc.safeLen <= 1:
@@ -275,12 +284,12 @@ proc semGenericStmt(c: PContext, n: PNode,
       result.sons[i] = semGenericStmt(c, result.sons[i], flags, ctx)
   of nkCurlyExpr:
     result = newNodeI(nkCall, n.info)
-    result.add newIdentNode(getIdent("{}"), n.info)
+    result.add newIdentNode(getIdent(c.cache, "{}"), n.info)
     for i in 0 ..< n.len: result.add(n[i])
     result = semGenericStmt(c, result, flags, ctx)
   of nkBracketExpr:
     result = newNodeI(nkCall, n.info)
-    result.add newIdentNode(getIdent("[]"), n.info)
+    result.add newIdentNode(getIdent(c.cache, "[]"), n.info)
     for i in 0 ..< n.len: result.add(n[i])
     withBracketExpr ctx, n.sons[0]:
       result = semGenericStmt(c, result, flags, ctx)
@@ -293,13 +302,13 @@ proc semGenericStmt(c: PContext, n: PNode,
     case k
     of nkCurlyExpr:
       result = newNodeI(nkCall, n.info)
-      result.add newIdentNode(getIdent("{}="), n.info)
+      result.add newIdentNode(getIdent(c.cache, "{}="), n.info)
       for i in 0 ..< a.len: result.add(a[i])
       result.add(b)
       result = semGenericStmt(c, result, flags, ctx)
     of nkBracketExpr:
       result = newNodeI(nkCall, n.info)
-      result.add newIdentNode(getIdent("[]="), n.info)
+      result.add newIdentNode(getIdent(c.cache, "[]="), n.info)
       for i in 0 ..< a.len: result.add(a[i])
       result.add(b)
       withBracketExpr ctx, a.sons[0]:
@@ -448,7 +457,7 @@ proc semGenericStmt(c: PContext, n: PNode,
                                               flags, ctx)
     if n.sons[paramsPos].kind != nkEmpty:
       if n.sons[paramsPos].sons[0].kind != nkEmpty:
-        addPrelimDecl(c, newSym(skUnknown, getIdent("result"), nil, n.info))
+        addPrelimDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info))
       n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx)
     n.sons[pragmasPos] = semGenericStmt(c, n.sons[pragmasPos], flags, ctx)
     var body: PNode
@@ -472,7 +481,6 @@ proc semGenericStmt(c: PContext, n: PNode,
   when defined(nimsuggest):
     if withinTypeDesc in flags: dec c.inTypeContext
 
-
 proc semGenericStmt(c: PContext, n: PNode): PNode =
   var ctx: GenericCtx
   ctx.toMixin = initIntset()
@@ -484,3 +492,4 @@ proc semConceptBody(c: PContext, n: PNode): PNode =
   ctx.toMixin = initIntset()
   result = semGenericStmt(c, n, {withinConcept}, ctx)
   semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
+
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index a5f0ca7d8..fac04e3a0 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -159,13 +159,13 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
       var oldPrc = c.generics[i].inst.sym
       pushProcCon(c, oldPrc)
       pushOwner(c, oldPrc)
-      pushInfoContext(oldPrc.info)
+      pushInfoContext(c.config, oldPrc.info)
       openScope(c)
       var n = oldPrc.ast
       n.sons[bodyPos] = copyTree(s.getBody)
       instantiateBody(c, n, oldPrc.typ.n, oldPrc, s)
       closeScope(c)
-      popInfoContext()
+      popInfoContext(c.config)
       popOwner(c)
       popProcCon(c)
 
@@ -220,6 +220,14 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
   result = replaceTypeVarsT(cl, header)
   closeScope(c)
 
+proc referencesAnotherParam(n: PNode, p: PSym): bool =
+  if n.kind == nkSym:
+    return n.sym.kind == skParam and n.sym.owner == p
+  else:
+    for i in 0..<n.safeLen:
+      if referencesAnotherParam(n[i], p): return true
+    return false
+
 proc instantiateProcType(c: PContext, pt: TIdTable,
                          prc: PSym, info: TLineInfo) =
   # XXX: Instantiates a generic proc signature, while at the same
@@ -236,7 +244,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
   # at this point semtypinst have to become part of sem, because it
   # will need to use openScope, addDecl, etc.
   #addDecl(c, prc)
-  pushInfoContext(info)
+  pushInfoContext(c.config, info)
   var typeMap = initLayeredTypeMap(pt)
   var cl = initTypeVars(c, addr(typeMap), info, nil)
   var result = instCopyType(cl, prc.typ)
@@ -247,25 +255,56 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
     if i > 1:
       resetIdTable(cl.symMap)
       resetIdTable(cl.localCache)
-    result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
-    propagateToOwner(result, result.sons[i])
+
+    # take a note of the original type. If't a free type or static parameter
+    # we'll need to keep it unbound for the `fitNode` operation below...
+    var typeToFit = result[i]
+
+    let needsStaticSkipping = result[i].kind == tyFromExpr
+    result[i] = replaceTypeVarsT(cl, result[i])
+    if needsStaticSkipping:
+      result[i] = result[i].skipTypes({tyStatic})
+
+    # ...otherwise, we use the instantiated type in `fitNode`
+    if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and
+       (typeToFit.kind != tyStatic):
+      typeToFit = result[i]
+
     internalAssert c.config, originalParams[i].kind == nkSym
-    when true:
-      let oldParam = originalParams[i].sym
-      let param = copySym(oldParam)
-      param.owner = prc
-      param.typ = result.sons[i]
-      if oldParam.ast != nil:
-        param.ast = fitNode(c, param.typ, oldParam.ast, oldParam.ast.info)
-
-      # don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])!
-      result.n.sons[i] = newSymNode(param)
-      addDecl(c, param)
-    else:
-      let param = replaceTypeVarsN(cl, originalParams[i])
-      result.n.sons[i] = param
-      param.sym.owner = prc
-      addDecl(c, result.n.sons[i].sym)
+    let oldParam = originalParams[i].sym
+    let param = copySym(oldParam)
+    param.owner = prc
+    param.typ = result[i]
+
+    # The default value is instantiated and fitted against the final
+    # concrete param type. We avoid calling `replaceTypeVarsN` on the
+    # call head symbol, because this leads to infinite recursion.
+    if oldParam.ast != nil:
+      var def = oldParam.ast.copyTree
+      if def.kind == nkCall:
+        for i in 1 ..< def.len:
+          def[i] = replaceTypeVarsN(cl, def[i])
+
+      def = semExprWithType(c, def)
+      if def.referencesAnotherParam(getCurrOwner(c)):
+        def.flags.incl nfDefaultRefsParam
+
+      var converted = indexTypesMatch(c, typeToFit, def.typ, def)
+      if converted == nil:
+        # The default value doesn't match the final instantiated type.
+        # As an example of this, see:
+        # https://github.com/nim-lang/Nim/issues/1201
+        # We are replacing the default value with an error node in case
+        # the user calls an explicit instantiation of the proc (this is
+        # the only way the default value might be inserted).
+        param.ast = errorNode(c, def)
+      else:
+        param.ast = fitNodePostMatch(c, typeToFit, converted)
+      param.typ = result[i]
+
+    result.n[i] = newSymNode(param)
+    propagateToOwner(result, result[i])
+    addDecl(c, param)
 
   resetIdTable(cl.symMap)
   resetIdTable(cl.localCache)
@@ -278,7 +317,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
   skipIntLiteralParams(result)
 
   prc.typ = result
-  popInfoContext()
+  popInfoContext(c.config)
 
 proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
                       info: TLineInfo): PSym =
@@ -309,7 +348,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   let gp = n.sons[genericParamsPos]
   internalAssert c.config, gp.kind != nkEmpty
   n.sons[namePos] = newSymNode(result)
-  pushInfoContext(info)
+  pushInfoContext(c.config, info)
   var entry = TInstantiation.new
   entry.sym = result
   # we need to compare both the generic types and the concrete types:
@@ -328,7 +367,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
     inc i
   if tfTriggersCompileTime in result.typ.flags:
     incl(result.flags, sfCompileTime)
-  n.sons[genericParamsPos] = ast.emptyNode
+  n.sons[genericParamsPos] = c.graph.emptyNode
   var oldPrc = genericCacheGet(fn, entry[], c.compilesContextId)
   if oldPrc == nil:
     # we MUST not add potentially wrong instantiations to the caching mechanism.
@@ -353,7 +392,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   else:
     result = oldPrc
   popProcCon(c)
-  popInfoContext()
+  popInfoContext(c.config)
   closeScope(c)           # close scope for parameters
   popOwner(c)
   c.currentScope = oldScope
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 8515e971d..1975fb77b 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -41,7 +41,7 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
   result = semSubscript(c, result, flags)
   if result.isNil:
     let x = copyTree(n)
-    x.sons[0] = newIdentNode(getIdent"[]", n.info)
+    x.sons[0] = newIdentNode(getIdent(c.cache, "[]"), n.info)
     bracketNotFoundError(c, x)
     #localError(c.config, n.info, "could not resolve: " & $n)
     result = n
@@ -76,9 +76,9 @@ proc semInstantiationInfo(c: PContext, n: PNode): PNode =
   result = newNodeIT(nkTupleConstr, n.info, n.typ)
   let idx = expectIntLit(c, n.sons[1])
   let useFullPaths = expectIntLit(c, n.sons[2])
-  let info = getInfoContext(idx)
+  let info = getInfoContext(c.config, idx)
   var filename = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString))
-  filename.strVal = if useFullPaths != 0: info.toFullPath else: info.toFilename
+  filename.strVal = if useFullPaths != 0: toFullPath(c.config, info) else: toFilename(c.config, info)
   var line = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt))
   line.intVal = toLinenumber(info)
   var column = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt))
@@ -154,7 +154,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
     result = newIntNodeT(ord(not complexObj), traitCall, c.graph)
   else:
     localError(c.config, traitCall.info, "unknown trait")
-    result = emptyNode
+    result = newNodeI(nkEmpty, traitCall.info)
 
 proc semTypeTraits(c: PContext, n: PNode): PNode =
   checkMinSonsLen(n, 2, c.config)
@@ -174,7 +174,7 @@ proc semOrd(c: PContext, n: PNode): PNode =
   if isOrdinalType(parType):
     discard
   elif parType.kind == tySet:
-    result.typ = makeRangeType(c, firstOrd(parType), lastOrd(parType), n.info)
+    result.typ = makeRangeType(c, firstOrd(c.config, parType), lastOrd(c.config, parType), n.info)
   else:
     localError(c.config, n.info, errOrdinalTypeExpected)
     result.typ = errorType(c)
@@ -194,7 +194,7 @@ proc semBindSym(c: PContext, n: PNode): PNode =
     localError(c.config, n.sons[2].info, errConstExprExpected)
     return errorNode(c, n)
 
-  let id = newIdentNode(getIdent(sl.strVal), n.info)
+  let id = newIdentNode(getIdent(c.cache, sl.strVal), n.info)
   let s = qualifiedLookUp(c, id, {checkUndeclared})
   if s != nil:
     # we need to mark all symbols:
@@ -209,10 +209,6 @@ proc semBindSym(c: PContext, n: PNode): PNode =
 
 proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
 
-proc isStrangeArray(t: PType): bool =
-  let t = t.skipTypes(abstractInst)
-  result = t.kind == tyArray and t.firstOrd != 0
-
 proc semOf(c: PContext, n: PNode): PNode =
   if sonsLen(n) == 3:
     n.sons[1] = semExprWithType(c, n.sons[1])
@@ -283,7 +279,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
   of mRoof:
     localError(c.config, n.info, "builtin roof operator is not supported anymore")
   of mPlugin:
-    let plugin = getPlugin(n[0].sym)
+    let plugin = getPlugin(c.cache, n[0].sym)
     if plugin.isNil:
       localError(c.config, n.info, "cannot find plugin " & n[0].sym.name.s)
       result = n
diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim
index 1e0802953..8b639806d 100644
--- a/compiler/semobjconstr.nim
+++ b/compiler/semobjconstr.nim
@@ -54,7 +54,7 @@ proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode =
       invalidObjConstr(c, assignment)
       continue
 
-    if fieldId == considerQuotedIdent(c.config, assignment[0]).id:
+    if fieldId == considerQuotedIdent(c, assignment[0]).id:
       return assignment
 
 proc semConstrField(c: PContext, flags: TExprFlags,
@@ -295,11 +295,11 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
       if field.kind != nkExprColonExpr:
         invalidObjConstr(c, field)
         continue
-      let id = considerQuotedIdent(c.config, field[0])
+      let id = considerQuotedIdent(c, field[0])
       # This node was not processed. There are two possible reasons:
       # 1) It was shadowed by a field with the same name on the left
       for j in 1 ..< i:
-        let prevId = considerQuotedIdent(c.config, result[j][0])
+        let prevId = considerQuotedIdent(c, result[j][0])
         if prevId.id == id.id:
           localError(c.config, field.info, errFieldInitTwice % id.s)
           return
diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim
index ea5aab628..0d780bdee 100644
--- a/compiler/semparallel.nim
+++ b/compiler/semparallel.nim
@@ -136,8 +136,8 @@ proc checkLe(c: AnalysisCtx; a, b: PNode) =
     localError(c.graph.config, a.info, "can prove: " & ?a & " > " & ?b & " (bounds check)")
 
 proc checkBounds(c: AnalysisCtx; arr, idx: PNode) =
-  checkLe(c, arr.lowBound, idx)
-  checkLe(c, idx, arr.highBound(c.guards.o))
+  checkLe(c, lowBound(c.graph.config, arr), idx)
+  checkLe(c, idx, highBound(c.graph.config, arr, c.guards.o))
 
 proc addLowerBoundAsFacts(c: var AnalysisCtx) =
   for v in c.locals:
@@ -438,7 +438,7 @@ proc transformSpawn(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode =
         let t = b[1][0].typ.sons[0]
         if spawnResult(t, true) == srByVar:
           result.add wrapProcForSpawn(g, owner, m, b.typ, barrier, it[0])
-          it.sons[it.len-1] = emptyNode
+          it.sons[it.len-1] = newNodeI(nkEmpty, it.info)
         else:
           it.sons[it.len-1] = wrapProcForSpawn(g, owner, m, b.typ, barrier, nil)
     if result.isNil: result = n
@@ -483,7 +483,7 @@ proc liftParallel*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
   checkArgs(a, body)
 
   var varSection = newNodeI(nkVarSection, n.info)
-  var temp = newSym(skTemp, getIdent"barrier", owner, n.info)
+  var temp = newSym(skTemp, getIdent(g.cache, "barrier"), owner, n.info)
   temp.typ = magicsys.getCompilerProc(g, "Barrier").typ
   incl(temp.flags, sfFromGeneric)
   let tempNode = newSymNode(temp)
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index b66d7d9f2..4d3ee0408 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -9,7 +9,7 @@
 
 import
   intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
-  wordrecg, strutils, options, guards, writetracking, configuration,
+  wordrecg, strutils, options, guards, writetracking, lineinfos,
   modulegraphs
 
 when defined(useDfa):
@@ -185,7 +185,7 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
       if reason.kind == nkSym:
         a.owner.gcUnsafetyReason = reason.sym
       else:
-        a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"),
+        a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name,
                                           a.owner, reason.info, {})
 
 when true:
@@ -410,7 +410,7 @@ proc effectSpec(n: PNode, effectType: TSpecialWord): PNode =
         result.add(it.sons[1])
       return
 
-proc documentEffect(n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
+proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
   let spec = effectSpec(x, effectType)
   if isNil(spec):
     let s = n.sons[namePos].sym
@@ -424,14 +424,14 @@ proc documentEffect(n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
     for i in 0 ..< real.len:
       var t = typeToString(real[i].typ)
       if t.startsWith("ref "): t = substr(t, 4)
-      effects.sons[i] = newIdentNode(getIdent(t), n.info)
+      effects.sons[i] = newIdentNode(getIdent(cache, t), n.info)
       # set the type so that the following analysis doesn't screw up:
       effects.sons[i].typ = real[i].typ
 
     result = newNode(nkExprColonExpr, n.info, @[
-      newIdentNode(getIdent(specialWords[effectType]), n.info), effects])
+      newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects])
 
-proc documentWriteEffect(n: PNode; flag: TSymFlag; pragmaName: string): PNode =
+proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode =
   let s = n.sons[namePos].sym
   let params = s.typ.n
 
@@ -442,21 +442,21 @@ proc documentWriteEffect(n: PNode; flag: TSymFlag; pragmaName: string): PNode =
 
   if effects.len > 0:
     result = newNode(nkExprColonExpr, n.info, @[
-      newIdentNode(getIdent(pragmaName), n.info), effects])
+      newIdentNode(getIdent(cache, pragmaName), n.info), effects])
 
-proc documentNewEffect(n: PNode): PNode =
+proc documentNewEffect(cache: IdentCache; n: PNode): PNode =
   let s = n.sons[namePos].sym
   if tfReturnsNew in s.typ.flags:
-    result = newIdentNode(getIdent("new"), n.info)
+    result = newIdentNode(getIdent(cache, "new"), n.info)
 
-proc documentRaises*(n: PNode) =
+proc documentRaises*(cache: IdentCache; n: PNode) =
   if n.sons[namePos].kind != nkSym: return
   let pragmas = n.sons[pragmasPos]
-  let p1 = documentEffect(n, pragmas, wRaises, exceptionEffects)
-  let p2 = documentEffect(n, pragmas, wTags, tagEffects)
-  let p3 = documentWriteEffect(n, sfWrittenTo, "writes")
-  let p4 = documentNewEffect(n)
-  let p5 = documentWriteEffect(n, sfEscapes, "escapes")
+  let p1 = documentEffect(cache, n, pragmas, wRaises, exceptionEffects)
+  let p2 = documentEffect(cache, n, pragmas, wTags, tagEffects)
+  let p3 = documentWriteEffect(cache, n, sfWrittenTo, "writes")
+  let p4 = documentNewEffect(cache, n)
+  let p5 = documentWriteEffect(cache, n, sfEscapes, "escapes")
 
   if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil:
     if pragmas.kind == nkEmpty:
@@ -859,9 +859,9 @@ proc checkRaisesSpec(g: ModuleGraph; spec, real: PNode, msg: string, hints: bool
           used.incl(s)
           break search
       # XXX call graph analysis would be nice here!
-      pushInfoContext(spec.info)
+      pushInfoContext(g.config, spec.info)
       localError(g.config, r.info, errGenerated, msg & typeToString(r.typ))
-      popInfoContext()
+      popInfoContext(g.config)
   # hint about unnecessarily listed exception types:
   if hints:
     for s in 0 ..< spec.len:
@@ -915,8 +915,8 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
   newSeq(effects.sons, effectListLen)
   effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
   effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
-  effects.sons[usesEffects] = ast.emptyNode
-  effects.sons[writeEffects] = ast.emptyNode
+  effects.sons[usesEffects] = g.emptyNode
+  effects.sons[writeEffects] = g.emptyNode
 
   t.exc = effects.sons[exceptionEffects]
   t.tags = effects.sons[tagEffects]
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 12337262a..2a959b8a8 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -36,8 +36,6 @@ const
   errRecursiveDependencyX = "recursive dependency: '$1'"
   errPragmaOnlyInHeaderOfProcX = "pragmas are only allowed in the header of a proc; redefinition of $1"
 
-var enforceVoidContext = PType(kind: tyStmt) # XXX global variable here
-
 proc semDiscard(c: PContext, n: PNode): PNode =
   result = n
   checkSonsLen(n, 1, c.config)
@@ -87,15 +85,15 @@ proc semWhile(c: PContext, n: PNode): PNode =
   n.sons[1] = semStmt(c, n.sons[1])
   dec(c.p.nestedLoopCounter)
   closeScope(c)
-  if n.sons[1].typ == enforceVoidContext:
-    result.typ = enforceVoidContext
+  if n.sons[1].typ == c.enforceVoidContext:
+    result.typ = c.enforceVoidContext
 
-proc toCover(t: PType): BiggestInt =
-  var t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
+proc toCover(c: PContext, t: PType): BiggestInt =
+  let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
   if t2.kind == tyEnum and enumHasHoles(t2):
     result = sonsLen(t2.n)
   else:
-    result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc}))
+    result = lengthOrd(c.config, skipTypes(t, abstractVar-{tyTypeDesc}))
 
 proc semProc(c: PContext, n: PNode): PNode
 
@@ -146,7 +144,7 @@ proc discardCheck(c: PContext, result: PNode) =
           result.typ.typeToString & "' and has to be discarded"
       if result.info.line != n.info.line or
           result.info.fileIndex != n.info.fileIndex:
-        s.add "; start of expression here: " & $result.info
+        s.add "; start of expression here: " & c.config$result.info
       if result.typ.kind == tyProc:
         s.add "; for a function call use ()"
       localError(c.config, n.info, s)
@@ -173,7 +171,7 @@ proc semIf(c: PContext, n: PNode): PNode =
     for it in n: discardCheck(c, it.lastSon)
     result.kind = nkIfStmt
     # propagate any enforced VoidContext:
-    if typ == enforceVoidContext: result.typ = enforceVoidContext
+    if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext
   else:
     for it in n:
       let j = it.len-1
@@ -203,7 +201,7 @@ proc semCase(c: PContext, n: PNode): PNode =
   for i in countup(1, sonsLen(n) - 1):
     var x = n.sons[i]
     when defined(nimsuggest):
-      if c.config.ideCmd == ideSug and exactEquals(gTrackPos, x.info) and caseTyp.kind == tyEnum:
+      if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, x.info) and caseTyp.kind == tyEnum:
         suggestEnum(c, x, caseTyp)
     case x.kind
     of nkOfBranch:
@@ -230,7 +228,7 @@ proc semCase(c: PContext, n: PNode): PNode =
     else:
       illFormedAst(x, c.config)
   if chckCovered:
-    if covered == toCover(n.sons[0].typ):
+    if covered == toCover(c, n.sons[0].typ):
       hasElse = true
     else:
       localError(c.config, n.info, "not all cases are covered")
@@ -238,8 +236,8 @@ proc semCase(c: PContext, n: PNode): PNode =
   if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
     for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
     # propagate any enforced VoidContext:
-    if typ == enforceVoidContext:
-      result.typ = enforceVoidContext
+    if typ == c.enforceVoidContext:
+      result.typ = c.enforceVoidContext
   else:
     for i in 1..n.len-1:
       var it = n.sons[i]
@@ -318,8 +316,8 @@ proc semTry(c: PContext, n: PNode): PNode =
   if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}:
     discardCheck(c, n.sons[0])
     for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
-    if typ == enforceVoidContext:
-      result.typ = enforceVoidContext
+    if typ == c.enforceVoidContext:
+      result.typ = c.enforceVoidContext
   else:
     if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon)
     n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info)
@@ -366,7 +364,7 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
     if result.owner.kind == skModule:
       incl(result.flags, sfGlobal)
   suggestSym(c.config, n.info, result, c.graph.usageSym)
-  styleCheckDef(result)
+  styleCheckDef(c.config, result)
 
 proc checkNilable(c: PContext; v: PSym) =
   if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and
@@ -394,7 +392,7 @@ proc isDiscardUnderscore(v: PSym): bool =
     result = true
 
 proc semUsing(c: PContext; n: PNode): PNode =
-  result = ast.emptyNode
+  result = c.graph.emptyNode
   if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "using")
   for i in countup(0, sonsLen(n)-1):
     var a = n.sons[i]
@@ -442,10 +440,10 @@ proc makeDeref(n: PNode): PNode =
 proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
   if n.len == 2:
     let x = semExprWithType(c, n[0])
-    let y = considerQuotedIdent(c.config, n[1])
+    let y = considerQuotedIdent(c, n[1])
     let obj = x.typ.skipTypes(abstractPtrs)
     if obj.kind == tyObject and tfPartial in obj.flags:
-      let field = newSym(skField, getIdent(y.s), obj.sym, n[1].info)
+      let field = newSym(skField, getIdent(c.cache, y.s), obj.sym, n[1].info)
       field.typ = skipIntLit(typ)
       field.position = sonsLen(obj.n)
       addSon(obj.n, newSymNode(field))
@@ -481,7 +479,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
       typ = semTypeNode(c, a.sons[length-2], nil)
     else:
       typ = nil
-    var def: PNode = ast.emptyNode
+    var def: PNode = c.graph.emptyNode
     if a.sons[length-1].kind != nkEmpty:
       def = semExprWithType(c, a.sons[length-1], {efAllowDestructor})
       if def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
@@ -521,7 +519,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         localError(c.config, a.info, errWrongNumberOfVariables)
       b = newNodeI(nkVarTuple, a.info)
       newSons(b, length)
-      b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
+      # keep type desc for doc generator
+      # NOTE: at the moment this is always ast.emptyNode, see parser.nim
+      b.sons[length-2] = a.sons[length-2]
       b.sons[length-1] = def
       addToVarSection(c, result, n, b)
     elif tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and
@@ -560,7 +560,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
           # keep documentation information:
           b.comment = a.comment
         addSon(b, newSymNode(v))
-        addSon(b, a.sons[length-2])      # keep type desc for doc generator
+        # keep type desc for doc generator, but only if the user explicitly
+        # added it
+        if a.sons[length-2].kind != nkEmpty:
+          addSon(b, newNodeIT(nkType, a.info, typ))
+        else:
+          addSon(b, a.sons[length-2])
         addSon(b, copyTree(def))
         addToVarSection(c, result, n, b)
       else:
@@ -571,8 +576,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         b.sons[j] = newSymNode(v)
       checkNilable(c, v)
       if sfCompileTime in v.flags: hasCompileTime = true
+      if v.flags * {sfGlobal, sfThread} == {sfGlobal}:
+        message(c.config, v.info, hintGlobalVar)
   if hasCompileTime:
-    vm.setupCompileTimeVar(c.module, c.cache, c.graph, result)
+    vm.setupCompileTimeVar(c.module, c.graph, result)
+    # handled by the VM codegen:
+    #c.graph.recordStmt(c.graph, c.module, result)
 
 proc semConst(c: PContext, n: PNode): PNode =
   result = copyNode(n)
@@ -625,7 +634,7 @@ proc addForVarDecl(c: PContext, v: PSym) =
 proc symForVar(c: PContext, n: PNode): PSym =
   let m = if n.kind == nkPragmaExpr: n.sons[0] else: n
   result = newSymG(skForVar, m, c)
-  styleCheckDef(result)
+  styleCheckDef(c.config, result)
 
 proc semForVars(c: PContext, n: PNode): PNode =
   result = n
@@ -664,7 +673,7 @@ proc semForVars(c: PContext, n: PNode): PNode =
 
 proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
   result = newNodeI(nkCall, arg.info)
-  result.add(newIdentNode(it.getIdent, arg.info))
+  result.add(newIdentNode(getIdent(c.cache, it), arg.info))
   if arg.typ != nil and arg.typ.kind in {tyVar, tyLent}:
     result.add newDeref(arg)
   else:
@@ -698,7 +707,8 @@ proc handleForLoopMacro(c: PContext; n: PNode): PNode =
             match = symx
           else:
             localError(c.config, n.info, errAmbiguousCallXYZ % [
-              getProcHeader(match), getProcHeader(symx), $iterExpr])
+              getProcHeader(c.config, match),
+              getProcHeader(c.config, symx), $iterExpr])
       symx = nextOverloadIter(o, c, headSymbol)
 
     if match == nil: return
@@ -746,8 +756,8 @@ proc semFor(c: PContext, n: PNode): PNode =
   else:
     result = semForVars(c, n)
   # propagate any enforced VoidContext:
-  if n.sons[length-1].typ == enforceVoidContext:
-    result.typ = enforceVoidContext
+  if n.sons[length-1].typ == c.enforceVoidContext:
+    result.typ = c.enforceVoidContext
   closeScope(c)
 
 proc semRaise(c: PContext, n: PNode): PNode =
@@ -796,8 +806,8 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
     let name = a.sons[0]
     var s: PSym
     if name.kind == nkDotExpr and a[2].kind == nkObjectTy:
-      let pkgName = considerQuotedIdent(c.config, name[0])
-      let typName = considerQuotedIdent(c.config, name[1])
+      let pkgName = considerQuotedIdent(c, name[0])
+      let typName = considerQuotedIdent(c, name[1])
       let pkg = c.graph.packageSyms.strTableGet(pkgName)
       if pkg.isNil or pkg.kind != skPackage:
         localError(c.config, name.info, "unknown package name: " & pkgName.s)
@@ -835,7 +845,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
               typsym.info = s.info
             else:
               localError(c.config, name.info, "cannot complete type '" & s.name.s & "' twice; " &
-                      "previous type completion was here: " & $typsym.info)
+                      "previous type completion was here: " & c.config$typsym.info)
             s = typsym
       # add it here, so that recursive types are possible:
       if sfGenSym notin s.flags: addInterfaceDecl(c, s)
@@ -989,7 +999,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
       internalAssert c.config, st.kind in {tyPtr, tyRef}
       internalAssert c.config, st.lastSon.sym == nil
       incl st.flags, tfRefsAnonObj
-      let obj = newSym(skType, getIdent(s.name.s & ":ObjectType"),
+      let obj = newSym(skType, getIdent(c.cache, s.name.s & ":ObjectType"),
                               getCurrOwner(c), s.info)
       obj.typ = st.lastSon
       st.lastSon.sym = obj
@@ -1057,9 +1067,9 @@ proc semAllTypeSections(c: PContext; n: PNode): PNode =
         var f = checkModuleName(c.config, n.sons[i])
         if f != InvalidFileIDX:
           if containsOrIncl(c.includedFiles, f.int):
-            localError(c.config, n.info, errRecursiveDependencyX % f.toFilename)
+            localError(c.config, n.info, errRecursiveDependencyX % toFilename(c.config, f))
           else:
-            let code = gIncludeFile(c.graph, c.module, f, c.cache)
+            let code = c.graph.includeFileCallback(c.graph, c.module, f)
             gatherStmts c, code, result
             excl(c.includedFiles, f.int)
     of nkStmtList:
@@ -1131,7 +1141,7 @@ proc semBorrow(c: PContext, n: PNode, s: PSym) =
 
 proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) =
   if t != nil:
-    var s = newSym(skResult, getIdent"result", getCurrOwner(c), info)
+    var s = newSym(skResult, getIdent(c.cache, "result"), getCurrOwner(c), info)
     s.typ = t
     incl(s.flags, sfUsed)
     addParamOrResult(c, s, owner)
@@ -1150,7 +1160,7 @@ proc lookupMacro(c: PContext, n: PNode): PSym =
     result = n.sym
     if result.kind notin {skMacro, skTemplate}: result = nil
   else:
-    result = searchInScopes(c, considerQuotedIdent(c.config, n), {skMacro, skTemplate})
+    result = searchInScopes(c, considerQuotedIdent(c, n), {skMacro, skTemplate})
 
 proc semProcAnnotation(c: PContext, prc: PNode;
                        validPragmas: TSpecialWords): PNode =
@@ -1162,7 +1172,7 @@ proc semProcAnnotation(c: PContext, prc: PNode;
     let m = lookupMacro(c, key)
     if m == nil:
       if key.kind == nkIdent and key.ident.id == ord(wDelegator):
-        if considerQuotedIdent(c.config, prc.sons[namePos]).s == "()":
+        if considerQuotedIdent(c, prc.sons[namePos]).s == "()":
           prc.sons[namePos] = newIdentNode(c.cache.idDelegator, prc.info)
           prc.sons[pragmasPos] = copyExcept(n, i)
         else:
@@ -1177,7 +1187,7 @@ proc semProcAnnotation(c: PContext, prc: PNode;
     x.add(newSymNode(m))
     prc.sons[pragmasPos] = copyExcept(n, i)
     if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0:
-      prc.sons[pragmasPos] = emptyNode
+      prc.sons[pragmasPos] = c.graph.emptyNode
 
     if it.kind in nkPragmaCallKinds and it.len > 1:
       # pass pragma arguments to the macro too:
@@ -1200,7 +1210,7 @@ proc setGenericParamsMisc(c: PContext; n: PNode): PNode =
   # issue https://github.com/nim-lang/Nim/issues/1713
   result = semGenericParamList(c, orig)
   if n.sons[miscPos].kind == nkEmpty:
-    n.sons[miscPos] = newTree(nkBracket, ast.emptyNode, orig)
+    n.sons[miscPos] = newTree(nkBracket, c.graph.emptyNode, orig)
   else:
     n.sons[miscPos].sons[1] = orig
   n.sons[genericParamsPos] = result
@@ -1271,7 +1281,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
   result = n
   s.ast = result
   n.sons[namePos].sym = s
-  n.sons[genericParamsPos] = emptyNode
+  n.sons[genericParamsPos] = c.graph.emptyNode
   # for LL we need to avoid wrong aliasing
   let params = copyTree n.typ.n
   n.sons[paramsPos] = params
@@ -1398,14 +1408,14 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
       localError(c.config, n.info, errGenerated,
                  "'destroy' or 'deepCopy' expected for 'override'")
 
-proc cursorInProcAux(n: PNode): bool =
-  if inCheckpoint(n.info) != cpNone: return true
+proc cursorInProcAux(conf: ConfigRef; n: PNode): bool =
+  if inCheckpoint(n.info, conf.m.trackPos) != cpNone: return true
   for i in 0..<n.safeLen:
-    if cursorInProcAux(n[i]): return true
+    if cursorInProcAux(conf, n[i]): return true
 
-proc cursorInProc(n: PNode): bool =
-  if n.info.fileIndex == gTrackPos.fileIndex:
-    result = cursorInProcAux(n)
+proc cursorInProc(conf: ConfigRef; n: PNode): bool =
+  if n.info.fileIndex == conf.m.trackPos.fileIndex:
+    result = cursorInProcAux(conf, n)
 
 type
   TProcCompilationSteps = enum
@@ -1484,6 +1494,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     s.ast = n
     #s.scope = c.currentScope
 
+  s.options = c.config.options
+
   # before compiling the proc body, set as current the scope
   # where the proc was declared
   let oldScope = c.currentScope
@@ -1544,7 +1556,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
       # linking names do agree:
       if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags:
         localError(c.config, n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX %
-          ("'" & proto.name.s & "' from " & $proto.info))
+          ("'" & proto.name.s & "' from " & c.config$proto.info))
     if sfForward notin proto.flags:
       wrongRedefinition(c, n.info, proto.name.s)
     excl(proto.flags, sfForward)
@@ -1555,6 +1567,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     addParams(c, proto.typ.n, proto.kind)
     proto.info = s.info       # more accurate line information
     s.typ = proto.typ
+    proto.options = s.options
     s = proto
     n.sons[genericParamsPos] = proto.ast.sons[genericParamsPos]
     n.sons[paramsPos] = proto.ast.sons[paramsPos]
@@ -1566,7 +1579,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     proto.ast = n             # needed for code generation
     popOwner(c)
     pushOwner(c, s)
-  s.options = c.config.options
+
   if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
   if s.name.s[0] in {'.', '('}:
     if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}:
@@ -1585,7 +1598,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     # only used for overload resolution (there is no instantiation of
     # the symbol, so we must process the body now)
     if not usePseudoGenerics and c.config.ideCmd in {ideSug, ideCon} and not
-        cursorInProc(n.sons[bodyPos]):
+        cursorInProc(c.config, n.sons[bodyPos]):
       discard "speed up nimsuggest"
       if s.kind == skMethod: semMethodPrototype(c, s, n)
     else:
@@ -1605,7 +1618,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
           n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s)
       else:
         if s.typ.sons[0] != nil and kind != skIterator:
-          addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info))
+          addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info))
 
         openScope(c)
         n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos])
@@ -1614,7 +1627,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         if s.kind == skMethod: semMethodPrototype(c, s, n)
       if sfImportc in s.flags:
         # so we just ignore the body after semantic checking for importc:
-        n.sons[bodyPos] = ast.emptyNode
+        n.sons[bodyPos] = c.graph.emptyNode
       popProcCon(c)
   else:
     if s.kind == skMethod: semMethodPrototype(c, s, n)
@@ -1693,7 +1706,7 @@ proc semMethod(c: PContext, n: PNode): PNode =
     let ret = s.typ.sons[0]
     disp.typ.sons[0] = ret
     if disp.ast[resultPos].kind == nkSym:
-      if isEmptyType(ret): disp.ast.sons[resultPos] = emptyNode
+      if isEmptyType(ret): disp.ast.sons[resultPos] = c.graph.emptyNode
       else: disp.ast[resultPos].sym.typ = ret
 
 proc semConverterDef(c: PContext, n: PNode): PNode =
@@ -1739,9 +1752,9 @@ proc evalInclude(c: PContext, n: PNode): PNode =
     var f = checkModuleName(c.config, n.sons[i])
     if f != InvalidFileIDX:
       if containsOrIncl(c.includedFiles, f.int):
-        localError(c.config, n.info, errRecursiveDependencyX % f.toFilename)
+        localError(c.config, n.info, errRecursiveDependencyX % toFilename(c.config, f))
       else:
-        addSon(result, semStmt(c, gIncludeFile(c.graph, c.module, f, c.cache)))
+        addSon(result, semStmt(c, c.graph.includeFileCallback(c.graph, c.module, f)))
         excl(c.includedFiles, f.int)
 
 proc setLine(n: PNode, info: TLineInfo) =
@@ -1767,12 +1780,18 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
   #echo "semStaticStmt"
   #writeStackTrace()
   inc c.inStaticContext
+  openScope(c)
   let a = semStmt(c, n.sons[0])
+  closeScope(c)
   dec c.inStaticContext
   n.sons[0] = a
-  evalStaticStmt(c.module, c.cache, c.graph, a, c.p.owner)
-  result = newNodeI(nkDiscardStmt, n.info, 1)
-  result.sons[0] = emptyNode
+  evalStaticStmt(c.module, c.graph, a, c.p.owner)
+  when false:
+    # for incremental replays, keep the AST as required for replays:
+    result = n
+  else:
+    result = newNodeI(nkDiscardStmt, n.info, 1)
+    result.sons[0] = c.graph.emptyNode
 
 proc usesResult(n: PNode): bool =
   # nkStmtList(expr) properly propagates the void context,
@@ -1835,9 +1854,9 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
           localError(c.config, result.info, "concept predicate failed")
       of tyUnknown: continue
       else: discard
-    if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]):
+    if n.sons[i].typ == c.enforceVoidContext: #or usesResult(n.sons[i]):
       voidContext = true
-      n.typ = enforceVoidContext
+      n.typ = c.enforceVoidContext
     if i == last and (length == 1 or efWantValue in flags):
       n.typ = n.sons[i].typ
       if not isEmptyType(n.typ): n.kind = nkStmtListExpr
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 352bc5c6b..75c6bc4bb 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -99,7 +99,7 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
 
 proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode =
   for i in 0 ..< n.len:
-    toMixin.incl(considerQuotedIdent(c.config, n.sons[i]).id)
+    toMixin.incl(considerQuotedIdent(c, n.sons[i]).id)
   result = newNodeI(nkEmpty, n.info)
 
 proc replaceIdentBySym(c: PContext; n: var PNode, s: PNode) =
@@ -166,7 +166,7 @@ proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode =
       result.sons[i] = onlyReplaceParams(c, n.sons[i])
 
 proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
-  result = newSym(kind, considerQuotedIdent(c.c.config, n), c.owner, n.info)
+  result = newSym(kind, considerQuotedIdent(c.c, n), c.owner, n.info)
   incl(result.flags, sfGenSym)
   incl(result.flags, sfShadowed)
 
@@ -206,14 +206,14 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
       #
       # We need to ensure that both 'a' produce the same gensym'ed symbol.
       # So we need only check the *current* scope.
-      let s = localSearchInScope(c.c, considerQuotedIdent(c.c.config, ident))
+      let s = localSearchInScope(c.c, considerQuotedIdent(c.c, ident))
       if s != nil and s.owner == c.owner and sfGenSym in s.flags:
         styleCheckUse(n.info, s)
         replaceIdentBySym(c.c, n, newSymNode(s, n.info))
       else:
         let local = newGenSym(k, ident, c)
         addPrelimDecl(c.c, local)
-        styleCheckDef(n.info, local)
+        styleCheckDef(c.c.config, n.info, local)
         replaceIdentBySym(c.c, n, newSymNode(local, n.info))
     else:
       replaceIdentBySym(c.c, n, ident)
@@ -260,7 +260,7 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
       var s = newGenSym(k, ident, c)
       s.ast = n
       addPrelimDecl(c.c, s)
-      styleCheckDef(n.info, s)
+      styleCheckDef(c.c.config, n.info, s)
       n.sons[namePos] = newSymNode(s, n.sons[namePos].info)
     else:
       n.sons[namePos] = ident
@@ -305,7 +305,7 @@ proc semTemplBodySons(c: var TemplCtx, n: PNode): PNode =
 
 proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
   result = n
-  semIdeForTemplateOrGenericCheck(n, c.cursorInBody)
+  semIdeForTemplateOrGenericCheck(c.c.config, n, c.cursorInBody)
   case n.kind
   of nkIdent:
     if n.ident.id in c.toInject: return n
@@ -382,7 +382,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
         # labels are always 'gensym'ed:
         let s = newGenSym(skLabel, n.sons[0], c)
         addPrelimDecl(c.c, s)
-        styleCheckDef(s)
+        styleCheckDef(c.c.config, s)
         n.sons[0] = newSymNode(s, n.sons[0].info)
     n.sons[1] = semTemplBody(c, n.sons[1])
     closeScope(c)
@@ -460,14 +460,14 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
         x.sons[1] = semTemplBody(c, x.sons[1])
   of nkBracketExpr:
     result = newNodeI(nkCall, n.info)
-    result.add newIdentNode(getIdent("[]"), n.info)
+    result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info)
     for i in 0 ..< n.len: result.add(n[i])
     let n0 = semTemplBody(c, n.sons[0])
     withBracketExpr c, n0:
       result = semTemplBodySons(c, result)
   of nkCurlyExpr:
     result = newNodeI(nkCall, n.info)
-    result.add newIdentNode(getIdent("{}"), n.info)
+    result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
     for i in 0 ..< n.len: result.add(n[i])
     result = semTemplBodySons(c, result)
   of nkAsgn, nkFastAsgn:
@@ -479,7 +479,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
     case k
     of nkBracketExpr:
       result = newNodeI(nkCall, n.info)
-      result.add newIdentNode(getIdent("[]="), n.info)
+      result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info)
       for i in 0 ..< a.len: result.add(a[i])
       result.add(b)
       let a0 = semTemplBody(c, a.sons[0])
@@ -487,7 +487,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
         result = semTemplBodySons(c, result)
     of nkCurlyExpr:
       result = newNodeI(nkCall, n.info)
-      result.add newIdentNode(getIdent("{}="), n.info)
+      result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info)
       for i in 0 ..< a.len: result.add(a[i])
       result.add(b)
       result = semTemplBodySons(c, result)
@@ -518,7 +518,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
 
 proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode =
   result = n
-  semIdeForTemplateOrGenericCheck(n, c.cursorInBody)
+  semIdeForTemplateOrGenericCheck(c.c.config, n, c.cursorInBody)
   case n.kind
   of nkIdent:
     let s = qualifiedLookUp(c.c, n, {})
@@ -551,7 +551,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
     incl(s.flags, sfGlobal)
   else:
     s = semIdentVis(c, skTemplate, n.sons[0], {})
-  styleCheckDef(s)
+  styleCheckDef(c.config, s)
   # check parameter list:
   #s.scope = c.currentScope
   pushOwner(c, s)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 8b5c26f99..d1ebb950c 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -37,6 +37,12 @@ const
   errNoGenericParamsAllowedForX = "no generic parameters allowed for $1"
   errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types"
 
+const
+  mStaticTy = {mStatic}
+  mTypeTy = {mType, mTypeOf}
+  # XXX: This should be needed only temporarily until the C
+  # sources are rebuilt
+
 proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
   if prev == nil:
     result = newTypeS(kind, c)
@@ -66,7 +72,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
     base = semTypeNode(c, n.sons[0].sons[0], nil)
     if base.kind != tyEnum:
       localError(c.config, n.sons[0].info, "inheritance only works with an enum")
-    counter = lastOrd(base) + 1
+    counter = lastOrd(c.config, base) + 1
   rawAddSon(result, base)
   let isPure = result.sym != nil and sfPure in result.sym.flags
   var symbols: TStrTable
@@ -114,8 +120,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
       incl(e.flags, sfExported)
       if not isPure: strTableAdd(c.module.tab, e)
     addSon(result.n, newSymNode(e))
-    let conf = c.config
-    styleCheckDef(e)
+    styleCheckDef(c.config, e)
     if sfGenSym notin e.flags:
       if not isPure: addDecl(c, e)
       else: importPureEnumField(c, e)
@@ -133,7 +138,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
     if base.kind != tyGenericParam:
       if not isOrdinalType(base):
         localError(c.config, n.info, errOrdinalTypeExpected)
-      elif lengthOrd(base) > MaxSetElements:
+      elif lengthOrd(c.config, base) > MaxSetElements:
         localError(c.config, n.info, errSetTooBig)
   else:
     localError(c.config, n.info, errXExpectsOneTypeParam % "set")
@@ -157,7 +162,7 @@ proc semVarargs(c: PContext, n: PNode, prev: PType): PType =
     var base = semTypeNode(c, n.sons[1], nil)
     addSonSkipIntLit(result, base)
     if sonsLen(n) == 3:
-      result.n = newIdentNode(considerQuotedIdent(c.config, n.sons[2]), n.sons[2].info)
+      result.n = newIdentNode(considerQuotedIdent(c, n.sons[2]), n.sons[2].info)
   else:
     localError(c.config, n.info, errXExpectsOneTypeParam % "varargs")
     addSonSkipIntLit(result, errorType(c))
@@ -233,13 +238,13 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
   if not hasUnknownTypes:
     if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})):
       localError(c.config, n.info, "type mismatch")
-    elif not rangeT[0].isOrdinalType:
-      localError(c.config, n.info, "ordinal type expected")
+    elif not rangeT[0].isOrdinalType and rangeT[0].kind notin tyFloat..tyFloat128:
+      localError(c.config, n.info, "ordinal or float type expected")
     elif enumHasHoles(rangeT[0]):
       localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0]))
 
   for i in 0..1:
-    if hasGenericArguments(range[i]):
+    if hasUnresolvedArgs(c, range[i]):
       result.n.addSon makeStaticExpr(c, range[i])
       result.flags.incl tfUnresolved
     else:
@@ -267,7 +272,7 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
           n.sons[1].floatVal < 0.0:
         incl(result.flags, tfNeedsInit)
     else:
-      if n[1].kind == nkInfix and considerQuotedIdent(c.config, n[1][0]).s == "..<":
+      if n[1].kind == nkInfix and considerQuotedIdent(c, n[1][0]).s == "..<":
         localError(c.config, n[0].info, "range types need to be constructed with '..', '..<' is not supported")
       else:
         localError(c.config, n.sons[0].info, "expected range")
@@ -296,7 +301,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
         localError(c.config, info, errOrdinalTypeExpected)
       result = makeRangeWithStaticExpr(c, e)
       if c.inGenericContext > 0: result.flags.incl tfUnresolved
-    elif e.kind in nkCallKinds and hasGenericArguments(e):
+    elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e):
       if not isOrdinalType(e.typ):
         localError(c.config, n[1].info, errOrdinalTypeExpected)
       # This is an int returning call, depending on an
@@ -364,6 +369,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
     if result != nil:
       markUsed(c.config, n.info, result, c.graph.usageSym)
       styleCheckUse(n.info, result)
+
       if result.kind == skParam and result.typ.kind == tyTypeDesc:
         # This is a typedesc param. is it already bound?
         # it's not bound when it's used multiple times in the
@@ -389,8 +395,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
         else:
           localError(c.config, n.info, errTypeExpected)
           return errorSym(c, n)
-
-      if result.kind != skType:
+      if result.kind != skType and result.magic notin (mStaticTy + mTypeTy):
         # this implements the wanted ``var v: V, x: V`` feature ...
         var ov: TOverloadIter
         var amb = initOverloadIter(ov, c, n)
@@ -451,7 +456,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
       else:
         addSon(result.n, newSymNode(field))
         addSonSkipIntLit(result, typ)
-      if c.config.cmd == cmdPretty: styleCheckDef(a.sons[j].info, field)
+      styleCheckDef(c.config, a.sons[j].info, field)
   if result.n.len == 0: result.n = nil
 
 proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
@@ -462,7 +467,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
       # for gensym'ed identifiers the identifier may already have been
       # transformed to a symbol and we need to use that here:
       result = newSymG(kind, n.sons[1], c)
-      var v = considerQuotedIdent(c.config, n.sons[0])
+      var v = considerQuotedIdent(c, n.sons[0])
       if sfExported in allowed and v.id == ord(wStar):
         incl(result.flags, sfExported)
       else:
@@ -491,7 +496,7 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
     else: discard
   else:
     result = semIdentVis(c, kind, n, allowed)
-  if c.config.cmd == cmdPretty: styleCheckDef(n.info, result)
+  styleCheckDef(c.config, n.info, result)
 
 proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) =
   let ex = t[branchIndex][currentEx].skipConv
@@ -553,7 +558,7 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
         inc(covered)
       else:
         if r.kind == nkCurly:
-          r = r.deduplicate
+          r = deduplicate(c.config, r)
 
         # first element is special and will overwrite: branch.sons[i]:
         branch.sons[i] = semCaseBranchSetElem(c, t, r[0], covered)
@@ -584,10 +589,10 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
   var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc})
   if not isOrdinalType(typ):
     localError(c.config, n.info, "selector must be of an ordinal type")
-  elif firstOrd(typ) != 0:
+  elif firstOrd(c.config, typ) != 0:
     localError(c.config, n.info, "low(" & $a.sons[0].sym.name.s &
                                      ") must be 0 for discriminant")
-  elif lengthOrd(typ) > 0x00007FFF:
+  elif lengthOrd(c.config, typ) > 0x00007FFF:
     localError(c.config, n.info, "len($1) must be less than 32768" % a.sons[0].sym.name.s)
   var chckCovered = true
   for i in countup(1, sonsLen(n) - 1):
@@ -603,7 +608,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
     else: illFormedAst(n, c.config)
     delSon(b, sonsLen(b) - 1)
     semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype)
-  if chckCovered and covered != lengthOrd(a.sons[0].typ):
+  if chckCovered and covered != lengthOrd(c.config, a.sons[0].typ):
     localError(c.config, a.info, "not all cases are covered")
   addSon(father, a)
 
@@ -658,7 +663,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
     var length = sonsLen(n)
     var a: PNode
     if father.kind != nkRecList and length>=4: a = newNodeI(nkRecList, n.info)
-    else: a = ast.emptyNode
+    else: a = newNodeI(nkEmpty, n.info)
     if n.sons[length-1].kind != nkEmpty:
       localError(c.config, n.sons[length-1].info, errInitHereNotAllowed)
     var typ: PType
@@ -685,7 +690,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
         localError(c.config, n.sons[i].info, "attempt to redefine: '" & f.name.s & "'")
       if a.kind == nkEmpty: addSon(father, newSymNode(f))
       else: addSon(a, newSymNode(f))
-      styleCheckDef(f)
+      styleCheckDef(c.config, f)
     if a.kind != nkEmpty: addSon(father, a)
   of nkSym:
     # This branch only valid during generic object
@@ -770,7 +775,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
   semRecordNodeAux(c, n.sons[2], check, pos, result.n, result)
   if n.sons[0].kind != nkEmpty:
     # dummy symbol for `pragma`:
-    var s = newSymS(skType, newIdentNode(getIdent("dummy"), n.info), c)
+    var s = newSymS(skType, newIdentNode(getIdent(c.cache, "dummy"), n.info), c)
     s.typ = result
     pragma(c, s, n.sons[0], typePragmas)
   if base == nil and tfInheritable notin result.flags:
@@ -804,8 +809,6 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
   else:
     if sfGenSym notin param.flags: addDecl(c, param)
 
-let typedescId = getIdent"typedesc"
-
 template shouldHaveMeta(t) =
   internalAssert c.config, tfHasMeta in t.flags
   # result.lastSon.flags.incl tfHasMeta
@@ -815,13 +818,13 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
                    info: TLineInfo, anon = false): PType =
   if paramType == nil: return # (e.g. proc return type)
 
-  proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType =
+  proc addImplicitGenericImpl(c: PContext; typeClass: PType, typId: PIdent): PType =
     if genericParams == nil:
       # This happens with anonymous proc types appearing in signatures
       # XXX: we need to lift these earlier
       return
     let finalTypId = if typId != nil: typId
-                     else: getIdent(paramName & ":type")
+                     else: getIdent(c.cache, paramName & ":type")
     # is this a bindOnce type class already present in the param list?
     for i in countup(0, genericParams.len - 1):
       if genericParams.sons[i].sym.name.id == finalTypId.id:
@@ -852,16 +855,16 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
     (if lifted != nil: lifted else: typ)
 
   template addImplicitGeneric(e): untyped =
-    addImplicitGenericImpl(e, paramTypId)
+    addImplicitGenericImpl(c, e, paramTypId)
 
   case paramType.kind:
   of tyAnything:
-    result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil)
+    result = addImplicitGenericImpl(c, newTypeS(tyGenericParam, c), nil)
 
   of tyStatic:
-    # proc(a: expr{string}, b: expr{nkLambda})
-    # overload on compile time values and AST trees
-    if paramType.n != nil: return # this is a concrete type
+    if paramType.base.kind != tyNone and paramType.n != nil:
+      # this is a concrete static value
+      return
     if tfUnresolved in paramType.flags: return # already lifted
     let base = paramType.base.maybeLift
     if base.isMetaType and procKind == skMacro:
@@ -873,14 +876,19 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
     if tfUnresolved notin paramType.flags:
       # naked typedescs are not bindOnce types
       if paramType.base.kind == tyNone and paramTypId != nil and
-         paramTypId.id == typedescId.id: paramTypId = nil
+          paramTypId.id == getIdent(c.cache, "typedesc").id:
+        # XXX Why doesn't this check for tyTypeDesc instead?
+        paramTypId = nil
       result = addImplicitGeneric(
         c.newTypeWithSons(tyTypeDesc, @[paramType.base]))
 
   of tyDistinct:
     if paramType.sonsLen == 1:
       # disable the bindOnce behavior for the type class
-      result = liftingWalk(paramType.sons[0], true)
+      result = liftingWalk(paramType.base, true)
+
+  of tyAlias:
+    result = liftingWalk(paramType.base)
 
   of tySequence, tySet, tyArray, tyOpenArray,
      tyVar, tyLent, tyPtr, tyRef, tyProc:
@@ -997,13 +1005,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
                      prev: PType, kind: TSymKind; isType=false): PType =
   # for historical reasons (code grows) this is invoked for parameter
   # lists too and then 'isType' is false.
-  var cl: IntSet
   checkMinSonsLen(n, 1, c.config)
   result = newProcType(c, n.info, prev)
-  if genericParams != nil and sonsLen(genericParams) == 0:
-    cl = initIntSet()
   var check = initIntSet()
   var counter = 0
+
   for i in countup(1, n.len - 1):
     var a = n.sons[i]
     if a.kind != nkIdentDefs:
@@ -1013,6 +1019,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       # pass over this instantiation:
       if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue
       illFormedAst(a, c.config)
+
     checkMinSonsLen(a, 3, c.config)
     var
       typ: PType = nil
@@ -1021,26 +1028,52 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       length = sonsLen(a)
       hasType = a.sons[length-2].kind != nkEmpty
       hasDefault = a.sons[length-1].kind != nkEmpty
+
     if hasType:
       typ = semParamType(c, a.sons[length-2], constraint)
 
     if hasDefault:
-      def = semExprWithType(c, a.sons[length-1])
-      # check type compatibility between def.typ and typ:
+      def = a[^1]
+      block determineType:
+        if genericParams != nil and genericParams.len > 0:
+          def = semGenericStmt(c, def)
+          if hasUnresolvedArgs(c, def):
+            def.typ = makeTypeFromExpr(c, def.copyTree)
+            break determineType
+
+        def = semExprWithType(c, def, {efDetermineType})
+        if def.referencesAnotherParam(getCurrOwner(c)):
+          def.flags.incl nfDefaultRefsParam
+
       if typ == nil:
         typ = def.typ
-      elif def != nil:
-        # and def.typ != nil and def.typ.kind != tyNone:
+        if typ.kind == tyTypeDesc:
+          # consider a proc such as:
+          # proc takesType(T = int)
+          # a naive analysis may conclude that the proc type is type[int]
+          # which will prevent other types from matching - clearly a very
+          # surprising behavior. We must instead fix the expected type of
+          # the proc to be the unbound typedesc type:
+          typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
+
+      else:
+        # if def.typ != nil and def.typ.kind != tyNone:
         # example code that triggers it:
         # proc sort[T](cmp: proc(a, b: T): int = cmp)
         if not containsGenericType(typ):
+          # check type compatibility between def.typ and typ:
+          def = fitNode(c, typ, def, def.info)
+        elif typ.kind == tyStatic:
+          def = semConstExpr(c, def)
           def = fitNode(c, typ, def, def.info)
+
     if not hasType and not hasDefault:
       if isType: localError(c.config, a.info, "':' expected")
       if kind in {skTemplate, skMacro}:
         typ = newTypeS(tyExpr, c)
     elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid:
       continue
+
     for j in countup(0, length-3):
       var arg = newSymG(skParam, a.sons[j], c)
       if not hasType and not hasDefault and kind notin {skTemplate, skMacro}:
@@ -1056,13 +1089,14 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       arg.position = counter
       arg.constraint = constraint
       inc(counter)
-      if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def)
+      if def != nil and def.kind != nkEmpty:
+        arg.ast = copyTree(def)
       if containsOrIncl(check, arg.name.id):
         localError(c.config, a.sons[j].info, "attempt to redefine: '" & arg.name.s & "'")
       addSon(result.n, newSymNode(arg))
       rawAddSon(result, finalType)
       addParamOrResult(c, arg, kind)
-      if c.config.cmd == cmdPretty: styleCheckDef(a.sons[j].info, arg)
+      styleCheckDef(c.config, a.sons[j].info, arg)
 
   var r: PType
   if n.sons[0].kind != nkEmpty:
@@ -1311,7 +1345,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
     incl dummyParam.flags, sfUsed
     addDecl(c, dummyParam)
 
-  result.n.sons[3] = semConceptBody(c, n[3])
+  result.n[3] = semConceptBody(c, n[3])
   closeScope(c)
 
 proc semProcTypeWithScope(c: PContext, n: PNode,
@@ -1322,7 +1356,7 @@ proc semProcTypeWithScope(c: PContext, n: PNode,
   # start with 'ccClosure', but of course pragmas can overwrite this:
   result.callConv = ccClosure
   # dummy symbol for `pragma`:
-  var s = newSymS(kind, newIdentNode(getIdent("dummy"), n.info), c)
+  var s = newSymS(kind, newIdentNode(getIdent(c.cache, "dummy"), n.info), c)
   s.typ = result
   if n.sons[1].kind != nkEmpty and n.sons[1].len > 0:
     pragma(c, s, n.sons[1], procTypePragmas)
@@ -1345,11 +1379,17 @@ proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) =
 
 proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
   if n.kind == nkType:
-    result = symFromType(n.typ, n.info)
+    result = symFromType(c, n.typ, n.info)
   else:
     localError(c.config, n.info, errTypeExpected)
     result = errorSym(c, n)
 
+proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
+  result = newOrPrevType(tyStatic, prev, c)
+  var base = semTypeNode(c, childNode, nil).skipTypes({tyTypeDesc, tyAlias})
+  result.rawAddSon(base)
+  result.flags.incl tfHasStatic
+
 proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   result = nil
   inc c.inTypeContext
@@ -1393,7 +1433,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     elif n[0].kind notin nkIdentKinds:
       result = semTypeExpr(c, n, prev)
     else:
-      let op = considerQuotedIdent(c.config, n.sons[0])
+      let op = considerQuotedIdent(c, n.sons[0])
       if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
         checkSonsLen(n, 3, c.config)
         var
@@ -1457,7 +1497,11 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     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 mTypeDesc, mTypeTy:
+      result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
+      result.flags.incl tfExplicit
+    of mStaticTy:
+      result = semStaticType(c, n[1], prev)
     of mExpr:
       result = semTypeNode(c, n.sons[0], nil)
       if result != nil:
@@ -1546,11 +1590,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
   of nkVarTy: result = semVarType(c, n, prev)
   of nkDistinctTy: result = semDistinct(c, n, prev)
-  of nkStaticTy:
-    result = newOrPrevType(tyStatic, prev, c)
-    var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
-    result.rawAddSon(base)
-    result.flags.incl tfHasStatic
+  of nkStaticTy: result = semStaticType(c, n[0], prev)
   of nkIteratorTy:
     if n.sonsLen == 0:
       result = newTypeS(tyBuiltInTypeClass, c)
@@ -1585,7 +1625,7 @@ when false:
     result = semTypeNodeInner(c, n, prev)
     instAllTypeBoundOp(c, n.info)
 
-proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
+proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) =
   # source : https://en.wikipedia.org/wiki/Data_structure_alignment#x86
   m.typ.kind = kind
   m.typ.size = size
@@ -1596,10 +1636,10 @@ proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
 
   # FIXME: proper support for clongdouble should be added.
   # long double size can be 8, 10, 12, 16 bytes depending on platform & compiler
-  if targetCPU == cpuI386 and size == 8:
+  if conf.target.targetCPU == cpuI386 and size == 8:
     #on Linux/BSD i386, double are aligned to 4bytes (except with -malign-double)
     if kind in {tyFloat64, tyFloat} and
-       targetOS in {osLinux, osAndroid, osNetbsd, osFreebsd, osOpenbsd, osDragonfly}:
+        conf.target.targetOS in {osLinux, osAndroid, osNetbsd, osFreebsd, osOpenbsd, osDragonfly}:
       m.typ.align = 4
     # on i386, all known compiler, 64bits ints are aligned to 4bytes (except with -malign-double)
     elif kind in {tyInt, tyUInt, tyInt64, tyUInt64}:
@@ -1609,73 +1649,76 @@ proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
 
 proc processMagicType(c: PContext, m: PSym) =
   case m.magic
-  of mInt: setMagicType(m, tyInt, intSize)
-  of mInt8: setMagicType(m, tyInt8, 1)
-  of mInt16: setMagicType(m, tyInt16, 2)
-  of mInt32: setMagicType(m, tyInt32, 4)
-  of mInt64: setMagicType(m, tyInt64, 8)
-  of mUInt: setMagicType(m, tyUInt, intSize)
-  of mUInt8: setMagicType(m, tyUInt8, 1)
-  of mUInt16: setMagicType(m, tyUInt16, 2)
-  of mUInt32: setMagicType(m, tyUInt32, 4)
-  of mUInt64: setMagicType(m, tyUInt64, 8)
-  of mFloat: setMagicType(m, tyFloat, floatSize)
-  of mFloat32: setMagicType(m, tyFloat32, 4)
-  of mFloat64: setMagicType(m, tyFloat64, 8)
-  of mFloat128: setMagicType(m, tyFloat128, 16)
-  of mBool: setMagicType(m, tyBool, 1)
-  of mChar: setMagicType(m, tyChar, 1)
+  of mInt: setMagicType(c.config, m, tyInt, c.config.target.intSize)
+  of mInt8: setMagicType(c.config, m, tyInt8, 1)
+  of mInt16: setMagicType(c.config, m, tyInt16, 2)
+  of mInt32: setMagicType(c.config, m, tyInt32, 4)
+  of mInt64: setMagicType(c.config, m, tyInt64, 8)
+  of mUInt: setMagicType(c.config, m, tyUInt, c.config.target.intSize)
+  of mUInt8: setMagicType(c.config, m, tyUInt8, 1)
+  of mUInt16: setMagicType(c.config, m, tyUInt16, 2)
+  of mUInt32: setMagicType(c.config, m, tyUInt32, 4)
+  of mUInt64: setMagicType(c.config, m, tyUInt64, 8)
+  of mFloat: setMagicType(c.config, m, tyFloat, c.config.target.floatSize)
+  of mFloat32: setMagicType(c.config, m, tyFloat32, 4)
+  of mFloat64: setMagicType(c.config, m, tyFloat64, 8)
+  of mFloat128: setMagicType(c.config, m, tyFloat128, 16)
+  of mBool: setMagicType(c.config, m, tyBool, 1)
+  of mChar: setMagicType(c.config, m, tyChar, 1)
   of mString:
-    setMagicType(m, tyString, ptrSize)
+    setMagicType(c.config, m, tyString, c.config.target.ptrSize)
     rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
   of mCstring:
-    setMagicType(m, tyCString, ptrSize)
+    setMagicType(c.config, m, tyCString, c.config.target.ptrSize)
     rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
-  of mPointer: setMagicType(m, tyPointer, ptrSize)
+  of mPointer: setMagicType(c.config, m, tyPointer, c.config.target.ptrSize)
   of mEmptySet:
-    setMagicType(m, tySet, 1)
+    setMagicType(c.config, m, tySet, 1)
     rawAddSon(m.typ, newTypeS(tyEmpty, c))
-  of mIntSetBaseType: setMagicType(m, tyRange, intSize)
-  of mNil: setMagicType(m, tyNil, ptrSize)
+  of mIntSetBaseType: setMagicType(c.config, m, tyRange, c.config.target.intSize)
+  of mNil: setMagicType(c.config, m, tyNil, c.config.target.ptrSize)
   of mExpr:
     if m.name.s == "auto":
-      setMagicType(m, tyAnything, 0)
+      setMagicType(c.config, m, tyAnything, 0)
     else:
-      setMagicType(m, tyExpr, 0)
+      setMagicType(c.config, m, tyExpr, 0)
       if m.name.s == "expr": m.typ.flags.incl tfOldSchoolExprStmt
   of mStmt:
-    setMagicType(m, tyStmt, 0)
+    setMagicType(c.config, m, tyStmt, 0)
     if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt
-  of mTypeDesc:
-    setMagicType(m, tyTypeDesc, 0)
+  of mTypeDesc, mType:
+    setMagicType(c.config, m, tyTypeDesc, 0)
+    rawAddSon(m.typ, newTypeS(tyNone, c))
+  of mStatic:
+    setMagicType(c.config, m, tyStatic, 0)
     rawAddSon(m.typ, newTypeS(tyNone, c))
   of mVoidType:
-    setMagicType(m, tyVoid, 0)
+    setMagicType(c.config, m, tyVoid, 0)
   of mArray:
-    setMagicType(m, tyArray, 0)
+    setMagicType(c.config, m, tyArray, 0)
   of mOpenArray:
-    setMagicType(m, tyOpenArray, 0)
+    setMagicType(c.config, m, tyOpenArray, 0)
   of mVarargs:
-    setMagicType(m, tyVarargs, 0)
+    setMagicType(c.config, m, tyVarargs, 0)
   of mRange:
-    setMagicType(m, tyRange, 0)
+    setMagicType(c.config, m, tyRange, 0)
     rawAddSon(m.typ, newTypeS(tyNone, c))
   of mSet:
-    setMagicType(m, tySet, 0)
+    setMagicType(c.config, m, tySet, 0)
   of mSeq:
-    setMagicType(m, tySequence, 0)
+    setMagicType(c.config, m, tySequence, 0)
   of mOpt:
-    setMagicType(m, tyOpt, 0)
+    setMagicType(c.config, m, tyOpt, 0)
   of mOrdinal:
-    setMagicType(m, tyOrdinal, 0)
+    setMagicType(c.config, m, tyOrdinal, 0)
     rawAddSon(m.typ, newTypeS(tyNone, c))
   of mPNimrodNode:
     incl m.typ.flags, tfTriggersCompileTime
   of mException: discard
   of mBuiltinType:
     case m.name.s
-    of "lent": setMagicType(m, tyLent, ptrSize)
-    of "sink": setMagicType(m, tySink, 0)
+    of "lent": setMagicType(c.config, m, tyLent, c.config.target.ptrSize)
+    of "sink": setMagicType(c.config, m, tySink, 0)
     else: localError(c.config, m.info, errTypeExpected)
   else: localError(c.config, m.info, errTypeExpected)
 
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 61d92bb19..22ea09af1 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -9,7 +9,8 @@
 
 # This module does the instantiation of generic types.
 
-import ast, astalgo, msgs, types, magicsys, semdata, renderer, options
+import ast, astalgo, msgs, types, magicsys, semdata, renderer, options,
+  lineinfos
 
 const
   tfInstClearedFlags = {tfHasMeta, tfUnresolved}
@@ -27,7 +28,7 @@ proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
     localError(conf, info, "invalid pragma: acyclic")
   elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
     localError(conf, info, "type 'var var' is not allowed")
-  elif computeSize(t) == szIllegalRecursion:
+  elif computeSize(conf, t) == szIllegalRecursion:
     localError(conf, info,  "illegal recursion in type '" & typeToString(t) & "'")
   when false:
     if t.kind == tyObject and t.sons[0] != nil:
@@ -143,17 +144,6 @@ proc isTypeParam(n: PNode): bool =
          (n.sym.kind == skGenericParam or
            (n.sym.kind == skType and sfFromGeneric in n.sym.flags))
 
-proc hasGenericArguments*(n: PNode): bool =
-  if n.kind == nkSym:
-    return n.sym.kind == skGenericParam or
-           tfInferrableStatic in n.sym.typ.flags or
-           (n.sym.kind == skType and
-            n.sym.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
-  else:
-    for i in 0..<n.safeLen:
-      if hasGenericArguments(n.sons[i]): return true
-    return false
-
 proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
   # This is needed for tgenericshardcases
   # It's possible that a generic param will be used in a proc call to a
@@ -230,6 +220,17 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
   # symbol is not our business:
   if cl.owner != nil and s.owner != cl.owner:
     return s
+
+  # XXX: Bound symbols in default parameter expressions may reach here.
+  # We cannot process them, becase `sym.n` may point to a proc body with
+  # cyclic references that will lead to an infinite recursion.
+  # Perhaps we should not use a black-list here, but a whitelist instead
+  # (e.g. skGenericParam and skType).
+  # Note: `s.magic` may be `mType` in an example such as:
+  # proc foo[T](a: T, b = myDefault(type(a)))
+  if s.kind == skProc or s.magic != mNone:
+    return s
+
   #result = PSym(idTableGet(cl.symMap, s))
   #if result == nil:
   result = copySym(s, false)
@@ -277,7 +278,8 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
   # is difficult to handle:
   const eqFlags = eqTypeFlags + {tfGcSafe}
   var body = t.sons[0]
-  if body.kind != tyGenericBody: internalError(cl.c.config, cl.info, "no generic body")
+  if body.kind != tyGenericBody:
+    internalError(cl.c.config, cl.info, "no generic body")
   var header: PType = t
   # search for some instantiation here:
   if cl.allowMetaTypes:
@@ -568,35 +570,35 @@ proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode;
   var typeMap = initLayeredTypeMap(pt)
   var cl = initTypeVars(p, addr(typeMap), n.info, owner)
   cl.allowMetaTypes = allowMetaTypes
-  pushInfoContext(n.info)
+  pushInfoContext(p.config, n.info)
   result = replaceTypeVarsN(cl, n)
-  popInfoContext()
+  popInfoContext(p.config)
 
 proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode;
                             original, new: PSym): PNode =
   var typeMap = initLayeredTypeMap(pt)
   var cl = initTypeVars(p, addr(typeMap), n.info, original)
   idTablePut(cl.symMap, original, new)
-  pushInfoContext(n.info)
+  pushInfoContext(p.config, n.info)
   result = replaceTypeVarsN(cl, n)
-  popInfoContext()
+  popInfoContext(p.config)
 
 proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
                            t: PType): PType =
   var typeMap = initLayeredTypeMap(pt)
   var cl = initTypeVars(p, addr(typeMap), info, nil)
-  pushInfoContext(info)
+  pushInfoContext(p.config, info)
   result = replaceTypeVarsT(cl, t)
-  popInfoContext()
+  popInfoContext(p.config)
 
 proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo,
                                  t: PType): PType =
   var typeMap = initLayeredTypeMap(pt)
   var cl = initTypeVars(p, addr(typeMap), info, nil)
   cl.allowMetaTypes = true
-  pushInfoContext(info)
+  pushInfoContext(p.config, info)
   result = replaceTypeVarsT(cl, t)
-  popInfoContext()
+  popInfoContext(p.config)
 
 template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode,
                                t: PType): untyped =
diff --git a/compiler/service.nim b/compiler/service.nim
index f1a988ae5..0e82c03f8 100644
--- a/compiler/service.nim
+++ b/compiler/service.nim
@@ -11,7 +11,7 @@
 
 import
   times, commands, options, msgs, nimconf,
-  extccomp, strutils, os, platform, parseopt, idents, configuration
+  extccomp, strutils, os, platform, parseopt, idents, lineinfos
 
 when useCaas:
   import net
@@ -26,25 +26,6 @@ var
     # in caas mode, the list of defines and options will be given at start-up?
     # it's enough to check that the previous compilation command is the same?
 
-proc processCmdLine*(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
-  var p = parseopt.initOptParser(cmd)
-  var argsCount = 0
-  while true:
-    parseopt.next(p)
-    case p.kind
-    of cmdEnd: break
-    of cmdLongoption, cmdShortOption:
-      if p.key == " ":
-        p.key = "-"
-        if processArgument(pass, p, argsCount, config): break
-      else:
-        processSwitch(pass, p, config)
-    of cmdArgument:
-      if processArgument(pass, p, argsCount, config): break
-  if pass == passCmd2:
-    if optRun notin config.globalOptions and config.arguments.len > 0 and config.command.normalize != "run":
-      rawMessage(config, errGenerated, errArgsNeedRunOption)
-
 proc serve*(cache: IdentCache; action: proc (cache: IdentCache){.nimcall.}; config: ConfigRef) =
   template execute(cmd) =
     curCaasCmd = cmd
@@ -71,7 +52,7 @@ proc serve*(cache: IdentCache; action: proc (cache: IdentCache){.nimcall.}; conf
       var inp = "".TaintedString
       server.listen()
       var stdoutSocket = newSocket()
-      msgs.writelnHook = proc (line: string) =
+      config.writelnHook = proc (line: string) =
         stdoutSocket.send(line & "\c\L")
 
       while true:
diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim
index 46b83c386..0bf2b8459 100644
--- a/compiler/sighashes.nim
+++ b/compiler/sighashes.nim
@@ -12,7 +12,7 @@
 import ast, md5, tables, ropes
 from hashes import Hash
 from astalgo import debug
-from types import typeToString, preferDesc
+import types
 from strutils import startsWith, contains
 
 when false:
@@ -148,19 +148,23 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
   of tyGenericInvocation:
     for i in countup(0, sonsLen(t) - 1):
       c.hashType t.sons[i], flags
-    return
   of tyDistinct:
     if CoType in flags:
       c.hashType t.lastSon, flags
     else:
       c.hashSym(t.sym)
-    return
-  of tyAlias, tySink, tyGenericInst, tyUserTypeClasses:
+  of tyGenericInst:
+    if sfInfixCall in t.base.sym.flags:
+      # This is an imported C++ generic type.
+      # We cannot trust the `lastSon` to hold a properly populated and unique
+      # value for each instantiation, so we hash the generic parameters here:
+      let normalizedType = t.skipGenericAlias
+      for i in 0 .. normalizedType.len - 2:
+        c.hashType t.sons[i], flags
+    else:
+      c.hashType t.lastSon, flags
+  of tyAlias, tySink, tyUserTypeClasses:
     c.hashType t.lastSon, flags
-    return
-  else:
-    discard
-  case t.kind
   of tyBool, tyChar, tyInt..tyUInt64:
     # no canonicalization for integral types, so that e.g. ``pid_t`` is
     # produced instead of ``NI``:
@@ -168,11 +172,12 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
     if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}:
       c.hashSym(t.sym)
   of tyObject, tyEnum:
-    c &= char(t.kind)
     if t.typeInst != nil:
       assert t.typeInst.kind == tyGenericInst
-      for i in countup(1, sonsLen(t.typeInst) - 2):
+      for i in countup(0, sonsLen(t.typeInst) - 2):
         c.hashType t.typeInst.sons[i], flags
+      return
+    c &= char(t.kind)
     # Every cyclic type in Nim need to be constructed via some 't.sym', so this
     # is actually safe without an infinite recursion check:
     if t.sym != nil:
@@ -184,18 +189,19 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
         c.hashTypeSym(t.sym)
       else:
         c.hashSym(t.sym)
-      if sfAnon in t.sym.flags:
+      if {sfAnon, sfGenSym} * t.sym.flags != {}:
         # generated object names can be identical, so we need to
         # disambiguate furthermore by hashing the field types and names:
         # mild hack to prevent endless recursions (makes nimforum compile again):
-        excl t.sym.flags, sfAnon
+        let oldFlags = t.sym.flags
+        t.sym.flags = t.sym.flags - {sfAnon, sfGenSym}
         let n = t.n
         for i in 0 ..< n.len:
           assert n[i].kind == nkSym
           let s = n[i].sym
           c.hashSym s
           c.hashType s.typ, flags
-        incl t.sym.flags, sfAnon
+        t.sym.flags = oldFlags
     else:
       c &= t.id
     if t.len > 0 and t.sons[0] != nil:
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index fcfdda8bb..84e59349e 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -13,9 +13,9 @@
 import
   intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
   magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees,
-  nimfix.pretty
+  linter, lineinfos
 
-when not defined(noDocgen):
+when defined(booting) or defined(nimsuggest):
   import docgen
 
 type
@@ -253,7 +253,7 @@ proc complexDisambiguation(a, b: PType): int =
     result = x - y
 
 proc writeMatches*(c: TCandidate) =
-  echo "Candidate '", c.calleeSym.name.s, "' at ", c.calleeSym.info
+  echo "Candidate '", c.calleeSym.name.s, "' at ", c.c.config $ c.calleeSym.info
   echo "  exact matches: ", c.exactMatches
   echo "  generic matches: ", c.genericMatches
   echo "  subtype matches: ", c.subtypeMatches
@@ -376,8 +376,10 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
     if k == f.kind: result = isSubrange
     elif k == tyInt and f.kind in {tyRange, tyInt8..tyInt64,
                                    tyUInt..tyUInt64} and
-        isIntLit(ab) and ab.n.intVal >= firstOrd(f) and
-                         ab.n.intVal <= lastOrd(f):
+        isIntLit(ab) and ab.n.intVal >= firstOrd(nil, f) and
+                         ab.n.intVal <= lastOrd(nil, f):
+      # passing 'nil' to firstOrd/lastOrd here as type checking rules should
+      # not depent on the target integer size configurations!
       # integer literal in the proper range; we want ``i16 + 4`` to stay an
       # ``int16`` operation so we declare the ``4`` pseudo-equal to int16
       result = isFromIntLit
@@ -387,8 +389,10 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
       result = isConvertible
     elif a.kind == tyRange and a.sons[0].kind in {tyInt..tyInt64,
                                                   tyUInt8..tyUInt32} and
-                         a.n[0].intVal >= firstOrd(f) and
-                         a.n[1].intVal <= lastOrd(f):
+                         a.n[0].intVal >= firstOrd(nil, f) and
+                         a.n[1].intVal <= lastOrd(nil, f):
+      # passing 'nil' to firstOrd/lastOrd here as type checking rules should
+      # not depent on the target integer size configurations!
       result = isConvertible
     else: result = isNone
     #elif f.kind == tyInt and k in {tyInt..tyInt32}: result = isIntConv
@@ -633,20 +637,22 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
   else: discard
 
 proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
-  let
-    a0 = firstOrd(a)
-    a1 = lastOrd(a)
-    f0 = firstOrd(f)
-    f1 = lastOrd(f)
-  if a0 == f0 and a1 == f1:
-    result = isEqual
-  elif a0 >= f0 and a1 <= f1:
-    result = isConvertible
-  elif a0 <= f1 and f0 <= a1:
-    # X..Y and C..D overlap iff (X <= D and C <= Y)
-    result = isConvertible
-  else:
-    result = isNone
+  template checkRange[T](a0, a1, f0, f1: T): TTypeRelation = 
+    if a0 == f0 and a1 == f1:
+      isEqual
+    elif a0 >= f0 and a1 <= f1:
+      isConvertible
+    elif a0 <= f1 and f0 <= a1:
+      # X..Y and C..D overlap iff (X <= D and C <= Y)
+      isConvertible
+    else:
+      isNone
+  
+  if f.isOrdinalType: 
+    checkRange(firstOrd(nil, a), lastOrd(nil, a), firstOrd(nil, f), lastOrd(nil, f))
+  else: 
+    checkRange(firstFloat(a), lastFloat(a), firstFloat(f), lastFloat(f))
+    
 
 proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
   var
@@ -718,7 +724,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
       addDecl(c, param)
 
   var
-    oldWriteHook: type(writelnHook)
+    oldWriteHook: type(m.c.config.writelnHook)
     diagnostics: seq[string]
     errorPrefix: string
     flags: TExprFlags = {}
@@ -726,12 +732,12 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
                          sfExplain in typeClass.sym.flags
 
   if collectDiagnostics:
-    oldWriteHook = writelnHook
+    oldWriteHook = m.c.config.writelnHook
     # XXX: we can't write to m.diagnostics directly, because
     # Nim doesn't support capturing var params in closures
     diagnostics = @[]
     flags = {efExplain}
-    writelnHook = proc (s: string) =
+    m.c.config.writelnHook = proc (s: string) =
       if errorPrefix == nil: errorPrefix = typeClass.sym.name.s & ":"
       let msg = s.replace("Error:", errorPrefix)
       if oldWriteHook != nil: oldWriteHook msg
@@ -740,7 +746,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
   var checkedBody = c.semTryExpr(c, body.copyTree, flags)
 
   if collectDiagnostics:
-    writelnHook = oldWriteHook
+    m.c.config.writelnHook = oldWriteHook
     for msg in diagnostics:
       m.diagnostics.safeAdd msg
       m.diagnosticsEnabled = true
@@ -764,11 +770,11 @@ proc shouldSkipDistinct(m: TCandidate; rules: PNode, callIdent: PIdent): bool =
   # XXX This is bad as 'considerQuotedIdent' can produce an error!
   if rules.kind == nkWith:
     for r in rules:
-      if considerQuotedIdent(m.c.graph.config, r) == callIdent: return true
+      if considerQuotedIdent(m.c, r) == callIdent: return true
     return false
   else:
     for r in rules:
-      if considerQuotedIdent(m.c.graph.config, r) == callIdent: return false
+      if considerQuotedIdent(m.c, r) == callIdent: return false
     return true
 
 proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType =
@@ -887,17 +893,17 @@ proc inferStaticsInRange(c: var TCandidate,
     if inferStaticParam(c, exp, rhs):
       return isGeneric
     else:
-      failureToInferStaticParam(c.c.graph.config, exp)
+      failureToInferStaticParam(c.c.config, exp)
 
   if lowerBound.kind == nkIntLit:
     if upperBound.kind == nkIntLit:
-      if lengthOrd(concrete) == upperBound.intVal - lowerBound.intVal + 1:
+      if lengthOrd(c.c.config, concrete) == upperBound.intVal - lowerBound.intVal + 1:
         return isGeneric
       else:
         return isNone
-    doInferStatic(upperBound, lengthOrd(concrete) + lowerBound.intVal - 1)
+    doInferStatic(upperBound, lengthOrd(c.c.config, concrete) + lowerBound.intVal - 1)
   elif upperBound.kind == nkIntLit:
-    doInferStatic(lowerBound, upperBound.intVal + 1 - lengthOrd(concrete))
+    doInferStatic(lowerBound, upperBound.intVal + 1 - lengthOrd(c.c.config, concrete))
 
 template subtypeCheck() =
   if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent}:
@@ -1085,8 +1091,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
              else: isNone
 
   of tyAnything:
-    return if f.kind == tyAnything: isGeneric
-           else: isNone
+    if f.kind == tyAnything: return isGeneric
+    else: return isNone
 
   of tyUserTypeClass, tyUserTypeClassInst:
     if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4:
@@ -1176,7 +1182,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
       elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic:
         return inferStaticsInRange(c, aRange, f)
       else:
-        if lengthOrd(fRange) != lengthOrd(aRange):
+        if lengthOrd(c.c.config, fRange) != lengthOrd(c.c.config, aRange):
           result = isNone
     else: discard
   of tyOpenArray, tyVarargs:
@@ -1342,7 +1348,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
       if a.len == 1:
         let pointsTo = a.sons[0].skipTypes(abstractInst)
         if pointsTo.kind == tyChar: result = isConvertible
-        elif pointsTo.kind == tyArray and firstOrd(pointsTo.sons[0]) == 0 and
+        elif pointsTo.kind == tyArray and firstOrd(nil, pointsTo.sons[0]) == 0 and
             skipTypes(pointsTo.sons[0], {tyRange}).kind in {tyInt..tyInt64} and
             pointsTo.sons[1].kind == tyChar:
           result = isConvertible
@@ -1662,13 +1668,17 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
     let prev = PType(idTableGet(c.bindings, f))
     if prev == nil:
       if aOrig.kind == tyStatic:
-        result = typeRel(c, f.lastSon, a)
-        if result != isNone and f.n != nil:
-          if not exprStructuralEquivalent(f.n, aOrig.n):
-            result = isNone
+        if f.base.kind != tyNone:
+          result = typeRel(c, f.base, a)
+          if result != isNone and f.n != nil:
+            if not exprStructuralEquivalent(f.n, aOrig.n):
+              result = isNone
+        else:
+          result = isGeneric
         if result != isNone: put(c, f, aOrig)
       elif aOrig.n != nil and aOrig.n.typ != nil:
-        result = typeRel(c, f.lastSon, aOrig.n.typ)
+        result = if f.base.kind != tyNone: typeRel(c, f.lastSon, aOrig.n.typ)
+                 else: isGeneric
         if result != isNone:
           var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ])
           boundType.n = aOrig.n
@@ -1703,9 +1713,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
       # proc foo(T: typedesc, x: T)
       # when `f` is an unresolved typedesc, `a` could be any
       # type, so we should not perform this check earlier
-      if a.kind != tyTypeDesc: return isNone
-
-      if f.base.kind == tyNone:
+      if a.kind != tyTypeDesc:
+        if a.kind == tyGenericParam and tfWildcard in a.flags:
+          # TODO: prevent `a` from matching as a wildcard again
+          result = isGeneric
+        else:
+          result = isNone
+      elif f.base.kind == tyNone:
         result = isGeneric
       else:
         result = typeRel(c, f.base, a.base)
@@ -1774,7 +1788,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
   else:
     result.typ = f
   if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv")
-  addSon(result, ast.emptyNode)
+  addSon(result, c.graph.emptyNode)
   addSon(result, arg)
 
 proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
@@ -1993,7 +2007,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
       return arg
     elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList:
       # lift do blocks without params to lambdas
-      let lifted = c.semExpr(c, newProcNode(nkDo, argOrig.info, argOrig), {})
+      let p = c.graph
+      let lifted = c.semExpr(c, newProcNode(nkDo, argOrig.info, body = argOrig,
+          params = p.emptyNode, name = p.emptyNode, pattern = p.emptyNode,
+          genericParams = p.emptyNode, pragmas = p.emptyNode, exceptions = p.emptyNode), {})
       if f.kind == tyBuiltInTypeClass:
         inc m.genericMatches
         put(m, f, lifted.typ)
@@ -2127,10 +2144,10 @@ proc prepareOperand(c: PContext; a: PNode): PNode =
     result = a
     considerGenSyms(c, result)
 
-proc prepareNamedParam(a: PNode; conf: ConfigRef) =
+proc prepareNamedParam(a: PNode; c: PContext) =
   if a.sons[0].kind != nkIdent:
     var info = a.sons[0].info
-    a.sons[0] = newIdentNode(considerQuotedIdent(conf, a.sons[0]), info)
+    a.sons[0] = newIdentNode(considerQuotedIdent(c, a.sons[0]), info)
 
 proc arrayConstr(c: PContext, n: PNode): PType =
   result = newTypeS(tyArray, c)
@@ -2195,7 +2212,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
     elif n.sons[a].kind == nkExprEqExpr:
       # named param
       # check if m.callee has such a param:
-      prepareNamedParam(n.sons[a], c.config)
+      prepareNamedParam(n.sons[a], c)
       if n.sons[a].sons[0].kind != nkIdent:
         localError(c.config, n.sons[a].info, "named parameter has to be an identifier")
         m.state = csNoMatch
@@ -2346,12 +2363,22 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
           m.firstMismatch = f
           break
       else:
-        # use default value:
+        if formal.ast.kind == nkEmpty:
+          # The default param value is set to empty in `instantiateProcType`
+          # when the type of the default expression doesn't match the type
+          # of the instantiated proc param:
+          localError(c.config, m.call.info,
+                     ("The default parameter '$1' has incompatible type " &
+                      "with the explicitly requested proc instantiation") %
+                      formal.name.s)
+        if nfDefaultRefsParam in formal.ast.flags:
+          m.call.flags.incl nfDefaultRefsParam
         var def = copyTree(formal.ast)
         if def.kind == nkNilLit:
           def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c)
         if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}:
           put(m, formal.typ, def.typ)
+        def.flags.incl nfDefaultParam
         setSon(m.call, formal.position + 1, def)
     inc(f)
   # forget all inferred types if the overload matching failed
@@ -2359,14 +2386,18 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
     for t in m.inferredTypes:
       if t.sonsLen > 1: t.sons.setLen 1
 
-proc argtypeMatches*(c: PContext, f, a: PType): bool =
+proc argtypeMatches*(c: PContext, f, a: PType, fromHlo = false): bool =
   var m: TCandidate
   initCandidate(c, m, f)
-  let res = paramTypesMatch(m, f, a, ast.emptyNode, nil)
+  let res = paramTypesMatch(m, f, a, c.graph.emptyNode, nil)
   #instantiateGenericConverters(c, res, m)
   # XXX this is used by patterns.nim too; I think it's better to not
   # instantiate generic converters for that
-  result = res != nil
+  if not fromHlo:
+    res != nil
+  else:
+    # pattern templates do not allow for conversions except from int literal
+    res != nil and m.convMatches == 0 and m.intConvMatches in [0, 256]
 
 proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
                       op: TTypeAttachedOp; col: int): PSym {.procvar.} =
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 23aecfa71..a21d64338 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -32,7 +32,7 @@
 
 # included from sigmatch.nim
 
-import algorithm, prefixmatches, configuration
+import algorithm, prefixmatches, lineinfos
 from wordrecg import wDeprecated
 
 when defined(nimsuggest):
@@ -41,31 +41,6 @@ when defined(nimsuggest):
 const
   sep = '\t'
 
-type
-  Suggest* = ref object
-    section*: IdeCmd
-    qualifiedPath*: seq[string]
-    name*: PIdent                # not used beyond sorting purposes; name is also
-                                 # part of 'qualifiedPath'
-    filePath*: string
-    line*: int                   # Starts at 1
-    column*: int                 # Starts at 0
-    doc*: string           # Not escaped (yet)
-    symkind*: TSymKind
-    forth*: string               # type
-    quality*: range[0..100]   # matching quality
-    isGlobal*: bool # is a global variable
-    contextFits*: bool # type/non-type context matches
-    prefix*: PrefixMatch
-    scope*, localUsages*, globalUsages*: int # more usages is better
-    tokenLen*: int
-  Suggestions* = seq[Suggest]
-
-var
-  suggestionResultHook*: proc (result: Suggest) {.closure.}
-  suggestVersion*: int
-  suggestMaxResults* = 10_000
-
 #template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n"
 
 template origModuleName(m: PSym): string = m.name.s
@@ -104,7 +79,7 @@ proc cmpSuggestions(a, b: Suggest): int =
   cf globalUsages
   # if all is equal, sort alphabetically for deterministic output,
   # independent of hashing order:
-  result = cmp(a.name.s, b.name.s)
+  result = cmp(a.name[], b.name[])
 
 proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo;
                   quality: range[0..100]; prefix: PrefixMatch;
@@ -117,14 +92,14 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info
   result.prefix = prefix
   result.contextFits = inTypeContext == (s.kind in {skType, skGenericParam})
   result.scope = scope
-  result.name = s.name
+  result.name = addr s.name.s
   when defined(nimsuggest):
     result.globalUsages = s.allUsages.len
     var c = 0
     for u in s.allUsages:
       if u.fileIndex == info.fileIndex: inc c
     result.localUsages = c
-  result.symkind = s.kind
+  result.symkind = byte s.kind
   if optIdeTerse notin conf.globalOptions:
     result.qualifiedPath = @[]
     if not isLocal and s.kind != skModule:
@@ -140,23 +115,24 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info
       result.forth = typeToString(s.typ)
     else:
       result.forth = ""
-    when not defined(noDocgen):
+    when defined(nimsuggest) and not defined(noDocgen):
       result.doc = s.extractDocComment
   let infox = if section in {ideUse, ideHighlight, ideOutline}: info else: s.info
-  result.filePath = toFullPath(infox)
+  result.filePath = toFullPath(conf, infox)
   result.line = toLinenumber(infox)
   result.column = toColumn(infox)
+  result.version = conf.suggestVersion
 
 proc `$`*(suggest: Suggest): string =
   result = $suggest.section
   result.add(sep)
   if suggest.section == ideHighlight:
-    if suggest.symkind == skVar and suggest.isGlobal:
+    if suggest.symkind.TSymKind == skVar and suggest.isGlobal:
       result.add("skGlobalVar")
-    elif suggest.symkind == skLet and suggest.isGlobal:
+    elif suggest.symkind.TSymKind == skLet and suggest.isGlobal:
       result.add("skGlobalLet")
     else:
-      result.add($suggest.symkind)
+      result.add($suggest.symkind.TSymKind)
     result.add(sep)
     result.add($suggest.line)
     result.add(sep)
@@ -164,9 +140,9 @@ proc `$`*(suggest: Suggest): string =
     result.add(sep)
     result.add($suggest.tokenLen)
   else:
-    result.add($suggest.symkind)
+    result.add($suggest.symkind.TSymKind)
     result.add(sep)
-    if suggest.qualifiedPath != nil:
+    if suggest.qualifiedPath.len != 0:
       result.add(suggest.qualifiedPath.join("."))
     result.add(sep)
     result.add(suggest.forth)
@@ -177,20 +153,20 @@ proc `$`*(suggest: Suggest): string =
     result.add(sep)
     result.add($suggest.column)
     result.add(sep)
-    when not defined(noDocgen):
+    when defined(nimsuggest) and not defined(noDocgen):
       result.add(suggest.doc.escape)
-    if suggestVersion == 0:
+    if suggest.version == 0:
       result.add(sep)
       result.add($suggest.quality)
       if suggest.section == ideSug:
         result.add(sep)
         result.add($suggest.prefix)
 
-proc suggestResult(s: Suggest) =
-  if not isNil(suggestionResultHook):
-    suggestionResultHook(s)
+proc suggestResult(conf: ConfigRef; s: Suggest) =
+  if not isNil(conf.suggestionResultHook):
+    conf.suggestionResultHook(s)
   else:
-    suggestWriteln($s)
+    conf.suggestWriteln($s)
 
 proc produceOutput(a: var Suggestions; conf: ConfigRef) =
   if conf.ideCmd in {ideSug, ideCon}:
@@ -198,13 +174,13 @@ proc produceOutput(a: var Suggestions; conf: ConfigRef) =
   when defined(debug):
     # debug code
     writeStackTrace()
-  if a.len > suggestMaxResults: a.setLen(suggestMaxResults)
-  if not isNil(suggestionResultHook):
+  if a.len > conf.suggestMaxResults: a.setLen(conf.suggestMaxResults)
+  if not isNil(conf.suggestionResultHook):
     for s in a:
-      suggestionResultHook(s)
+      conf.suggestionResultHook(s)
   else:
     for s in a:
-      suggestWriteln($s)
+      conf.suggestWriteln($s)
 
 proc filterSym(s: PSym; prefix: PNode; res: var PrefixMatch): bool {.inline.} =
   proc prefixMatch(s: PSym; n: PNode): PrefixMatch =
@@ -340,14 +316,14 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
   var typ = n.typ
   var pm: PrefixMatch
   when defined(nimsuggest):
-    if n.kind == nkSym and n.sym.kind == skError and suggestVersion == 0:
+    if n.kind == nkSym and n.sym.kind == skError and c.config.suggestVersion == 0:
       # consider 'foo.|' where 'foo' is some not imported module.
-      let fullPath = findModule(c.config, n.sym.name.s, n.info.toFullPath)
+      let fullPath = findModule(c.config, n.sym.name.s, toFullPath(c.config, n.info))
       if fullPath.len == 0:
         # error: no known module name:
         typ = nil
       else:
-        let m = gImportModule(c.graph, c.module, fileInfoIdx(c.config, fullpath), c.cache)
+        let m = c.graph.importModuleCallback(c.graph, c.module, fileInfoIdx(c.config, fullpath))
         if m == nil: typ = nil
         else:
           for it in items(n.sym.tab):
@@ -397,17 +373,17 @@ type
   TCheckPointResult* = enum
     cpNone, cpFuzzy, cpExact
 
-proc inCheckpoint*(current: TLineInfo): TCheckPointResult =
-  if current.fileIndex == gTrackPos.fileIndex:
-    if current.line == gTrackPos.line and
-        abs(current.col-gTrackPos.col) < 4:
+proc inCheckpoint*(current, trackPos: TLineInfo): TCheckPointResult =
+  if current.fileIndex == trackPos.fileIndex:
+    if current.line == trackPos.line and
+        abs(current.col-trackPos.col) < 4:
       return cpExact
-    if current.line >= gTrackPos.line:
+    if current.line >= trackPos.line:
       return cpFuzzy
 
-proc isTracked*(current: TLineInfo, tokenLen: int): bool =
-  if current.fileIndex==gTrackPos.fileIndex and current.line==gTrackPos.line:
-    let col = gTrackPos.col
+proc isTracked*(current, trackPos: TLineInfo, tokenLen: int): bool =
+  if current.fileIndex==trackPos.fileIndex and current.line==trackPos.line:
+    let col = trackPos.col
     if col >= current.col and col <= current.col+tokenLen-1:
       return true
 
@@ -425,30 +401,27 @@ when defined(nimsuggest):
       if infoB.infoToInt == infoAsInt: return
     s.allUsages.add(info)
 
-var
-  lastLineInfo*: TLineInfo # XXX global here
-
 proc findUsages(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
-  if suggestVersion == 1:
-    if usageSym == nil and isTracked(info, s.name.s.len):
+  if conf.suggestVersion == 1:
+    if usageSym == nil and isTracked(info, conf.m.trackPos, s.name.s.len):
       usageSym = s
-      suggestResult(symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
     elif s == usageSym:
-      if lastLineInfo != info:
-        suggestResult(symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
-      lastLineInfo = info
+      if conf.lastLineInfo != info:
+        suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0))
+      conf.lastLineInfo = info
 
 when defined(nimsuggest):
   proc listUsages*(conf: ConfigRef; s: PSym) =
     #echo "usages ", len(s.allUsages)
     for info in s.allUsages:
       let x = if info == s.info and info.col == s.info.col: ideDef else: ideUse
-      suggestResult(symToSuggest(conf, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(conf, symToSuggest(conf, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0))
 
 proc findDefinition(conf: ConfigRef; info: TLineInfo; s: PSym) =
   if s.isNil: return
-  if isTracked(info, s.name.s.len):
-    suggestResult(symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
+  if isTracked(info, conf.m.trackPos, s.name.s.len):
+    suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
     suggestQuit()
 
 proc ensureIdx[T](x: var T, y: int) =
@@ -460,7 +433,7 @@ proc ensureSeq[T](x: var seq[T]) =
 proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} =
   ## misnamed: should be 'symDeclared'
   when defined(nimsuggest):
-    if suggestVersion == 0:
+    if conf.suggestVersion == 0:
       if s.allUsages.isNil:
         s.allUsages = @[info]
       else:
@@ -471,14 +444,14 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym;
     elif conf.ideCmd == ideDef:
       findDefinition(conf, info, s)
     elif conf.ideCmd == ideDus and s != nil:
-      if isTracked(info, s.name.s.len):
-        suggestResult(symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
+      if isTracked(info, conf.m.trackPos, s.name.s.len):
+        suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0))
       findUsages(conf, info, s, usageSym)
-    elif conf.ideCmd == ideHighlight and info.fileIndex == gTrackPos.fileIndex:
-      suggestResult(symToSuggest(conf, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
-    elif conf.ideCmd == ideOutline and info.fileIndex == gTrackPos.fileIndex and
+    elif conf.ideCmd == ideHighlight and info.fileIndex == conf.m.trackPos.fileIndex:
+      suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0))
+    elif conf.ideCmd == ideOutline and info.fileIndex == conf.m.trackPos.fileIndex and
         isDecl:
-      suggestResult(symToSuggest(conf, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
+      suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
 
 proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) =
   if s.kind in routineKinds:
@@ -510,7 +483,7 @@ proc safeSemExpr*(c: PContext, n: PNode): PNode =
   try:
     result = c.semExpr(c, n)
   except ERecoverableError:
-    result = ast.emptyNode
+    result = c.graph.emptyNode
 
 proc sugExpr(c: PContext, n: PNode, outputs: var Suggestions) =
   if n.kind == nkDotExpr:
@@ -519,14 +492,14 @@ proc sugExpr(c: PContext, n: PNode, outputs: var Suggestions) =
     # of the next line, so we check the 'field' is actually on the same
     # line as the object to prevent this from happening:
     let prefix = if n.len == 2 and n[1].info.line == n[0].info.line and
-       not gTrackPosAttached: n[1] else: nil
+       not c.config.m.trackPosAttached: n[1] else: nil
     suggestFieldAccess(c, obj, prefix, outputs)
 
     #if optIdeDebug in gGlobalOptions:
     #  echo "expression ", renderTree(obj), " has type ", typeToString(obj.typ)
     #writeStackTrace()
   else:
-    let prefix = if gTrackPosAttached: nil else: n
+    let prefix = if c.config.m.trackPosAttached: nil else: n
     suggestEverything(c, n, prefix, outputs)
 
 proc suggestExprNoCheck*(c: PContext, n: PNode) =
@@ -555,10 +528,10 @@ proc suggestExprNoCheck*(c: PContext, n: PNode) =
     suggestQuit()
 
 proc suggestExpr*(c: PContext, n: PNode) =
-  if exactEquals(gTrackPos, n.info): suggestExprNoCheck(c, n)
+  if exactEquals(c.config.m.trackPos, n.info): suggestExprNoCheck(c, n)
 
 proc suggestDecl*(c: PContext, n: PNode; s: PSym) =
-  let attached = gTrackPosAttached
+  let attached = c.config.m.trackPosAttached
   if attached: inc(c.inTypeContext)
   defer:
     if attached: dec(c.inTypeContext)
@@ -574,7 +547,7 @@ proc suggestEnum*(c: PContext; n: PNode; t: PType) =
   if outputs.len > 0: suggestQuit()
 
 proc suggestSentinel*(c: PContext) =
-  if c.config.ideCmd != ideSug or c.module.position != gTrackPos.fileIndex.int32: return
+  if c.config.ideCmd != ideSug or c.module.position != c.config.m.trackPos.fileIndex.int32: return
   if c.compilesContextId > 0: return
   inc(c.compilesContextId)
   var outputs: Suggestions = @[]
@@ -587,7 +560,9 @@ proc suggestSentinel*(c: PContext) =
     for it in items(scope.symbols):
       var pm: PrefixMatch
       if filterSymNoOpr(it, nil, pm):
-        outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, newLineInfo(gTrackPos.fileIndex, -1, -1), 0, PrefixMatch.None, false, scopeN))
+        outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug,
+            newLineInfo(c.config.m.trackPos.fileIndex, -1, -1), 0,
+            PrefixMatch.None, false, scopeN))
 
   dec(c.compilesContextId)
   produceOutput(outputs, c.config)
diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim
index 4bc153e46..069f65eee 100644
--- a/compiler/syntaxes.nim
+++ b/compiler/syntaxes.nim
@@ -11,7 +11,7 @@
 
 import
   strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser,
-  filters, filter_tmpl, renderer, configuration
+  filters, filter_tmpl, renderer, lineinfos
 
 type
   TFilterKind* = enum
@@ -38,7 +38,6 @@ proc parseAll*(p: var TParsers): PNode =
     result = parser.parseAll(p.parser)
   of skinEndX:
     internalError(p.config, "parser to implement")
-    result = ast.emptyNode
 
 proc parseTopLevelStmt*(p: var TParsers): PNode =
   case p.skin
@@ -46,7 +45,6 @@ proc parseTopLevelStmt*(p: var TParsers): PNode =
     result = parser.parseTopLevelStmt(p.parser)
   of skinEndX:
     internalError(p.config, "parser to implement")
-    result = ast.emptyNode
 
 proc utf8Bom(s: string): int =
   if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF':
@@ -62,7 +60,7 @@ proc containsShebang(s: string, i: int): bool =
 
 proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache;
                config: ConfigRef): PNode =
-  result = ast.emptyNode
+  result = newNode(nkEmpty)
   var s = llStreamOpen(filename, fmRead)
   if s != nil:
     var line = newStringOfCap(80)
@@ -144,7 +142,7 @@ proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream;
   assert config != nil
   var s: PLLStream
   p.skin = skinStandard
-  let filename = fileIdx.toFullPathConsiderDirty
+  let filename = toFullPathConsiderDirty(config, fileIdx)
   var pipe = parsePipe(filename, inputstream, cache, config)
   p.config() = config
   if pipe != nil: s = evalPipe(p, pipe, filename, inputstream)
@@ -162,7 +160,7 @@ proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode
   var
     p: TParsers
     f: File
-  let filename = fileIdx.toFullPathConsiderDirty
+  let filename = toFullPathConsiderDirty(config, fileIdx)
   if not open(f, filename):
     rawMessage(config, errGenerated, "cannot open file: " & filename)
     return
diff --git a/compiler/transf.nim b/compiler/transf.nim
index c2add13ff..abe713eb8 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -20,9 +20,9 @@
 
 import
   intsets, strutils, options, ast, astalgo, trees, treetab, msgs, lookups,
-  idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
+  idents, renderer, types, passes, semfold, magicsys, cgmeth,
   lambdalifting, sempass2, lowerings, destroyer, liftlocals, closureiters,
-  modulegraphs
+  modulegraphs, lineinfos
 
 type
   PTransNode* = distinct PNode
@@ -94,7 +94,7 @@ proc getCurrOwner(c: PTransf): PSym =
   else: result = c.module
 
 proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
-  let r = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info)
+  let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), getCurrOwner(c), info)
   r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink})
   incl(r.flags, sfFromGeneric)
   let owner = getCurrOwner(c)
@@ -194,7 +194,7 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode =
         idNodeTablePut(c.transCon.mapping, it.sons[j].sym, x)
         defs[j] = x.PTransNode
       assert(it.sons[L-2].kind == nkEmpty)
-      defs[L-2] = ast.emptyNode.PTransNode
+      defs[L-2] = newNodeI(nkEmpty, it.info).PTransNode
       defs[L-1] = transform(c, it.sons[L-1])
       result[i] = defs
 
@@ -221,7 +221,7 @@ proc hasContinue(n: PNode): bool =
 
 proc newLabel(c: PTransf, n: PNode): PSym =
   result = newSym(skLabel, nil, getCurrOwner(c), n.info)
-  result.name = getIdent(genPrefix & $result.id)
+  result.name = getIdent(c.graph.cache, genPrefix & $result.id)
 
 proc freshLabels(c: PTransf, n: PNode; symMap: var TIdTable) =
   if n.kind in {nkBlockStmt, nkBlockExpr}:
@@ -393,7 +393,7 @@ proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode =
   if c.graph.config.cmd == cmdCompileToJS: return prc
   result = newNodeIT(nkClosure, prc.info, dest)
   var conv = newNodeIT(nkHiddenSubConv, prc.info, dest)
-  conv.add(emptyNode)
+  conv.add(newNodeI(nkEmpty, prc.info))
   conv.add(prc)
   if prc.kind == nkClosure:
     internalError(c.graph.config, prc.info, "closure to closure created")
@@ -410,8 +410,8 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
     if not isOrdinalType(source):
       # float -> int conversions. ugh.
       result = transformSons(c, n)
-    elif firstOrd(n.typ) <= firstOrd(n.sons[1].typ) and
-        lastOrd(n.sons[1].typ) <= lastOrd(n.typ):
+    elif firstOrd(c.graph.config, n.typ) <= firstOrd(c.graph.config, n.sons[1].typ) and
+        lastOrd(c.graph.config, n.sons[1].typ) <= lastOrd(c.graph.config, n.typ):
       # BUGFIX: simply leave n as it is; we need a nkConv node,
       # but no range check:
       result = transformSons(c, n)
@@ -423,8 +423,8 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
         result = newTransNode(nkChckRange, n, 3)
       dest = skipTypes(n.typ, abstractVar)
       result[0] = transform(c, n.sons[1])
-      result[1] = newIntTypeNode(nkIntLit, firstOrd(dest), dest).PTransNode
-      result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), dest).PTransNode
+      result[1] = newIntTypeNode(nkIntLit, firstOrd(c.graph.config, dest), dest).PTransNode
+      result[2] = newIntTypeNode(nkIntLit, lastOrd(c.graph.config, dest), dest).PTransNode
   of tyFloat..tyFloat128:
     # XXX int64 -> float conversion?
     if skipTypes(n.typ, abstractVar).kind == tyRange:
@@ -598,7 +598,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
       idNodeTablePut(newC.mapping, formal, temp)
 
   var body = iter.getBody.copyTree
-  pushInfoContext(n.info)
+  pushInfoContext(c.graph.config, n.info)
   # XXX optimize this somehow. But the check "c.inlining" is not correct:
   var symMap: TIdTable
   initIdTable symMap
@@ -608,7 +608,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
   add(stmtList, transform(c, body))
   #findWrongOwners(c, stmtList.pnode)
   dec(c.inlining)
-  popInfoContext()
+  popInfoContext(c.graph.config)
   popTransCon(c)
   # echo "transformed: ", stmtList.PNode.renderTree
 
@@ -721,16 +721,16 @@ proc transformExceptBranch(c: PTransf, n: PNode): PTransNode =
     let actions = newTransNode(nkStmtListExpr, n[1], 2)
     # Generating `let exc = (excType)(getCurrentException())`
     # -> getCurrentException()
-    let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException", ast.emptyNode))
+    let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException", newNodeI(nkEmpty, n.info)))
     # -> (excType)
     let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2)
-    convNode[0] = PTransNode(ast.emptyNode)
+    convNode[0] = PTransNode(newNodeI(nkEmpty, n.info))
     convNode[1] = excCall
     PNode(convNode).typ = excTypeNode.typ.toRef()
     # -> let exc = ...
     let identDefs = newTransNode(nkIdentDefs, n[1].info, 3)
     identDefs[0] = PTransNode(n[0][2])
-    identDefs[1] = PTransNode(ast.emptyNode)
+    identDefs[1] = PTransNode(newNodeI(nkEmpty, n.info))
     identDefs[2] = convNode
 
     let letSection = newTransNode(nkLetSection, n[1].info, 1)
@@ -780,6 +780,43 @@ proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode =
     else:
       result = n
 
+proc hoistParamsUsedInDefault(c: PTransf, call, letSection, defExpr: PNode): PNode =
+  # This takes care of complicated signatures such as:
+  # proc foo(a: int, b = a)
+  # proc bar(a: int, b: int, c = a + b)
+  #
+  # The recursion may confuse you. It performs two duties:
+  #
+  # 1) extracting all referenced params from default expressions
+  #    into a let section preceeding the call
+  #
+  # 2) replacing the "references" within the default expression
+  #    with these extracted skLet symbols.
+  #
+  # The first duty is carried out directly in the code here, while the second
+  # duty is activated by returning a non-nil value. The caller is responsible
+  # for replacing the input to the function with the returned non-nil value.
+  # (which is the hoisted symbol)
+  if defExpr.kind == nkSym:
+    if defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym:
+      let paramPos = defExpr.sym.position + 1
+
+      if call[paramPos].kind == nkSym and sfHoisted in call[paramPos].sym.flags:
+        # Already hoisted, we still need to return it in order to replace the
+        # placeholder expression in the default value.
+        return call[paramPos]
+
+      let hoistedVarSym = hoistExpr(letSection,
+                                    call[paramPos],
+                                    getIdent(c.graph.cache, genPrefix),
+                                    c.transCon.owner).newSymNode
+      call[paramPos] = hoistedVarSym
+      return hoistedVarSym
+  else:
+    for i in 0..<defExpr.safeLen:
+      let hoisted = hoistParamsUsedInDefault(c, call, letSection, defExpr[i])
+      if hoisted != nil: defExpr[i] = hoisted
+
 proc transform(c: PTransf, n: PNode): PTransNode =
   when false:
     var oldDeferAnchor: PNode
@@ -849,6 +886,15 @@ proc transform(c: PTransf, n: PNode): PTransNode =
   of nkBreakStmt: result = transformBreak(c, n)
   of nkCallKinds:
     result = transformCall(c, n)
+    var call = result.PNode
+    if nfDefaultRefsParam in call.flags:
+      # We've found a default value that references another param.
+      # See the notes in `hoistParamsUsedInDefault` for more details.
+      var hoistedParams = newNodeI(nkLetSection, call.info, 0)
+      for i in 1 ..< call.len:
+        let hoisted = hoistParamsUsedInDefault(c, call, hoistedParams, call[i])
+        if hoisted != nil: call[i] = hoisted
+      result = newTree(nkStmtListExpr, hoistedParams, call).PTransNode
   of nkAddr, nkHiddenAddr:
     result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
   of nkDerefExpr, nkHiddenDeref:
@@ -863,7 +909,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
         # ensure that e.g. discard "some comment" gets optimized away
         # completely:
         result = PTransNode(newNode(nkCommentStmt))
-  of nkCommentStmt, nkTemplateDef:
+  of nkCommentStmt, nkTemplateDef, nkImportStmt, nkStaticStmt:
     return n.PTransNode
   of nkConstSection:
     # do not replace ``const c = 3`` with ``const 3 = 3``
@@ -916,7 +962,7 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
   # Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
   # this step! We have to rely that the semantic pass transforms too errornous
   # nodes into an empty node.
-  if c.rd != nil or nfTransf in n.flags: return n
+  if nfTransf in n.flags: return n
   pushTransCon(c, newTransCon(owner))
   result = PNode(transform(c, n))
   popTransCon(c)
@@ -981,11 +1027,11 @@ proc transformBody*(g: ModuleGraph; module: PSym, n: PNode, prc: PSym): PNode =
     liftDefer(c, result)
     #result = liftLambdas(prc, result)
     when useEffectSystem: trackProc(g, prc, result)
-    result = liftLocalsIfRequested(prc, result, g.config)
+    result = liftLocalsIfRequested(prc, result, g.cache, g.config)
     if c.needsDestroyPass: #and newDestructors:
       result = injectDestructorCalls(g, prc, result)
 
-    if prc.isIterator and oldIterTransf notin g.config.features:
+    if prc.isIterator:
       result = g.transformClosureIterator(prc, result)
 
     incl(result.flags, nfTransf)
diff --git a/compiler/types.nim b/compiler/types.nim
index 1fab842cc..c5af855cc 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -10,7 +10,8 @@
 # this module contains routines for accessing and iterating over types
 
 import
-  intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options
+  intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options,
+  lineinfos
 
 type
   TPreferedDesc* = enum
@@ -88,13 +89,16 @@ proc isPureObject*(typ: PType): bool =
 
 proc getOrdValue*(n: PNode): BiggestInt =
   case n.kind
-  of nkCharLit..nkUInt64Lit: result = n.intVal
-  of nkNilLit: result = 0
-  of nkHiddenStdConv: result = getOrdValue(n.sons[1])
-  else:
-    #localError(n.info, errOrdinalTypeExpected)
-    # XXX check usages of getOrdValue
-    result = high(BiggestInt)
+  of nkCharLit..nkUInt64Lit: n.intVal
+  of nkNilLit: 0
+  of nkHiddenStdConv: getOrdValue(n.sons[1])
+  else: high(BiggestInt)
+
+proc getFloatValue*(n: PNode): BiggestFloat =
+  case n.kind
+  of nkFloatLiterals: n.floatVal
+  of nkHiddenStdConv: getFloatValue(n.sons[1])
+  else: NaN
 
 proc isIntLit*(t: PType): bool {.inline.} =
   result = t.kind == tyInt and t.n != nil and t.n.kind == nkIntLit
@@ -102,7 +106,7 @@ 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 getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string =
+proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName): string =
   result = sym.owner.name.s & '.' & sym.name.s & '('
   var n = sym.typ.n
   for i in countup(1, sonsLen(n) - 1):
@@ -118,13 +122,13 @@ proc getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string =
   if n.sons[0].typ != nil:
     result.add(": " & typeToString(n.sons[0].typ, prefer))
   result.add "[declared in "
-  result.add($sym.info)
+  result.add(conf$sym.info)
   result.add "]"
 
 proc elemType*(t: PType): PType =
   assert(t != nil)
   case t.kind
-  of tyGenericInst, tyDistinct, tyAlias: result = elemType(lastSon(t))
+  of tyGenericInst, tyDistinct, tyAlias, tySink: result = elemType(lastSon(t))
   of tyArray: result = t.sons[1]
   else: result = t.lastSon
   assert(result != nil)
@@ -134,12 +138,12 @@ proc isOrdinalType*(t: PType): bool =
   const
     # caution: uint, uint64 are no ordinal types!
     baseKinds = {tyChar,tyInt..tyInt64,tyUInt8..tyUInt32,tyBool,tyEnum}
-    parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tyDistinct}
+    parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tySink, tyDistinct}
   t.kind in baseKinds or (t.kind in parentKinds and isOrdinalType(t.sons[0]))
 
 proc enumHasHoles*(t: PType): bool =
   var b = t
-  while b.kind in {tyRange, tyGenericInst, tyAlias}: b = b.sons[0]
+  while b.kind in {tyRange, tyGenericInst, tyAlias, tySink}: b = b.sons[0]
   result = b.kind == tyEnum and tfEnumHasHoles in b.flags
 
 proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
@@ -164,7 +168,7 @@ proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
   if result: return
   if not containsOrIncl(marker, t.id):
     case t.kind
-    of tyGenericInst, tyGenericBody, tyAlias, tyInferred:
+    of tyGenericInst, tyGenericBody, tyAlias, tySink, tyInferred:
       result = iterOverTypeAux(marker, lastSon(t), iter, closure)
     else:
       for i in countup(0, sonsLen(t) - 1):
@@ -214,7 +218,7 @@ proc searchTypeForAux(t: PType, predicate: TTypePredicate,
     if t.sons[0] != nil:
       result = searchTypeForAux(t.sons[0].skipTypes(skipPtrs), predicate, marker)
     if not result: result = searchTypeNodeForAux(t.n, predicate, marker)
-  of tyGenericInst, tyDistinct, tyAlias:
+  of tyGenericInst, tyDistinct, tyAlias, tySink:
     result = searchTypeForAux(lastSon(t), predicate, marker)
   of tyArray, tySet, tyTuple:
     for i in countup(0, sonsLen(t) - 1):
@@ -257,7 +261,7 @@ proc analyseObjectWithTypeFieldAux(t: PType,
       if res == frHeader: result = frHeader
     if result == frNone:
       if isObjectWithTypeFieldPredicate(t): result = frHeader
-  of tyGenericInst, tyDistinct, tyAlias:
+  of tyGenericInst, tyDistinct, tyAlias, tySink:
     result = analyseObjectWithTypeFieldAux(lastSon(t), marker)
   of tyArray, tyTuple:
     for i in countup(0, sonsLen(t) - 1):
@@ -501,7 +505,10 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
     #internalAssert t.len == 0
     result = "untyped"
   of tyFromExpr:
-    result = renderTree(t.n)
+    if t.n == nil:
+      result = "unknown"
+    else:
+      result = "type(" & renderTree(t.n) & ")"
   of tyArray:
     if t.sons[0].kind == tyRange:
       result = "array[" & rangeToStr(t.sons[0].n) & ", " &
@@ -589,18 +596,19 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
     result = typeToStr[t.kind]
   result.addTypeFlags(t)
 
-proc firstOrd*(t: PType): BiggestInt =
+
+proc firstOrd*(conf: ConfigRef; t: PType): BiggestInt =
   case t.kind
   of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy:
     result = 0
-  of tySet, tyVar: result = firstOrd(t.sons[0])
-  of tyArray: result = firstOrd(t.sons[0])
+  of tySet, tyVar: result = firstOrd(conf, t.sons[0])
+  of tyArray: result = firstOrd(conf, t.sons[0])
   of tyRange:
     assert(t.n != nil)        # range directly given:
     assert(t.n.kind == nkRange)
     result = getOrdValue(t.n.sons[0])
   of tyInt:
-    if platform.intSize == 4: result = - (2147483646) - 2
+    if conf != nil and conf.target.intSize == 4: result = - (2147483646) - 2
     else: result = 0x8000000000000000'i64
   of tyInt8: result = - 128
   of tyInt16: result = - 32768
@@ -610,38 +618,55 @@ proc firstOrd*(t: PType): BiggestInt =
   of tyEnum:
     # if basetype <> nil then return firstOrd of basetype
     if sonsLen(t) > 0 and t.sons[0] != nil:
-      result = firstOrd(t.sons[0])
+      result = firstOrd(conf, t.sons[0])
     else:
       assert(t.n.sons[0].kind == nkSym)
       result = t.n.sons[0].sym.position
-  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred:
-    result = firstOrd(lastSon(t))
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
+     tyStatic, tyInferred, tyUserTypeClassInst:
+    result = firstOrd(conf, lastSon(t))
   of tyOrdinal:
-    if t.len > 0: result = firstOrd(lastSon(t))
-    else: internalError(newPartialConfigRef(), "invalid kind for firstOrd(" & $t.kind & ')')
+    if t.len > 0: result = firstOrd(conf, lastSon(t))
+    else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')')
   else:
-    internalError(newPartialConfigRef(), "invalid kind for firstOrd(" & $t.kind & ')')
+    internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')')
     result = 0
 
-proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt =
+
+proc firstFloat*(t: PType): BiggestFloat =
+  case t.kind
+  of tyFloat..tyFloat128: -Inf
+  of tyRange:
+    assert(t.n != nil)        # range directly given:
+    assert(t.n.kind == nkRange)
+    getFloatValue(t.n.sons[0])
+  of tyVar: firstFloat(t.sons[0])
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
+     tyStatic, tyInferred:
+    firstFloat(lastSon(t))
+  else:
+    internalError(newPartialConfigRef(), "invalid kind for firstFloat(" & $t.kind & ')')
+    NaN
+
+proc lastOrd*(conf: ConfigRef; t: PType; fixedUnsigned = false): BiggestInt =
   case t.kind
   of tyBool: result = 1
   of tyChar: result = 255
-  of tySet, tyVar: result = lastOrd(t.sons[0])
-  of tyArray: result = lastOrd(t.sons[0])
+  of tySet, tyVar: result = lastOrd(conf, t.sons[0])
+  of tyArray: result = lastOrd(conf, t.sons[0])
   of tyRange:
     assert(t.n != nil)        # range directly given:
     assert(t.n.kind == nkRange)
     result = getOrdValue(t.n.sons[1])
   of tyInt:
-    if platform.intSize == 4: result = 0x7FFFFFFF
+    if conf != nil and conf.target.intSize == 4: result = 0x7FFFFFFF
     else: result = 0x7FFFFFFFFFFFFFFF'i64
   of tyInt8: result = 0x0000007F
   of tyInt16: result = 0x00007FFF
   of tyInt32: result = 0x7FFFFFFF
   of tyInt64: result = 0x7FFFFFFFFFFFFFFF'i64
   of tyUInt:
-    if platform.intSize == 4: result = 0xFFFFFFFF
+    if conf != nil and conf.target.intSize == 4: result = 0xFFFFFFFF
     elif fixedUnsigned: result = 0xFFFFFFFFFFFFFFFF'i64
     else: result = 0x7FFFFFFFFFFFFFFF'i64
   of tyUInt8: result = 0xFF
@@ -653,28 +678,46 @@ proc lastOrd*(t: PType; fixedUnsigned = false): 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, tyAlias, tyStatic, tyInferred:
-    result = lastOrd(lastSon(t))
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
+     tyStatic, tyInferred:
+    result = lastOrd(conf, lastSon(t))
   of tyProxy: result = 0
   of tyOrdinal:
-    if t.len > 0: result = lastOrd(lastSon(t))
-    else: internalError(newPartialConfigRef(), "invalid kind for lastOrd(" & $t.kind & ')')
+    if t.len > 0: result = lastOrd(conf, lastSon(t))
+    else: internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')')
   else:
-    internalError(newPartialConfigRef(), "invalid kind for lastOrd(" & $t.kind & ')')
+    internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')')
     result = 0
 
-proc lengthOrd*(t: PType): BiggestInt =
+
+proc lastFloat*(t: PType): BiggestFloat =
+  case t.kind
+  of tyFloat..tyFloat128: Inf
+  of tyVar: lastFloat(t.sons[0])
+  of tyRange:
+    assert(t.n != nil)        # range directly given:
+    assert(t.n.kind == nkRange)
+    getFloatValue(t.n.sons[1])
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
+     tyStatic, tyInferred:
+    lastFloat(lastSon(t))
+  else:
+    internalError(newPartialConfigRef(), "invalid kind for lastFloat(" & $t.kind & ')')
+    NaN
+
+
+proc lengthOrd*(conf: ConfigRef; t: PType): BiggestInt =
   case t.kind
-  of tyInt64, tyInt32, tyInt: result = lastOrd(t)
-  of tyDistinct: result = lengthOrd(t.sons[0])
+  of tyInt64, tyInt32, tyInt: result = lastOrd(conf, t)
+  of tyDistinct: result = lengthOrd(conf, t.sons[0])
   else:
-    let last = lastOrd t
-    let first = firstOrd t
+    let last = lastOrd(conf, t)
+    let first = firstOrd(conf, t)
     # XXX use a better overflow check here:
     if last == high(BiggestInt) and first <= 0:
       result = last
     else:
-      result = lastOrd(t) - firstOrd(t) + 1
+      result = lastOrd(conf, t) - firstOrd(conf, t) + 1
 
 # -------------- type equality -----------------------------------------------
 
@@ -1209,81 +1252,24 @@ proc typeAllowed*(t: PType, kind: TSymKind; flags: TTypeAllowedFlags = {}): PTyp
 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
   szUnknownSize* = -1
 
-proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt
-proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt =
+proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt
+proc computeRecSizeAux(conf: ConfigRef; n: PNode, a, currOffset: var BiggestInt): BiggestInt =
   var maxAlign, maxSize, b, res: BiggestInt
   case n.kind
   of nkRecCase:
     assert(n.sons[0].kind == nkSym)
-    result = computeRecSizeAux(n.sons[0], a, currOffset)
+    result = computeRecSizeAux(conf, n.sons[0], a, currOffset)
     maxSize = 0
     maxAlign = 1
     for i in countup(1, sonsLen(n) - 1):
       case n.sons[i].kind
       of nkOfBranch, nkElse:
-        res = computeRecSizeAux(lastSon(n.sons[i]), b, currOffset)
+        res = computeRecSizeAux(conf, lastSon(n.sons[i]), b, currOffset)
         if res < 0: return res
         maxSize = max(maxSize, res)
         maxAlign = max(maxAlign, b)
@@ -1296,20 +1282,20 @@ proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt =
     result = 0
     maxAlign = 1
     for i in countup(0, sonsLen(n) - 1):
-      res = computeRecSizeAux(n.sons[i], b, currOffset)
+      res = computeRecSizeAux(conf, n.sons[i], b, currOffset)
       if res < 0: return res
       currOffset = align(currOffset, b) + res
       result = align(result, b) + res
       if b > maxAlign: maxAlign = b
     a = maxAlign
   of nkSym:
-    result = computeSizeAux(n.sym.typ, a)
+    result = computeSizeAux(conf, n.sym.typ, a)
     n.sym.offset = int(currOffset)
   else:
     a = 1
     result = szNonConcreteType
 
-proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
+proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt =
   var res, maxAlign, length, currOffset: BiggestInt
   if typ.size == szIllegalRecursion:
     # we are already computing the size of the type
@@ -1323,7 +1309,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
   typ.size = szIllegalRecursion # mark as being computed
   case typ.kind
   of tyInt, tyUInt:
-    result = intSize
+    result = conf.target.intSize
     a = result
   of tyInt8, tyUInt8, tyBool, tyChar:
     result = 1
@@ -1341,30 +1327,30 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
     result = 16
     a = result
   of tyFloat:
-    result = floatSize
+    result = conf.target.floatSize
     a = result
   of tyProc:
-    if typ.callConv == ccClosure: result = 2 * ptrSize
-    else: result = ptrSize
-    a = ptrSize
+    if typ.callConv == ccClosure: result = 2 * conf.target.ptrSize
+    else: result = conf.target.ptrSize
+    a = conf.target.ptrSize
   of tyString, tyNil:
-    result = ptrSize
+    result = conf.target.ptrSize
     a = result
   of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray:
     let base = typ.lastSon
     if base == typ or (base.kind == tyTuple and base.size==szIllegalRecursion):
       result = szIllegalRecursion
-    else: result = ptrSize
+    else: result = conf.target.ptrSize
     a = result
   of tyArray:
-    let elemSize = computeSizeAux(typ.sons[1], a)
+    let elemSize = computeSizeAux(conf, typ.sons[1], a)
     if elemSize < 0: return elemSize
-    result = lengthOrd(typ.sons[0]) * elemSize
+    result = lengthOrd(conf, typ.sons[0]) * elemSize
   of tyEnum:
-    if firstOrd(typ) < 0:
+    if firstOrd(conf, typ) < 0:
       result = 4              # use signed int32
     else:
-      length = lastOrd(typ)   # BUGFIX: use lastOrd!
+      length = lastOrd(conf, typ)   # BUGFIX: use lastOrd!
       if length + 1 < `shl`(1, 8): result = 1
       elif length + 1 < `shl`(1, 16): result = 2
       elif length + 1 < `shl`(BiggestInt(1), 32): result = 4
@@ -1374,7 +1360,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
     if typ.sons[0].kind == tyGenericParam:
       result = szUnknownSize
     else:
-      length = lengthOrd(typ.sons[0])
+      length = lengthOrd(conf, typ.sons[0])
       if length <= 8: result = 1
       elif length <= 16: result = 2
       elif length <= 32: result = 4
@@ -1383,12 +1369,12 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
       else: result = align(length, 8) div 8 + 1
     a = result
   of tyRange:
-    result = computeSizeAux(typ.sons[0], a)
+    result = computeSizeAux(conf, typ.sons[0], a)
   of tyTuple:
     result = 0
     maxAlign = 1
     for i in countup(0, sonsLen(typ) - 1):
-      res = computeSizeAux(typ.sons[i], a)
+      res = computeSizeAux(conf, typ.sons[i], a)
       if res < 0: return res
       maxAlign = max(maxAlign, a)
       result = align(result, a) + res
@@ -1396,61 +1382,52 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
     a = maxAlign
   of tyObject:
     if typ.sons[0] != nil:
-      result = computeSizeAux(typ.sons[0].skipTypes(skipPtrs), a)
+      result = computeSizeAux(conf, typ.sons[0].skipTypes(skipPtrs), a)
       if result < 0: return
       maxAlign = a
     elif isObjectWithTypeFieldPredicate(typ):
-      result = intSize
+      result = conf.target.intSize
       maxAlign = result
     else:
       result = 0
       maxAlign = 1
     currOffset = result
-    result = computeRecSizeAux(typ.n, a, currOffset)
+    result = computeRecSizeAux(conf, typ.n, a, currOffset)
     if result < 0: return
     if a < maxAlign: a = maxAlign
     result = align(result, a)
   of tyInferred:
     if typ.len > 1:
-      result = computeSizeAux(typ.lastSon, a)
-  of tyGenericInst, tyDistinct, tyGenericBody, tyAlias:
-    result = computeSizeAux(lastSon(typ), a)
+      result = computeSizeAux(conf, typ.lastSon, a)
+  of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink:
+    result = computeSizeAux(conf, lastSon(typ), a)
   of tyTypeClasses:
-    result = if typ.isResolvedUserTypeClass: computeSizeAux(typ.lastSon, a)
+    result = if typ.isResolvedUserTypeClass: computeSizeAux(conf, typ.lastSon, a)
              else: szUnknownSize
   of tyTypeDesc:
-    result = computeSizeAux(typ.base, a)
+    result = computeSizeAux(conf, typ.base, a)
   of tyForward: return szIllegalRecursion
   of tyStatic:
-    result = if typ.n != nil: computeSizeAux(typ.lastSon, a)
+    result = if typ.n != nil: computeSizeAux(conf, 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*(conf: ConfigRef; typ: PType): BiggestInt =
   var a: BiggestInt = 1
-  result = computeSizeAux(typ, a)
+  result = computeSizeAux(conf, typ, a)
 
 proc getReturnType*(s: PSym): PType =
   # Obtains the return type of a iterator/proc/macro/template
   assert s.kind in skProcKinds
   result = s.typ.sons[0]
 
-proc getSize*(typ: PType): BiggestInt =
-  result = computeSize(typ)
-  #if result < 0: internalError("getSize: " & $typ.kind)
-  # XXX review all usages of 'getSize'
+proc getSize*(conf: ConfigRef; typ: PType): BiggestInt =
+  result = computeSize(conf, typ)
+  if result < 0: internalError(conf, "getSize: " & $typ.kind)
 
 proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
   case t.kind
@@ -1601,7 +1578,7 @@ proc isEmptyContainer*(t: PType): bool =
   of tyArray: result = t.sons[1].kind == tyEmpty
   of tySet, tySequence, tyOpenArray, tyVarargs:
     result = t.sons[0].kind == tyEmpty
-  of tyGenericInst, tyAlias: result = isEmptyContainer(t.lastSon)
+  of tyGenericInst, tyAlias, tySink: result = isEmptyContainer(t.lastSon)
   else: result = false
 
 proc takeType*(formal, arg: PType): PType =
diff --git a/compiler/vm.nim b/compiler/vm.nim
index cbd304caa..b16eb0fd4 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, gorgeimpl, configuration
+  vmmarshal, gorgeimpl, lineinfos, tables, btrees, macrocacheimpl
 
 from semfold import leValueConv, ordinalValToString
 from evaltempl import evalTemplate
@@ -66,7 +66,7 @@ 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 = substr(toFilename(info), 0)
+    var s = substr(toFilename(c.config, 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.
@@ -204,22 +204,14 @@ proc asgnComplex(x: var TFullReg, y: TFullReg) =
   of rkRegisterAddr: x.regAddr = y.regAddr
   of rkNodeAddr: x.nodeAddr = y.nodeAddr
 
-proc putIntoNode(n: var PNode; x: TFullReg) =
+proc writeField(n: var PNode, x: TFullReg) =
   case x.kind
   of rkNone: discard
   of rkInt: n.intVal = x.intVal
   of rkFloat: n.floatVal = x.floatVal
-  of rkNode:
-    if nfIsRef in x.node.flags:
-      n = x.node
-    else:
-      let destIsRef = nfIsRef in n.flags    
-      n[] = x.node[]
-      # Ref-ness must be kept for the destination
-      if destIsRef:
-        n.flags.incl nfIsRef
-  of rkRegisterAddr: putIntoNode(n, x.regAddr[])
-  of rkNodeAddr: n[] = x.nodeAddr[][]
+  of rkNode: n = x.node
+  of rkRegisterAddr: writeField(n, x.regAddr[])
+  of rkNodeAddr: n = x.nodeAddr[]
 
 proc putIntoReg(dest: var TFullReg; n: PNode) =
   case n.kind
@@ -370,7 +362,7 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType):
         dest.intVal = int(src.floatVal)
       else:
         dest.intVal = src.intVal
-      if dest.intVal < firstOrd(desttyp) or dest.intVal > lastOrd(desttyp):
+      if dest.intVal < firstOrd(c.config, desttyp) or dest.intVal > lastOrd(c.config, desttyp):
         return true
     of tyUInt..tyUInt64:
       if dest.kind != rkInt:
@@ -498,9 +490,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       asgnComplex(regs[ra], regs[instr.regB])
     of opcAsgnRef:
       asgnRef(regs[ra], regs[instr.regB])
-    of opcRegToNode:
-      decodeB(rkNode)
-      putIntoNode(regs[ra].node, regs[rb])
     of opcNodeToReg:
       let ra = instr.regA
       let rb = instr.regB
@@ -559,7 +548,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         else:
           stackTrace(c, tos, pc, errIndexOutOfBounds)
       elif idx <% arr.len:
-        putIntoNode(arr.sons[idx], regs[rc])
+        writeField(arr.sons[idx], regs[rc])
       else:
         stackTrace(c, tos, pc, errIndexOutOfBounds)
     of opcLdObj:
@@ -579,9 +568,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       if dest.kind == nkNilLit:
         stackTrace(c, tos, pc, errNilAccess)
       elif dest.sons[shiftedRb].kind == nkExprColonExpr:
-        putIntoNode(dest.sons[shiftedRb].sons[1], regs[rc])
+        writeField(dest.sons[shiftedRb].sons[1], regs[rc])
       else:
-        putIntoNode(dest.sons[shiftedRb], regs[rc])
+        writeField(dest.sons[shiftedRb], regs[rc])
     of opcWrStrIdx:
       decodeBC(rkNode)
       let idx = regs[rb].intVal.int
@@ -624,9 +613,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       let ra = instr.regA
       let rc = instr.regC
       case regs[ra].kind
-      of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rc])
+      of rkNodeAddr:
+        # XXX: Workaround for vmgen bug:
+        let n = regs[rc].regToNode
+        if (nfIsRef in regs[ra].nodeAddr[].flags or
+            regs[ra].nodeAddr[].kind == nkNilLit) and nfIsRef notin n.flags:
+          if regs[ra].nodeAddr[].kind == nkNilLit:
+            stackTrace(c, tos, pc, errNilAccess)
+          regs[ra].nodeAddr[][] = n[]
+          regs[ra].nodeAddr[].flags.incl nfIsRef
+        else:
+          regs[ra].nodeAddr[] = n
       of rkRegisterAddr: regs[ra].regAddr[] = regs[rc]
-      of rkNode: putIntoNode(regs[ra].node, regs[rc])
+      of rkNode:
+        if regs[ra].node.kind == nkNilLit:
+          stackTrace(c, tos, pc, errNilAccess)
+        assert nfIsRef in regs[ra].node.flags
+        regs[ra].node[] = regs[rc].regToNode[]
+        regs[ra].node.flags.incl nfIsRef
       else: stackTrace(c, tos, pc, errNilAccess)
     of opcAddInt:
       decodeBC(rkInt)
@@ -700,12 +704,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeB(rkNode)
       var b = newNodeIT(nkCurly, regs[ra].node.info, regs[ra].node.typ)
       addSon(b, regs[rb].regToNode)
-      var r = diffSets(regs[ra].node, b)
+      var r = diffSets(c.config, regs[ra].node, b)
       discardSons(regs[ra].node)
       for i in countup(0, sonsLen(r) - 1): addSon(regs[ra].node, r.sons[i])
     of opcCard:
       decodeB(rkInt)
-      regs[ra].intVal = nimsets.cardSet(regs[rb].node)
+      regs[ra].intVal = nimsets.cardSet(c.config, regs[rb].node)
     of opcMulInt:
       decodeBC(rkInt)
       let
@@ -853,35 +857,35 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       regs[ra].intVal = ord(regs[rb].node.strVal < regs[rc].node.strVal)
     of opcLeSet:
       decodeBC(rkInt)
-      regs[ra].intVal = ord(containsSets(regs[rb].node, regs[rc].node))
+      regs[ra].intVal = ord(containsSets(c.config, regs[rb].node, regs[rc].node))
     of opcEqSet:
       decodeBC(rkInt)
-      regs[ra].intVal = ord(equalSets(regs[rb].node, regs[rc].node))
+      regs[ra].intVal = ord(equalSets(c.config, regs[rb].node, regs[rc].node))
     of opcLtSet:
       decodeBC(rkInt)
       let a = regs[rb].node
       let b = regs[rc].node
-      regs[ra].intVal = ord(containsSets(a, b) and not equalSets(a, b))
+      regs[ra].intVal = ord(containsSets(c.config, a, b) and not equalSets(c.config, a, b))
     of opcMulSet:
       decodeBC(rkNode)
       createSet(regs[ra])
       move(regs[ra].node.sons,
-            nimsets.intersectSets(regs[rb].node, regs[rc].node).sons)
+            nimsets.intersectSets(c.config, regs[rb].node, regs[rc].node).sons)
     of opcPlusSet:
       decodeBC(rkNode)
       createSet(regs[ra])
       move(regs[ra].node.sons,
-           nimsets.unionSets(regs[rb].node, regs[rc].node).sons)
+           nimsets.unionSets(c.config, regs[rb].node, regs[rc].node).sons)
     of opcMinusSet:
       decodeBC(rkNode)
       createSet(regs[ra])
       move(regs[ra].node.sons,
-           nimsets.diffSets(regs[rb].node, regs[rc].node).sons)
+           nimsets.diffSets(c.config, regs[rb].node, regs[rc].node).sons)
     of opcSymdiffSet:
       decodeBC(rkNode)
       createSet(regs[ra])
       move(regs[ra].node.sons,
-           nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons)
+           nimsets.symdiffSets(c.config, regs[rb].node, regs[rc].node).sons)
     of opcConcatStr:
       decodeBC(rkNode)
       createStr regs[ra]
@@ -972,7 +976,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         when hasFFI:
           let prcValue = c.globals.sons[prc.position-1]
           if prcValue.kind == nkEmpty:
-            globalError(c.config, c.debug[pc], "canot run " & prc.name.s)
+            globalError(c.config, c.debug[pc], "cannot run " & prc.name.s)
           let newValue = callForeignFunction(prcValue, prc.typ, tos.slots,
                                              rb+1, rc-1, c.debug[pc])
           if newValue.kind != nkEmpty:
@@ -1211,6 +1215,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNBindSym:
       decodeBx(rkNode)
       regs[ra].node = copyTree(c.constants.sons[rbx])
+      regs[ra].node.flags.incl nfIsRef
     of opcNChild:
       decodeBC(rkNode)
       let idx = regs[rc].intVal.int
@@ -1291,7 +1296,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         # getType opcode:
         ensureKind(rkNode)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
-          regs[ra].node = opMapTypeToAst(regs[rb].node.typ, c.debug[pc])
+          regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.typ, c.debug[pc])
         else:
           stackTrace(c, tos, pc, "node has no type")
       of 1:
@@ -1305,14 +1310,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         # getTypeInst opcode:
         ensureKind(rkNode)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
-          regs[ra].node = opMapTypeInstToAst(regs[rb].node.typ, c.debug[pc])
+          regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.typ, c.debug[pc])
         else:
           stackTrace(c, tos, pc, "node has no type")
       else:
         # getTypeImpl opcode:
         ensureKind(rkNode)
         if regs[rb].kind == rkNode and regs[rb].node.typ != nil:
-          regs[ra].node = opMapTypeImplToAst(regs[rb].node.typ, c.debug[pc])
+          regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.typ, c.debug[pc])
         else:
           stackTrace(c, tos, pc, "node has no type")
     of opcNStrVal:
@@ -1336,14 +1341,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       regs[ra].node.strVal = opSlurp(regs[rb].node.strVal, c.debug[pc],
                                      c.module, c.config)
     of opcGorge:
-      decodeBC(rkNode)
-      inc pc
-      let rd = c.code[pc].regA
-
-      createStr regs[ra]
-      regs[ra].node.strVal = opGorge(regs[rb].node.strVal,
-                                     regs[rc].node.strVal, regs[rd].node.strVal,
-                                     c.debug[pc], c.config)[0]
+      when defined(nimcore):
+        decodeBC(rkNode)
+        inc pc
+        let rd = c.code[pc].regA
+
+        createStr regs[ra]
+        regs[ra].node.strVal = opGorge(regs[rb].node.strVal,
+                                      regs[rc].node.strVal, regs[rd].node.strVal,
+                                      c.debug[pc], c.config)[0]
+      else:
+        globalError(c.config, c.debug[pc], "VM is not built with 'gorge' support")
     of opcNError:
       decodeB(rkNode)
       let a = regs[ra].node
@@ -1358,7 +1366,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       # c.debug[pc].line.int - countLines(regs[rb].strVal) ?
       var error: string
       let ast = parseString(regs[rb].node.strVal, c.cache, c.config,
-                            c.debug[pc].toFullPath, c.debug[pc].line.int,
+                            toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int,
                             proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) =
                               if error.isNil and msg <= errMax:
                                 error = formatMsg(conf, info, msg, arg))
@@ -1373,7 +1381,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeB(rkNode)
       var error: string
       let ast = parseString(regs[rb].node.strVal, c.cache, c.config,
-                            c.debug[pc].toFullPath, c.debug[pc].line.int,
+                            toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int,
                             proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) =
                               if error.isNil and msg <= errMax:
                                 error = formatMsg(conf, info, msg, arg))
@@ -1392,7 +1400,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNGetFile:
       decodeB(rkNode)
       let n = regs[rb].node
-      regs[ra].node = newStrNode(nkStrLit, n.info.toFilename)
+      regs[ra].node = newStrNode(nkStrLit, toFilename(c.config, n.info))
       regs[ra].node.info = n.info
       regs[ra].node.typ = n.typ
     of opcNGetLine:
@@ -1453,7 +1461,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         stackTrace(c, tos, pc, errFieldXNotFound & "strVal")
       else:
         regs[ra].node = newNodeI(nkIdent, c.debug[pc])
-        regs[ra].node.ident = getIdent(regs[rb].node.strVal)
+        regs[ra].node.ident = getIdent(c.cache, regs[rb].node.strVal)
     of opcSetType:
       if regs[ra].kind != rkNode:
         internalError(c.config, c.debug[pc], "cannot set type")
@@ -1566,9 +1574,110 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
                  else: regs[rc].node.strVal
       if k < 0 or k > ord(high(TSymKind)):
         internalError(c.config, c.debug[pc], "request to create symbol of invalid kind")
-      var sym = newSym(k.TSymKind, name.getIdent, c.module.owner, c.debug[pc])
+      var sym = newSym(k.TSymKind, getIdent(c.cache, name), c.module.owner, c.debug[pc])
       incl(sym.flags, sfGenSym)
       regs[ra].node = newSymNode(sym)
+      regs[ra].node.flags.incl nfIsRef
+    of opcNccValue:
+      decodeB(rkInt)
+      let destKey = regs[rb].node.strVal
+      regs[ra].intVal = getOrDefault(c.graph.cacheCounters, destKey)
+    of opcNccInc:
+      let g = c.graph
+      let destKey = regs[ra].node.strVal
+      let by = regs[instr.regB].intVal
+      let v = getOrDefault(g.cacheCounters, destKey)
+      g.cacheCounters[destKey] = v+by
+      recordInc(c, c.debug[pc], destKey, by)
+    of opcNcsAdd:
+      let g = c.graph
+      let destKey = regs[ra].node.strVal
+      let val = regs[instr.regB].node
+      if not contains(g.cacheSeqs, destKey):
+        g.cacheSeqs[destKey] = newTree(nkStmtList, val)
+        # newNodeI(nkStmtList, c.debug[pc])
+      else:
+        g.cacheSeqs[destKey].add val
+      recordAdd(c, c.debug[pc], destKey, val)
+    of opcNcsIncl:
+      let g = c.graph
+      let destKey = regs[ra].node.strVal
+      let val = regs[instr.regB].node
+      if not contains(g.cacheSeqs, destKey):
+        g.cacheSeqs[destKey] = newTree(nkStmtList, val)
+      else:
+        block search:
+          for existing in g.cacheSeqs[destKey]:
+            if exprStructuralEquivalent(existing, val, strictSymEquality=true):
+              break search
+          g.cacheSeqs[destKey].add val
+      recordIncl(c, c.debug[pc], destKey, val)
+    of opcNcsLen:
+      let g = c.graph
+      decodeB(rkInt)
+      let destKey = regs[rb].node.strVal
+      regs[ra].intVal =
+        if contains(g.cacheSeqs, destKey): g.cacheSeqs[destKey].len else: 0
+    of opcNcsAt:
+      let g = c.graph
+      decodeBC(rkNode)
+      let idx = regs[rc].intVal
+      let destKey = regs[rb].node.strVal
+      if contains(g.cacheSeqs, destKey) and idx <% g.cacheSeqs[destKey].len:
+        regs[ra].node = g.cacheSeqs[destKey][idx.int]
+      else:
+        stackTrace(c, tos, pc, errIndexOutOfBounds)
+    of opcNctPut:
+      let g = c.graph
+      let destKey = regs[ra].node.strVal
+      let key = regs[instr.regB].node.strVal
+      let val = regs[instr.regC].node
+      if not contains(g.cacheTables, destKey):
+        g.cacheTables[destKey] = initBTree[string, PNode]()
+      if not contains(g.cacheTables[destKey], key):
+        g.cacheTables[destKey].add(key, val)
+        recordPut(c, c.debug[pc], destKey, key, val)
+      else:
+        stackTrace(c, tos, pc, "key already exists: " & key)
+    of opcNctLen:
+      let g = c.graph
+      decodeB(rkInt)
+      let destKey = regs[rb].node.strVal
+      regs[ra].intVal =
+        if contains(g.cacheTables, destKey): g.cacheTables[destKey].len else: 0
+    of opcNctGet:
+      let g = c.graph
+      decodeBC(rkNode)
+      let destKey = regs[rb].node.strVal
+      let key = regs[rc].node.strVal
+      if contains(g.cacheTables, destKey):
+        if contains(g.cacheTables[destKey], key):
+          regs[ra].node = getOrDefault(g.cacheTables[destKey], key)
+        else:
+          stackTrace(c, tos, pc, "key does not exist: " & key)
+      else:
+        stackTrace(c, tos, pc, "key does not exist: " & destKey)
+    of opcNctHasNext:
+      let g = c.graph
+      decodeBC(rkInt)
+      let destKey = regs[rb].node.strVal
+      regs[ra].intVal =
+        if g.cacheTables.contains(destKey):
+          ord(btrees.hasNext(g.cacheTables[destKey], regs[rc].intVal.int))
+        else:
+          0
+    of opcNctNext:
+      let g = c.graph
+      decodeBC(rkNode)
+      let destKey = regs[rb].node.strVal
+      let index = regs[rc].intVal
+      if contains(g.cacheTables, destKey):
+        let (k, v, nextIndex) = btrees.next(g.cacheTables[destKey], index.int)
+        regs[ra].node = newTree(nkTupleConstr, newStrNode(k, c.debug[pc]), v,
+                                newIntNode(nkIntLit, nextIndex))
+      else:
+        stackTrace(c, tos, pc, "key does not exist: " & destKey)
+
     of opcTypeTrait:
       # XXX only supports 'name' for now; we can use regC to encode the
       # type trait operation
@@ -1583,7 +1692,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       let rb = instr.regB
       inc pc
       let typ = c.types[c.code[pc].regBx - wordExcess]
-      putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.config))
+      putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.cache, c.config))
     of opcMarshalStore:
       decodeB(rkNode)
       inc pc
@@ -1654,36 +1763,30 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode =
 
 include vmops
 
-# for now we share the 'globals' environment. XXX Coming soon: An API for
-# storing&loading the 'globals' environment to get what a component system
-# requires.
-var
-  globalCtx*: PCtx
-
-proc setupGlobalCtx(module: PSym; cache: IdentCache; graph: ModuleGraph) =
-  if globalCtx.isNil:
-    globalCtx = newCtx(module, cache, graph)
-    registerAdditionalOps(globalCtx)
+proc setupGlobalCtx(module: PSym; graph: ModuleGraph) =
+  if graph.vm.isNil:
+    graph.vm = newCtx(module, graph.cache, graph)
+    registerAdditionalOps(PCtx graph.vm)
   else:
-    refresh(globalCtx, module)
+    refresh(PCtx graph.vm, module)
 
-proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
+proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
   #var c = newEvalContext(module, emRepl)
   #c.features = {allowCast, allowFFI, allowInfiniteLoops}
   #pushStackFrame(c, newStackFrame())
 
   # XXX produce a new 'globals' environment here:
-  setupGlobalCtx(module, cache, graph)
-  result = globalCtx
+  setupGlobalCtx(module, graph)
+  result = PCtx graph.vm
   when hasFFI:
-    globalCtx.features = {allowFFI, allowCast}
+    PCtx(graph.vm).features = {allowFFI, allowCast}
 
 proc myProcess(c: PPassContext, n: PNode): PNode =
   let c = PCtx(c)
   # don't eval errornous code:
   if c.oldErrorCount == c.config.errorCounter:
     evalStmt(c, n)
-    result = emptyNode
+    result = newNodeI(nkEmpty, n.info)
   else:
     result = n
   c.oldErrorCount = c.config.errorCounter
@@ -1691,19 +1794,19 @@ proc myProcess(c: PPassContext, n: PNode): PNode =
 proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode =
   myProcess(c, n)
 
-const evalPass* = makePass(myOpen, nil, myProcess, myClose)
+const evalPass* = makePass(myOpen, myProcess, myClose)
 
-proc evalConstExprAux(module: PSym; cache: IdentCache;
+proc evalConstExprAux(module: PSym;
                       g: ModuleGraph; prc: PSym, n: PNode,
                       mode: TEvalMode): PNode =
   let n = transformExpr(g, module, n)
-  setupGlobalCtx(module, cache, g)
-  var c = globalCtx
+  setupGlobalCtx(module, g)
+  var c = PCtx g.vm
   let oldMode = c.mode
   defer: c.mode = oldMode
   c.mode = mode
   let start = genExpr(c, n, requiresValue = mode!=emStaticStmt)
-  if c.code[start].opcode == opcEof: return emptyNode
+  if c.code[start].opcode == opcEof: return newNodeI(nkEmpty, n.info)
   assert c.code[start].opcode != opcEof
   when debugEchoCode: c.echoCode start
   var tos = PStackFrame(prc: prc, comesFrom: 0, next: nil)
@@ -1712,17 +1815,17 @@ proc evalConstExprAux(module: PSym; cache: IdentCache;
   result = rawExecute(c, start, tos).regToNode
   if result.info.col < 0: result.info = n.info
 
-proc evalConstExpr*(module: PSym; cache: IdentCache, g: ModuleGraph; e: PNode): PNode =
-  result = evalConstExprAux(module, cache, g, nil, e, emConst)
+proc evalConstExpr*(module: PSym; g: ModuleGraph; e: PNode): PNode =
+  result = evalConstExprAux(module, g, nil, e, emConst)
 
-proc evalStaticExpr*(module: PSym; cache: IdentCache, g: ModuleGraph; e: PNode, prc: PSym): PNode =
-  result = evalConstExprAux(module, cache, g, prc, e, emStaticExpr)
+proc evalStaticExpr*(module: PSym; g: ModuleGraph; e: PNode, prc: PSym): PNode =
+  result = evalConstExprAux(module, g, prc, e, emStaticExpr)
 
-proc evalStaticStmt*(module: PSym; cache: IdentCache, g: ModuleGraph; e: PNode, prc: PSym) =
-  discard evalConstExprAux(module, cache, g, prc, e, emStaticStmt)
+proc evalStaticStmt*(module: PSym; g: ModuleGraph; e: PNode, prc: PSym) =
+  discard evalConstExprAux(module, g, prc, e, emStaticStmt)
 
-proc setupCompileTimeVar*(module: PSym; cache: IdentCache, g: ModuleGraph; n: PNode) =
-  discard evalConstExprAux(module, cache, g, nil, n, emStaticStmt)
+proc setupCompileTimeVar*(module: PSym; g: ModuleGraph; n: PNode) =
+  discard evalConstExprAux(module, g, nil, n, emStaticStmt)
 
 proc setupMacroParam(x: PNode, typ: PType): TFullReg =
   case typ.kind
@@ -1748,13 +1851,12 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
 
 # to prevent endless recursion in macro instantiation
 const evalMacroLimit = 1000
-var evalMacroCounter: int
 
-proc evalMacroCall*(module: PSym; cache: IdentCache; g: ModuleGraph;
+proc evalMacroCall*(module: PSym; g: ModuleGraph;
                     n, nOrig: PNode, sym: PSym): PNode =
   # XXX globalError() is ugly here, but I don't know a better solution for now
-  inc(evalMacroCounter)
-  if evalMacroCounter > evalMacroLimit:
+  inc(g.config.evalMacroCounter)
+  if g.config.evalMacroCounter > evalMacroLimit:
     globalError(g.config, n.info, "macro instantiation too nested")
 
   # immediate macros can bypass any type and arity checking so we check the
@@ -1763,8 +1865,8 @@ proc evalMacroCall*(module: PSym; cache: IdentCache; g: ModuleGraph;
     globalError(g.config, n.info, "in call '$#' got $#, but expected $# argument(s)" % [
         n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)])
 
-  setupGlobalCtx(module, cache, g)
-  var c = globalCtx
+  setupGlobalCtx(module, g)
+  var c = PCtx g.vm
   c.comesFromHeuristic.line = 0'u16
 
   c.callsite = nOrig
@@ -1795,12 +1897,12 @@ proc evalMacroCall*(module: PSym; cache: IdentCache; g: ModuleGraph;
       if idx < n.len:
         tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
       else:
-        dec(evalMacroCounter)
+        dec(g.config.evalMacroCounter)
         c.callsite = nil
         localError(c.config, n.info, "expected " & $gp.len &
                    " generic parameter(s)")
     elif gp[i].sym.typ.kind in {tyStatic, tyTypeDesc}:
-      dec(evalMacroCounter)
+      dec(g.config.evalMacroCounter)
       c.callsite = nil
       globalError(c.config, n.info, "static[T] or typedesc nor supported for .immediate macros")
   # temporary storage:
@@ -1808,5 +1910,5 @@ proc evalMacroCall*(module: PSym; cache: IdentCache; g: ModuleGraph;
   result = rawExecute(c, start, tos).regToNode
   if result.info.line < 0: result.info = n.info
   if cyclicTree(result): globalError(c.config, n.info, "macro produced a cyclic tree")
-  dec(evalMacroCounter)
+  dec(g.config.evalMacroCounter)
   c.callsite = nil
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index e10a62006..f7466b392 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -10,7 +10,8 @@
 ## This module contains the type definitions for the new evaluation engine.
 ## An instruction is 1-3 int32s in memory, it is a register based VM.
 
-import ast, passes, msgs, idents, intsets, options, modulegraphs
+import ast, passes, msgs, idents, intsets, options, modulegraphs, lineinfos,
+  tables, btrees
 
 const
   byteExcess* = 128 # we use excess-K for immediates
@@ -35,7 +36,6 @@ type
     opcAsgnFloat,
     opcAsgnRef,
     opcAsgnComplex,
-    opcRegToNode,
     opcNodeToReg,
 
     opcLdArr,  # a = b[c]
@@ -91,6 +91,9 @@ type
     opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetType, opcNSetStrVal,
     opcNNewNimNode, opcNCopyNimNode, opcNCopyNimTree, opcNDel, opcGenSym,
 
+    opcNccValue, opcNccInc, opcNcsAdd, opcNcsIncl, opcNcsLen, opcNcsAt,
+    opcNctPut, opcNctLen, opcNctGet, opcNctHasNext, opcNctNext,
+
     opcSlurp,
     opcGorge,
     opcParseExprToAst,
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 2c92348a6..865ecd36e 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -7,11 +7,11 @@
 #    distribution, for details about the copyright.
 #
 
-import ast, types, msgs, os, streams, options, idents
+import ast, types, msgs, os, streams, options, idents, lineinfos
 
 proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): string =
   try:
-    var filename = parentDir(info.toFullPath) / file
+    var filename = parentDir(toFullPath(conf, info)) / file
     if not fileExists(filename):
       filename = findFile(conf, file)
     result = readFile(filename)
@@ -23,8 +23,8 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): str
     localError(conf, info, "cannot open file: " & file)
     result = ""
 
-proc atomicTypeX(name: string; m: TMagic; t: PType; info: TLineInfo): PNode =
-  let sym = newSym(skType, getIdent(name), t.owner, info)
+proc atomicTypeX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo): PNode =
+  let sym = newSym(skType, getIdent(cache, name), t.owner, info)
   sym.magic = m
   sym.typ = t
   result = newSymNode(sym)
@@ -34,51 +34,51 @@ proc atomicTypeX(s: PSym; info: TLineInfo): PNode =
   result = newSymNode(s)
   result.info = info
 
-proc mapTypeToAstX(t: PType; info: TLineInfo;
+proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
                    inst=false; allowRecursionX=false): PNode
 
-proc mapTypeToBracketX(name: string; m: TMagic; t: PType; info: TLineInfo;
+proc mapTypeToBracketX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo;
                        inst=false): PNode =
   result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
-  result.add atomicTypeX(name, m, t, info)
+  result.add atomicTypeX(cache, name, m, t, info)
   for i in 0 ..< t.len:
     if t.sons[i] == nil:
-      let void = atomicTypeX("void", mVoid, t, info)
+      let void = atomicTypeX(cache, "void", mVoid, t, info)
       void.typ = newType(tyVoid, t.owner)
       result.add void
     else:
-      result.add mapTypeToAstX(t.sons[i], info, inst)
+      result.add mapTypeToAstX(cache, t.sons[i], info, inst)
 
-proc objectNode(n: PNode): PNode =
+proc objectNode(cache: IdentCache; n: PNode): PNode =
   if n.kind == nkSym:
     result = newNodeI(nkIdentDefs, n.info)
     result.add n  # name
-    result.add mapTypeToAstX(n.sym.typ, n.info, true, false)  # type
-    result.add ast.emptyNode  # no assigned value
+    result.add mapTypeToAstX(cache, n.sym.typ, n.info, true, false)  # type
+    result.add newNodeI(nkEmpty, n.info)  # no assigned value
   else:
     result = copyNode(n)
     for i in 0 ..< n.safeLen:
-      result.add objectNode(n[i])
+      result.add objectNode(cache, n[i])
 
-proc mapTypeToAstX(t: PType; info: TLineInfo;
+proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
                    inst=false; allowRecursionX=false): PNode =
   var allowRecursion = allowRecursionX
-  template atomicType(name, m): untyped = atomicTypeX(name, m, t, info)
+  template atomicType(name, m): untyped = atomicTypeX(cache, name, m, t, info)
   template atomicType(s): untyped = atomicTypeX(s, info)
-  template mapTypeToAst(t,info): untyped = mapTypeToAstX(t, info, inst)
-  template mapTypeToAstR(t,info): untyped = mapTypeToAstX(t, info, inst, true)
+  template mapTypeToAst(t,info): untyped = mapTypeToAstX(cache, t, info, inst)
+  template mapTypeToAstR(t,info): untyped = mapTypeToAstX(cache, t, info, inst, true)
   template mapTypeToAst(t,i,info): untyped =
-    if i<t.len and t.sons[i]!=nil: mapTypeToAstX(t.sons[i], info, inst)
-    else: ast.emptyNode
+    if i<t.len and t.sons[i]!=nil: mapTypeToAstX(cache, t.sons[i], info, inst)
+    else: newNodeI(nkEmpty, info)
   template mapTypeToBracket(name, m, t, info): untyped =
-    mapTypeToBracketX(name, m, t, info, inst)
+    mapTypeToBracketX(cache, name, m, t, info, inst)
   template newNodeX(kind): untyped =
     newNodeIT(kind, if t.n.isNil: info else: t.n.info, t)
   template newIdentDefs(n,t): untyped =
     var id = newNodeX(nkIdentDefs)
     id.add n  # name
     id.add mapTypeToAst(t, info)  # type
-    id.add ast.emptyNode  # no assigned value
+    id.add newNodeI(nkEmpty, info)  # no assigned value
     id
   template newIdentDefs(s): untyped = newIdentDefs(s, s.typ)
 
@@ -103,7 +103,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
     result.add atomicType("array", mArray)
     if inst and t.sons[0].kind == tyRange:
       var rng = newNodeX(nkInfix)
-      rng.add newIdentNode(getIdent(".."), info)
+      rng.add newIdentNode(getIdent(cache, ".."), info)
       rng.add t.sons[0].n.sons[0].copyTree
       rng.add t.sons[0].n.sons[1].copyTree
       result.add rng
@@ -132,14 +132,14 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
         for i in 1 ..< t.len-1:
           result.add mapTypeToAst(t.sons[i], info)
     else:
-      result = mapTypeToAstX(t.lastSon, info, inst, allowRecursion)
+      result = mapTypeToAstX(cache, t.lastSon, info, inst, allowRecursion)
   of tyGenericBody:
     if inst:
       result = mapTypeToAstR(t.lastSon, info)
     else:
       result = mapTypeToAst(t.lastSon, info)
   of tyAlias:
-    result = mapTypeToAstX(t.lastSon, info, inst, allowRecursion)
+    result = mapTypeToAstX(cache, t.lastSon, info, inst, allowRecursion)
   of tyOrdinal:
     result = mapTypeToAst(t.lastSon, info)
   of tyDistinct:
@@ -156,22 +156,22 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
   of tyObject:
     if inst:
       result = newNodeX(nkObjectTy)
-      result.add ast.emptyNode  # pragmas not reconstructed yet
-      if t.sons[0] == nil: result.add ast.emptyNode  # handle parent object
+      result.add newNodeI(nkEmpty, info)  # pragmas not reconstructed yet
+      if t.sons[0] == nil: result.add newNodeI(nkEmpty, info)  # handle parent object
       else:
         var nn = newNodeX(nkOfInherit)
         nn.add mapTypeToAst(t.sons[0], info)
         result.add nn
       if t.n.len > 0:
-        result.add objectNode(t.n)
+        result.add objectNode(cache, t.n)
       else:
-        result.add ast.emptyNode
+        result.add newNodeI(nkEmpty, info)
     else:
       if allowRecursion or t.sym == nil:
         result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t)
-        result.add ast.emptyNode
+        result.add newNodeI(nkEmpty, info)
         if t.sons[0] == nil:
-          result.add ast.emptyNode
+          result.add newNodeI(nkEmpty, info)
         else:
           result.add mapTypeToAst(t.sons[0], info)
         result.add copyTree(t.n)
@@ -179,7 +179,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
         result = atomicType(t.sym)
   of tyEnum:
     result = newNodeIT(nkEnumTy, if t.n.isNil: info else: t.n.info, t)
-    result.add ast.emptyNode  # pragma node, currently always empty for enum
+    result.add newNodeI(nkEmpty, info)  # pragma node, currently always empty for enum
     for c in t.n.sons:
       result.add copyTree(c)
   of tyTuple:
@@ -223,13 +223,13 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
       result = newNodeX(nkProcTy)
       var fp = newNodeX(nkFormalParams)
       if t.sons[0] == nil:
-        fp.add ast.emptyNode
+        fp.add newNodeI(nkEmpty, info)
       else:
         fp.add mapTypeToAst(t.sons[0], t.n[0].info)
       for i in 1..<t.sons.len:
         fp.add newIdentDefs(t.n[i], t.sons[i])
       result.add fp
-      result.add ast.emptyNode  # pragmas aren't reconstructed yet
+      result.add newNodeI(nkEmpty, info)  # pragmas aren't reconstructed yet
     else:
       result = mapTypeToBracket("proc", mNone, t, info)
   of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info)
@@ -283,15 +283,15 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
         result.add t.n.copyTree
   of tyUnused, tyOptAsRef: assert(false, "mapTypeToAstX")
 
-proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode =
-  result = mapTypeToAstX(t, info, false, true)
+proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode =
+  result = mapTypeToAstX(cache, t, info, false, true)
 
 # the "Inst" version includes generic parameters in the resulting type tree
 # and also tries to look like the corresponding Nim type declaration
-proc opMapTypeInstToAst*(t: PType; info: TLineInfo): PNode =
-  result = mapTypeToAstX(t, info, true, false)
+proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode =
+  result = mapTypeToAstX(cache, t, info, true, false)
 
 # the "Impl" version includes generic parameters in the resulting type tree
 # and also tries to look like the corresponding Nim type implementation
-proc opMapTypeImplToAst*(t: PType; info: TLineInfo): PNode =
-  result = mapTypeToAstX(t, info, true, true)
+proc opMapTypeImplToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode =
+  result = mapTypeToAstX(cache, t, info, true, true)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 8a3c7e2e6..d2243376c 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -29,7 +29,7 @@
 
 import
   strutils, ast, astalgo, types, msgs, renderer, vmdef,
-  trees, intsets, rodread, magicsys, options, lowerings
+  trees, intsets, magicsys, options, lowerings, lineinfos
 import platform
 from os import splitFile
 
@@ -40,8 +40,8 @@ type
   TGenFlag = enum gfAddrOf, gfFieldAccess
   TGenFlags = set[TGenFlag]
 
-proc debugInfo(info: TLineInfo): string =
-  result = info.toFilename.splitFile.name & ":" & $info.line
+proc debugInfo(c: PCtx; info: TLineInfo): string =
+  result = toFilename(c.config, info).splitFile.name & ":" & $info.line
 
 proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
   # first iteration: compute all necessary labels:
@@ -85,7 +85,7 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
     else:
       result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
     result.add("\t#")
-    result.add(debugInfo(c.debug[i]))
+    result.add(debugInfo(c, c.debug[i]))
     result.add("\n")
     inc i
 
@@ -550,7 +550,7 @@ proc genField(c: PCtx; n: PNode): TRegister =
   result = s.position
 
 proc genIndex(c: PCtx; n: PNode; arr: PType): TRegister =
-  if arr.skipTypes(abstractInst).kind == tyArray and (let x = firstOrd(arr);
+  if arr.skipTypes(abstractInst).kind == tyArray and (let x = firstOrd(c.config, arr);
       x != 0):
     let tmp = c.genx(n)
     # freeing the temporary here means we can produce:  regA = regA - Imm
@@ -767,12 +767,12 @@ proc genIntCast(c: PCtx; n: PNode; dest: var TDest) =
   var unsignedIntegers = {tyUInt8..tyUInt32, tyChar}
   let src = n.sons[1].typ.skipTypes(abstractRange)#.kind
   let dst = n.sons[0].typ.skipTypes(abstractRange)#.kind
-  let src_size = src.getSize
+  let src_size = getSize(c.config, src)
 
-  if platform.intSize < 8:
+  if c.config.target.intSize < 8:
     signedIntegers.incl(tyInt)
     unsignedIntegers.incl(tyUInt)
-  if src_size == dst.getSize and src.kind in allowedIntegers and
+  if src_size == getSize(c.config, dst) and src.kind in allowedIntegers and
                                  dst.kind in allowedIntegers:
     let tmp = c.genx(n.sons[1])
     var tmp2 = c.getTemp(n.sons[1].typ)
@@ -804,6 +804,17 @@ proc genIntCast(c: PCtx; n: PNode; dest: var TDest) =
   else:
     globalError(c.config, n.info, "VM is only allowed to 'cast' between integers of same size")
 
+proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) =
+  unused(c, n, dest)
+  var
+    tmp1 = c.genx(n[1])
+    tmp2 = c.genx(n[2])
+    tmp3 = c.genx(n[3])
+  c.gABC(n, opcode, tmp1, tmp2, tmp3)
+  c.freeTemp(tmp1)
+  c.freeTemp(tmp2)
+  c.freeTemp(tmp3)
+
 proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   case m
   of mAnd: c.genAndOr(n, opcFJmp, dest)
@@ -966,7 +977,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.freeTemp(tmp)
   of mSwap:
     unused(c, n, dest)
-    c.gen(lowerSwap(n, if c.prc == nil: c.module else: c.prc.sym))
+    c.gen(lowerSwap(c.graph, n, if c.prc == nil: c.module else: c.prc.sym))
   of mIsNil: genUnaryABC(c, n, dest, opcIsNil)
   of mCopyStr:
     if dest < 0: dest = c.getTemp(n.typ)
@@ -1068,20 +1079,25 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mNLen: genUnaryABI(c, n, dest, opcLenSeq, nimNodeFlag)
   of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl)
   of mNChild: genBinaryABC(c, n, dest, opcNChild)
-  of mNSetChild, mNDel:
-    unused(c, n, dest)
-    var
-      tmp1 = c.genx(n.sons[1])
-      tmp2 = c.genx(n.sons[2])
-      tmp3 = c.genx(n.sons[3])
-    c.gABC(n, if m == mNSetChild: opcNSetChild else: opcNDel, tmp1, tmp2, tmp3)
-    c.freeTemp(tmp1)
-    c.freeTemp(tmp2)
-    c.freeTemp(tmp3)
+  of mNSetChild: genVoidABC(c, n, dest, opcNSetChild)
+  of mNDel: genVoidABC(c, n, dest, opcNDel)
   of mNAdd: genBinaryABC(c, n, dest, opcNAdd)
   of mNAddMultiple: genBinaryABC(c, n, dest, opcNAddMultiple)
   of mNKind: genUnaryABC(c, n, dest, opcNKind)
   of mNSymKind: genUnaryABC(c, n, dest, opcNSymKind)
+
+  of mNccValue: genUnaryABC(c, n, dest, opcNccValue)
+  of mNccInc: genBinaryABC(c, n, dest, opcNccInc)
+  of mNcsAdd: genBinaryABC(c, n, dest, opcNcsAdd)
+  of mNcsIncl: genBinaryABC(c, n, dest, opcNcsIncl)
+  of mNcsLen: genUnaryABC(c, n, dest, opcNcsLen)
+  of mNcsAt: genBinaryABC(c, n, dest, opcNcsAt)
+  of mNctPut: genVoidABC(c, n, dest, opcNctPut)
+  of mNctLen: genUnaryABC(c, n, dest, opcNctLen)
+  of mNctGet: genBinaryABC(c, n, dest, opcNctGet)
+  of mNctHasNext: genBinaryABC(c, n, dest, opcNctHasNext)
+  of mNctNext: genBinaryABC(c, n, dest, opcNctNext)
+
   of mNIntVal: genUnaryABC(c, n, dest, opcNIntVal)
   of mNFloatVal: genUnaryABC(c, n, dest, opcNFloatVal)
   of mNSymbol: genUnaryABC(c, n, dest, opcNSymbol)
@@ -1544,7 +1560,6 @@ proc getNullValueAux(obj: PNode, result: PNode; conf: ConfigRef) =
 
 proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
   var t = skipTypes(typ, abstractRange-{tyTypeDesc})
-  result = emptyNode
   case t.kind
   of tyBool, tyEnum, tyChar, tyInt..tyInt64:
     result = newNodeIT(nkIntLit, info, t)
@@ -1576,7 +1591,7 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
     getNullValueAux(t.n, result, conf)
   of tyArray:
     result = newNodeIT(nkBracket, info, t)
-    for i in countup(0, int(lengthOrd(t)) - 1):
+    for i in countup(0, int(lengthOrd(conf, t)) - 1):
       addSon(result, getNullValue(elemType(t), info, conf))
   of tyTuple:
     result = newNodeIT(nkTupleConstr, info, t)
@@ -1590,6 +1605,7 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
     result = newNodeIT(nkBracket, info, t)
   else:
     globalError(conf, info, "cannot create null element for: " & $t.kind)
+    result = newNodeI(nkEmpty, info)
 
 proc ldNullOpcode(t: PType): TOpcode =
   assert t != nil
@@ -1796,6 +1812,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
       if s.magic != mNone:
         genMagic(c, n, dest, s.magic)
       elif matches(s, "stdlib", "marshal", "to"):
+        # XXX marshal load&store should not be opcodes, but use the
+        # general callback mechanisms.
         genMarshalLoad(c, n, dest)
       elif matches(s, "stdlib", "marshal", "$$"):
         genMarshalStore(c, n, dest)
@@ -1881,7 +1899,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     else:
       dest = tmp0
   of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma,
-     nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt:
+     nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt:
     unused(c, n, dest)
   of nkStringToCString, nkCStringToString:
     gen(c, n.sons[0], dest)
@@ -2009,7 +2027,7 @@ proc genProc(c: PCtx; s: PSym): int =
     #c.removeLastEof
     result = c.code.len+1 # skip the jump instruction
     if x.kind == nkEmpty:
-      x = newTree(nkBracket, newIntNode(nkIntLit, result), ast.emptyNode)
+      x = newTree(nkBracket, newIntNode(nkIntLit, result), x)
     else:
       x.sons[0] = newIntNode(nkIntLit, result)
     s.ast.sons[miscPos] = x
diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim
index f38be7c29..eb01b3514 100644
--- a/compiler/vmmarshal.nim
+++ b/compiler/vmmarshal.nim
@@ -10,7 +10,7 @@
 ## Implements marshaling for the VM.
 
 import streams, json, intsets, tables, ast, astalgo, idents, types, msgs,
-  options
+  options, lineinfos
 
 proc ptrToInt(x: PNode): int {.inline.} =
   result = cast[int](x) # don't skip alignment
@@ -140,6 +140,7 @@ proc storeAny*(s: var string; t: PType; a: PNode; conf: ConfigRef) =
 
 proc loadAny(p: var JsonParser, t: PType,
              tab: var Table[BiggestInt, PNode];
+             cache: IdentCache;
              conf: ConfigRef): PNode =
   case t.kind
   of tyNone: assert false
@@ -174,7 +175,7 @@ proc loadAny(p: var JsonParser, t: PType,
     next(p)
     result = newNode(nkBracket)
     while p.kind != jsonArrayEnd and p.kind != jsonEof:
-      result.add loadAny(p, t.elemType, tab, conf)
+      result.add loadAny(p, t.elemType, tab, cache, conf)
     if p.kind == jsonArrayEnd: next(p)
     else: raiseParseErr(p, "']' end of array expected")
   of tySequence:
@@ -186,7 +187,7 @@ proc loadAny(p: var JsonParser, t: PType,
       next(p)
       result = newNode(nkBracket)
       while p.kind != jsonArrayEnd and p.kind != jsonEof:
-        result.add loadAny(p, t.elemType, tab, conf)
+        result.add loadAny(p, t.elemType, tab, cache, conf)
       if p.kind == jsonArrayEnd: next(p)
       else: raiseParseErr(p, "")
     else:
@@ -202,7 +203,7 @@ proc loadAny(p: var JsonParser, t: PType,
       next(p)
       if i >= t.len:
         raiseParseErr(p, "too many fields to tuple type " & typeToString(t))
-      result.add loadAny(p, t.sons[i], tab, conf)
+      result.add loadAny(p, t.sons[i], tab, cache, conf)
       inc i
     if p.kind == jsonObjectEnd: next(p)
     else: raiseParseErr(p, "'}' end of object expected")
@@ -214,7 +215,7 @@ proc loadAny(p: var JsonParser, t: PType,
     while p.kind != jsonObjectEnd and p.kind != jsonEof:
       if p.kind != jsonString:
         raiseParseErr(p, "string expected for a field name")
-      let ident = getIdent(p.str)
+      let ident = getIdent(cache, p.str)
       let field = lookupInRecord(t.n, ident)
       if field.isNil:
         raiseParseErr(p, "unknown field for object of type " & typeToString(t))
@@ -224,7 +225,7 @@ proc loadAny(p: var JsonParser, t: PType,
         setLen(result.sons, pos + 1)
       let fieldNode = newNode(nkExprColonExpr)
       fieldNode.addSon(newSymNode(newSym(skField, ident, nil, unknownLineInfo())))
-      fieldNode.addSon(loadAny(p, field.typ, tab, conf))
+      fieldNode.addSon(loadAny(p, field.typ, tab, cache, conf))
       result.sons[pos] = fieldNode
     if p.kind == jsonObjectEnd: next(p)
     else: raiseParseErr(p, "'}' end of object expected")
@@ -233,7 +234,7 @@ proc loadAny(p: var JsonParser, t: PType,
     next(p)
     result = newNode(nkCurly)
     while p.kind != jsonArrayEnd and p.kind != jsonEof:
-      result.add loadAny(p, t.lastSon, tab, conf)
+      result.add loadAny(p, t.lastSon, tab, cache, conf)
       next(p)
     if p.kind == jsonArrayEnd: next(p)
     else: raiseParseErr(p, "']' end of array expected")
@@ -252,7 +253,7 @@ proc loadAny(p: var JsonParser, t: PType,
       if p.kind == jsonInt:
         let idx = p.getInt
         next(p)
-        result = loadAny(p, t.lastSon, tab, conf)
+        result = loadAny(p, t.lastSon, tab, cache, conf)
         tab[idx] = result
       else: raiseParseErr(p, "index for ref type expected")
       if p.kind == jsonArrayEnd: next(p)
@@ -280,14 +281,14 @@ proc loadAny(p: var JsonParser, t: PType,
       return
     raiseParseErr(p, "float expected")
   of tyRange, tyGenericInst, tyAlias, tySink:
-    result = loadAny(p, t.lastSon, tab, conf)
+    result = loadAny(p, t.lastSon, tab, cache, conf)
   else:
     internalError conf, "cannot marshal at compile-time " & t.typeToString
 
-proc loadAny*(s: string; t: PType; conf: ConfigRef): PNode =
+proc loadAny*(s: string; t: PType; cache: IdentCache; conf: ConfigRef): PNode =
   var tab = initTable[BiggestInt, PNode]()
   var p: JsonParser
   open(p, newStringStream(s), "unknown file")
   next(p)
-  result = loadAny(p, t, tab, conf)
+  result = loadAny(p, t, tab, cache, conf)
   close(p)
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
index 617295b0d..a7d47d7a3 100644
--- a/compiler/vmops.nim
+++ b/compiler/vmops.nim
@@ -107,15 +107,16 @@ proc registerAdditionalOps*(c: PCtx) =
   wrap1f_math(ceil)
   wrap2f_math(fmod)
 
-  wrap2s(getEnv, ospathsop)
-  wrap1s(existsEnv, ospathsop)
-  wrap2svoid(putEnv, ospathsop)
-  wrap1s(dirExists, osop)
-  wrap1s(fileExists, osop)
-  wrap2svoid(writeFile, systemop)
-  wrap1s(readFile, systemop)
-  systemop getCurrentExceptionMsg
-  registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
-    setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
-  systemop gorgeEx
+  when defined(nimcore):
+    wrap2s(getEnv, ospathsop)
+    wrap1s(existsEnv, ospathsop)
+    wrap2svoid(putEnv, ospathsop)
+    wrap1s(dirExists, osop)
+    wrap1s(fileExists, osop)
+    wrap2svoid(writeFile, systemop)
+    wrap1s(readFile, systemop)
+    systemop getCurrentExceptionMsg
+    registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
+      setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
+    systemop gorgeEx
   macrosop getProjectPath
diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim
index c0f7b7b20..1ea1deb2d 100644
--- a/compiler/writetracking.nim
+++ b/compiler/writetracking.nim
@@ -15,7 +15,8 @@
 ##   * Computing an aliasing relation based on the assignments. This relation
 ##     is then used to compute the 'writes' and 'escapes' effects.
 
-import intsets, idents, ast, astalgo, trees, renderer, msgs, types, options
+import intsets, idents, ast, astalgo, trees, renderer, msgs, types, options,
+  lineinfos
 
 const
   debug = false
diff --git a/doc/docgen.rst b/doc/docgen.rst
index e6693e153..d196b3a18 100644
--- a/doc/docgen.rst
+++ b/doc/docgen.rst
@@ -303,9 +303,9 @@ symbols in the `system module <system.html>`_.
   `#len,seq[T] <system.html#len,seq[T]>`_
 * ``iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}`` **=>**
   `#pairs.i,seq[T] <system.html#pairs.i,seq[T]>`_
-* ``template newException[](exceptn: typedesc; message: string): expr`` **=>**
-  `#newException.t,typedesc,string
-  <system.html#newException.t,typedesc,string>`_
+* ``template newException[](exceptn: type; message: string): expr`` **=>**
+  `#newException.t,type,string
+  <system.html#newException.t,type,string>`_
 
 
 Index (idx) file format
diff --git a/doc/intern.txt b/doc/intern.txt
index dadb0eb05..b253cac42 100644
--- a/doc/intern.txt
+++ b/doc/intern.txt
@@ -38,10 +38,6 @@ Path           Purpose
 Bootstrapping the compiler
 ==========================
 
-As of version 0.8.5 the compiler is maintained in Nim. (The first versions
-have been implemented in Object Pascal.) The Python-based build system has
-been rewritten in Nim too.
-
 Compiling the compiler is a simple matter of running::
 
   nim c koch.nim
@@ -202,16 +198,131 @@ Compilation cache
 =================
 
 The implementation of the compilation cache is tricky: There are lots
-of issues to be solved for the front- and backend. In the following
-sections *global* means *shared between modules* or *property of the whole
-program*.
+of issues to be solved for the front- and backend.
+
+
+General approach: AST replay
+----------------------------
+
+We store a module's AST of a successful semantic check in a SQLite
+database. There are plenty of features that require a sub sequence
+to be re-applied, for example:
+
+.. code-block:: nim
+  {.compile: "foo.c".} # even if the module is loaded from the DB,
+                       # "foo.c" needs to be compiled/linked.
+
+The solution is to **re-play** the module's top level statements.
+This solves the problem without having to special case the logic
+that fills the internal seqs which are affected by the pragmas.
+
+In fact, this decribes how the AST should be stored in the database,
+as a "shallow" tree. Let's assume we compile module ``m`` with the
+following contents:
+
+.. code-block:: nim
+  import strutils
+
+  var x*: int = 90
+  {.compile: "foo.c".}
+  proc p = echo "p"
+  proc q = echo "q"
+  static:
+    echo "static"
+
+Conceptually this is the AST we store for the module:
+
+.. code-block:: nim
+  import strutils
+
+  var x*
+  {.compile: "foo.c".}
+  proc p
+  proc q
+  static:
+    echo "static"
+
+The symbol's ``ast`` field is loaded lazily, on demand. This is where most
+savings come from, only the shallow outer AST is reconstructed immediately.
+
+It is also important that the replay involves the ``import`` statement so
+that the dependencies are resolved properly.
+
+
+Shared global compiletime state
+-------------------------------
+
+Nim allows ``.global, compiletime`` variables that can be filled by macro
+invokations across different modules. This feature breaks modularity in a
+severe way. Plenty of different solutions have been proposed:
+
+- Restrict the types of global compiletime variables to ``Set[T]`` or
+  similar unordered, only-growable collections so that we can track
+  the module's write effects to these variables and reapply the changes
+  in a different order.
+- In every module compilation, reset the variable to its default value.
+- Provide a restrictive API that can load/save the compiletime state to
+  a file.
+
+(These solutions are not mutually exclusive.)
+
+Since we adopt the "replay the top level statements" idea, the natural
+solution to this problem is to emit pseudo top level statements that
+reflect the mutations done to the global variable. However, this is
+MUCH harder than it sounds, for example ``squeaknim`` uses this
+snippet:
+
+.. code-block:: nim
+  apicall.add(") module: '" & dllName & "'>\C" &
+              "\t^self externalCallFailed\C!\C\C")
+  stCode.add(st & "\C\t\"Generated by NimSqueak\"\C\t" & apicall)
+
+We can "replay" ``stCode.add`` only if the values of ``st``
+and ``apicall`` are known. And even then a hash table's ``add`` with its
+hashing mechanism is too hard to replay.
+
+In practice, things are worse still, consider ``someGlobal[i][j].add arg``.
+We only know the root is ``someGlobal`` but the concrete path to the data
+is unknown as is the value that is added. We could compute a "diff" between
+the global states and use that to compute a symbol patchset, but this is
+quite some work, expensive to do at runtime (it would need to run after
+every module has been compiled) and also would break for hash tables.
+
+We need an API that hides the complex aliasing problems by not relying
+on Nim's global variables. The obvious solution is to use string keys
+instead of global variables:
+
+.. code-block:: nim
+
+  proc cachePut*(key: string; value: string)
+  proc cacheGet*(key: string): string
+
+However, the values being strings/json is quite problematic: Many
+lookup tables that are built at compiletime embed *proc vars* and
+types which have no obvious string representation... Seems like
+AST diffing is still the best idea as it will not require to use
+an alien API and works with some existing Nimble packages, at least.
+
+On the other hand, in Nim's future I would like to replace the VM
+by native code. A diff algorithm wouldn't work for that.
+Instead the native code would work with an API like ``put``, ``get``:
+
+.. code-block:: nim
+
+  proc cachePut*(key: string; value: NimNode)
+  proc cacheGet*(key: string): NimNode
+
+The API should embrace the AST diffing notion: See the
+module ``macrocache`` for the final details.
 
 
-Frontend issues
----------------
 
 Methods and type converters
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+---------------------------
+
+In the following
+sections *global* means *shared between modules* or *property of the whole
+program*.
 
 Nim contains language features that are *global*. The best example for that
 are multi methods: Introducing a new method with the same name and some
@@ -238,20 +349,17 @@ If in the above example module ``B`` is re-compiled, but ``A`` is not then
 ``B`` needs to be aware of ``toBool`` even though  ``toBool`` is not referenced
 in ``B`` *explicitly*.
 
-Both the multi method and the type converter problems are solved by storing
-them in special sections in the ROD file that are loaded *unconditionally*
-when the ROD file is read.
+Both the multi method and the type converter problems are solved by the
+AST replay implementation.
+
 
 Generics
 ~~~~~~~~
 
-If we generate an instance of a generic, we'd like to re-use that
-instance if possible across module boundaries. However, this is not
-possible if the compilation cache is enabled. So we give up then and use
-the caching of generics only per module, not per project. This means that
-``--symbolFiles:on`` hurts a bit for efficiency. A better solution would
-be to persist the instantiations in a global cache per project. This might be
-implemented in later versions.
+We cache generic instantiations and need to ensure this caching works
+well with the incremental compilation feature. Since the cache is
+attached to the ``PSym`` datastructure, it should work without any
+special logic.
 
 
 Backend issues
@@ -259,13 +367,10 @@ Backend issues
 
 - Init procs must not be "forgotten" to be called.
 - Files must not be "forgotten" to be linked.
-- Anything that is contained in ``nim__dat.c`` is shared between modules
-  implicitly.
 - Method dispatchers are global.
 - DLL loading via ``dlsym`` is global.
 - Emulated thread vars are global.
 
-
 However the biggest problem is that dead code elimination breaks modularity!
 To see why, consider this scenario: The module ``G`` (for example the huge
 Gtk2 module...) is compiled with dead code elimination turned on. So none
@@ -274,25 +379,21 @@ of ``G``'s procs is generated at all.
 Then module ``B`` is compiled that requires ``G.P1``. Ok, no problem,
 ``G.P1`` is loaded from the symbol file and ``G.c`` now contains ``G.P1``.
 
-Then module ``A`` (that depends onto ``B`` and ``G``) is compiled and ``B``
+Then module ``A`` (that depends on ``B`` and ``G``) is compiled and ``B``
 and ``G`` are left unchanged. ``A`` requires ``G.P2``.
 
 So now ``G.c`` MUST contain both ``P1`` and ``P2``, but we haven't even
 loaded ``P1`` from the symbol file, nor do we want to because we then quickly
-would restore large parts of the whole program. But we also don't want to
-store ``P1`` in ``B.c`` because that would mean to store every symbol where
-it is referred from which ultimately means the main module and putting
-everything in a single C file.
+would restore large parts of the whole program.
 
-There is however another solution: The old file ``G.c`` containing ``P1`` is
-**merged** with the new file ``G.c`` containing ``P2``. This is the solution
-that is implemented in the C code generator (have a look at the ``ccgmerge``
-module). The merging may lead to *cruft* (aka dead code) in generated C code
-which can only be removed by recompiling a project with the compilation cache
-turned off. Nevertheless the merge solution is way superior to the
-cheap solution "turn off dead code elimination if the compilation cache is
-turned on".
+Solution
+~~~~~~~~ 
 
+The backend must have some logic so that if the currently processed module
+is from the compilation cache, the ``ast`` field is not accessed. Instead
+the generated C(++) for the symbol's body needs to be cached too and
+inserted back into the produced C file. This approach seems to deal with
+all the outlined problems above.
 
 
 Debugging Nim's memory management
@@ -317,7 +418,7 @@ Introduction
 
 I use the term *cell* here to refer to everything that is traced
 (sequences, refs, strings).
-This section describes how the new GC works.
+This section describes how the GC works.
 
 The basic algorithm is *Deferrent Reference Counting* with cycle detection.
 References on the stack are not counted for better performance and easier C
diff --git a/doc/manual.rst b/doc/manual.rst
index d0a778bdd..ddee7b4b9 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -741,22 +741,26 @@ For further details, see `Convertible relation
 
 Subrange types
 --------------
-A subrange type is a range of values from an ordinal type (the base
+A subrange type is a range of values from an ordinal or floating point type (the base
 type). To define a subrange type, one must specify it's limiting values: the
 lowest and highest value of the type:
 
 .. code-block:: nim
   type
     Subrange = range[0..5]
+    PositiveFloat = range[0.0..Inf]
 
 
 ``Subrange`` is a subrange of an integer which can only hold the values 0
-to 5. Assigning any other value to a variable of type ``Subrange`` is a
+to 5. ``PositiveFloat`` defines a subrange of all positive floating point values.
+NaN does not belong to any subrange of floating point types.
+Assigning any other value to a variable of type ``Subrange`` is a
 checked runtime error (or static error if it can be statically
 determined). Assignments from the base type to one of its subrange types
 (and vice versa) are allowed.
 
-A subrange type has the same size as its base type (``int`` in the example).
+A subrange type has the same size as its base type (``int`` in the 
+Subrange example).
 
 
 Pre-defined floating point types
@@ -1732,7 +1736,7 @@ But it seems all this boilerplate code needs to be repeated for the ``Euro``
 currency. This can be solved with templates_.
 
 .. code-block:: nim
-  template additive(typ: typedesc) =
+  template additive(typ: type) =
     proc `+` *(x, y: typ): typ {.borrow.}
     proc `-` *(x, y: typ): typ {.borrow.}
 
@@ -1740,13 +1744,13 @@ currency. This can be solved with templates_.
     proc `+` *(x: typ): typ {.borrow.}
     proc `-` *(x: typ): typ {.borrow.}
 
-  template multiplicative(typ, base: typedesc) =
+  template multiplicative(typ, base: type) =
     proc `*` *(x: typ, y: base): typ {.borrow.}
     proc `*` *(x: base, y: typ): typ {.borrow.}
     proc `div` *(x: typ, y: base): typ {.borrow.}
     proc `mod` *(x: typ, y: base): typ {.borrow.}
 
-  template comparable(typ: typedesc) =
+  template comparable(typ: type) =
     proc `<` * (x, y: typ): bool {.borrow.}
     proc `<=` * (x, y: typ): bool {.borrow.}
     proc `==` * (x, y: typ): bool {.borrow.}
@@ -2396,7 +2400,7 @@ argument's resolution:
   rem unresolvedExpression(undeclaredIdentifier)
 
 ``untyped`` and ``varargs[untyped]`` are the only metatype that are lazy in this sense, the other
-metatypes ``typed`` and ``typedesc`` are not lazy.
+metatypes ``typed`` and ``type`` are not lazy.
 
 
 Varargs matching
@@ -4274,29 +4278,6 @@ therefore very useful for type specialization within generic code:
         deletedKeys: seq[bool]
 
 
-Type operator
--------------
-
-The ``type`` (in many other languages called `typeof`:idx:) operator can
-be used to get the type of an expression:
-
-.. code-block:: nim
-  var x = 0
-  var y: type(x) # y has type int
-
-If ``type`` is used to determine the result type of a proc/iterator/converter
-call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the
-interpretation where ``c`` is an iterator is preferred over the
-other interpretations:
-
-.. code-block:: nim
-  import strutils
-
-  # strutils contains both a ``split`` proc and iterator, but since an
-  # an iterator is the preferred interpretation, `y` has the type ``string``:
-  var y: type("a b c".split)
-
-
 Type Classes
 ------------
 
@@ -4450,10 +4431,10 @@ the presence of callable symbols with specific signatures:
     OutputStream = concept var s
       s.write(string)
 
-In order to check for symbols accepting ``typedesc`` params, you must prefix
-the type with an explicit ``type`` modifier. The named instance of the type,
-following the ``concept`` keyword is also considered an explicit ``typedesc``
-value that will be matched only as a type.
+In order to check for symbols accepting ``type`` params, you must prefix
+the type with the explicit ``type`` modifier. The named instance of the
+type, following the ``concept`` keyword is also considered to have the
+explicit modifier and will be matched only as a type.
 
 .. code-block:: nim
   type
@@ -4513,7 +4494,7 @@ The concept types can be parametric just like the regular generic types:
   import typetraits
 
   type
-    AnyMatrix*[R, C: static[int]; T] = concept m, var mvar, type M
+    AnyMatrix*[R, C: static int; T] = concept m, var mvar, type M
       M.ValueType is T
       M.Rows == R
       M.Cols == C
@@ -4523,7 +4504,7 @@ The concept types can be parametric just like the regular generic types:
 
       type TransposedType = stripGenericParams(M)[C, R, T]
 
-    AnySquareMatrix*[N: static[int], T] = AnyMatrix[N, N, T]
+    AnySquareMatrix*[N: static int, T] = AnyMatrix[N, N, T]
 
     AnyTransform3D* = AnyMatrix[4, 4, float]
 
@@ -4542,7 +4523,7 @@ The concept types can be parametric just like the regular generic types:
   ### matrix.nim
 
   type
-    Matrix*[M, N: static[int]; T] = object
+    Matrix*[M, N: static int; T] = object
       data: array[M*N, T]
 
   proc `[]`*(M: Matrix; m, n: int): M.T =
@@ -4554,7 +4535,7 @@ The concept types can be parametric just like the regular generic types:
   # Adapt the Matrix type to the concept's requirements
   template Rows*(M: type Matrix): expr = M.M
   template Cols*(M: type Matrix): expr = M.N
-  template ValueType*(M: type Matrix): typedesc = M.T
+  template ValueType*(M: type Matrix): type = M.T
 
   -------------
   ### usage.nim
@@ -4582,7 +4563,7 @@ operator and also when types dependent on them are being matched:
 
 .. code-block:: nim
   type
-    MatrixReducer[M, N: static[int]; T] = concept x
+    MatrixReducer[M, N: static int; T] = concept x
       x.reduce(SquareMatrix[N, T]) is array[M, int]
 
 The Nim compiler includes a simple linear equation solver, allowing it to
@@ -4771,12 +4752,12 @@ object inheritance syntax involving the ``of`` keyword:
 
     # the varargs param will here be converted to an array of StringRefValues
     # the proc will have only two instantiations for the two character types
-    proc log(format: static[string], varargs[StringRef])
+    proc log(format: static string, varargs[StringRef])
 
     # this proc will allow char and wchar values to be mixed in
     # the same call at the cost of additional instantiations
     # the varargs param will be converted to a tuple
-    proc log(format: static[string], varargs[distinct StringRef])
+    proc log(format: static string, varargs[distinct StringRef])
 
 
 ..
@@ -4940,9 +4921,8 @@ templates:
 | ``notin`` and ``isnot`` have the obvious meanings.
 
 The "types" of templates can be the symbols ``untyped``,
-``typed`` or ``typedesc`` (stands for *type
-description*). These are "meta types", they can only be used in certain
-contexts. Real types can be used too; this implies that ``typed`` expressions
+``typed`` or ``type``. These are "meta types", they can only be used in certain
+contexts. Regular types can be used too; this implies that ``typed`` expressions
 are expected.
 
 
@@ -5109,7 +5089,7 @@ In templates identifiers can be constructed with the backticks notation:
 .. code-block:: nim
     :test: "nim c $1"
 
-  template typedef(name: untyped, typ: typedesc) =
+  template typedef(name: untyped, typ: type) =
     type
       `T name`* {.inject.} = typ
       `P name`* {.inject.} = ref `T name`
@@ -5171,7 +5151,7 @@ template cannot be accessed in the instantiation context:
 .. code-block:: nim
     :test: "nim c $1"
 
-  template newException*(exceptn: typedesc, message: string): untyped =
+  template newException*(exceptn: type, message: string): untyped =
     var
       e: ref exceptn  # e is implicitly gensym'ed here
     new(e)
@@ -5493,7 +5473,7 @@ As their name suggests, static parameters must be known at compile-time:
 
 .. code-block:: nim
 
-  proc precompiledRegex(pattern: static[string]): RegEx =
+  proc precompiledRegex(pattern: static string): RegEx =
     var res {.global.} = re(pattern)
     return res
 
@@ -5513,9 +5493,9 @@ Static params can also appear in the signatures of generic types:
 .. code-block:: nim
 
   type
-    Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T]
+    Matrix[M,N: static int; T: Number] = array[0..(M*N - 1), T]
       # Note how `Number` is just a type constraint here, while
-      # `static[int]` requires us to supply a compile-time int value
+      # `static int` requires us to supply a compile-time int value
 
     AffineTransform2D[T] = Matrix[3, 3, T]
     AffineTransform3D[T] = Matrix[4, 4, T]
@@ -5523,53 +5503,75 @@ Static params can also appear in the signatures of generic types:
   var m1: AffineTransform3D[float]  # OK
   var m2: AffineTransform2D[string] # Error, `string` is not a `Number`
 
+Please note that ``static T`` is just a syntactic convenience for the
+underlying generic type ``static[T]``. The type param can be omitted
+to obtain the type class of all values known at compile-time. A more
+specific type class can be created by instantiating ``static`` with
+another type class.
 
-typedesc
---------
+You can force the evaluation of a certain expression at compile-time by
+coercing it to a corresponding ``static`` type:
+
+.. code-block:: nim
+  import math
+
+  echo static(fac(5)), " ", static[bool](16.isPowerOfTwo)
+
+The complier will report any failure to evaluate the expression or a
+possible type mismatch error.
 
-`typedesc` is a special type allowing one to treat types as compile-time values
-(i.e. if types are compile-time values and all values have a type, then
-typedesc must be their type).
+type[T]
+-------
+
+In many contexts, Nim allows you to treat the names of types as regular
+values. These values exists only during the compilation phase, but since
+all values must have a type, ``type`` is considered their special type.
 
-When used as a regular proc param, typedesc acts as a type class. The proc
-will be instantiated for each unique type parameter and one can refer to the
-instantiation type using the param name:
+``type`` acts like a generic type. For instance, the type of the symbol
+``int`` is ``type[int]``. Just like with regular generic types, when the
+generic param is ommited, ``type`` denotes the type class of all types.
+As a syntactic convenience, you can also use ``type`` as a modifier.
+``type int`` is considered the same as ``type[int]``.
+
+Procs featuring ``type`` params are considered implicitly generic.
+They will be instantiated for each unique combination of supplied types
+and within the body of the proc, the name of each param will refer to
+the bound concrete type:
 
 .. code-block:: nim
 
-  proc new(T: typedesc): ref T =
+  proc new(T: type): ref T =
     echo "allocating ", T.name
     new(result)
 
   var n = Node.new
   var tree = new(BinaryTree[int])
 
-When multiple typedesc params are present, they will bind freely to different
-types. To force a bind-once behavior
-one can use an explicit ``typedesc[T]`` generic param:
+When multiple type params are present, they will bind freely to different
+types. To force a bind-once behavior one can use an explicit generic param:
 
 .. code-block:: nim
-  proc acceptOnlyTypePairs[T, U](A, B: typedesc[T]; C, D: typedesc[U])
+  proc acceptOnlyTypePairs[T, U](A, B: type[T]; C, D: type[U])
 
-Once bound, typedesc params can appear in the rest of the proc signature:
+Once bound, type params can appear in the rest of the proc signature:
 
 .. code-block:: nim
     :test: "nim c $1"
 
-  template declareVariableWithType(T: typedesc, value: T) =
+  template declareVariableWithType(T: type, value: T) =
     var x: T = value
 
   declareVariableWithType int, 42
 
 
 Overload resolution can be further influenced by constraining the set of
-types that will match the typedesc param:
+types that will match the type param:
 
 .. code-block:: nim
     :test: "nim c $1"
 
-  template maxval(T: typedesc[int]): int = high(int)
-  template maxval(T: typedesc[float]): float = Inf
+  template maxval(T: type int): int = high(int)
+  template maxval(T: type float): float = Inf
 
   var i = int.maxval
   var f = float.maxval
@@ -5578,7 +5580,35 @@ types that will match the typedesc param:
 
 The constraint can be a concrete type or a type class.
 
+type operator
+-------------
+
+You can obtain the type of a given expression by constructing a ``type``
+value from it (in many other languages this is known as the `typeof`:idx:
+operator):
 
+.. code-block:: nim
+  var x = 0
+  var y: type(x) # y has type int
+
+You may add a constraint to the resulting type to trigger a compile-time error
+if the expression doesn't have the expected type:
+
+.. code-block:: nim
+  var x = 0
+  var y: type[object](x) # Error: type mismatch: got <int> but expected 'object'
+
+If ``type`` is used to determine the result type of a proc/iterator/converter
+call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the
+interpretation where ``c`` is an iterator is preferred over the
+other interpretations:
+
+.. code-block:: nim
+  import strutils
+
+  # strutils contains both a ``split`` proc and iterator, but since an
+  # an iterator is the preferred interpretation, `y` has the type ``string``:
+  var y: type("a b c".split)
 
 
 Special Operators
@@ -6251,6 +6281,35 @@ Likewise the following does not make sense as the name is ``strutils`` already:
   import lib.pure.strutils as strutils
 
 
+Collective imports from a directory
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The syntax ``import dir / [moduleA, moduleB]`` can be used to import multiple modules
+from the same directory.
+
+Path names are syntactically either Nim identifiers or string literals. If the path
+name is not a valid Nim identifier it needs to be a string literal:
+
+.. code-block:: nim
+  import "gfx/3d/somemodule" # in quotes because '3d' is not a valid Nim identifier
+
+
+Pseudo import/include paths
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A directory can also be a so called "pseudo directory".
+
+There are two pseudo directories:
+
+1. ``std``: The ``std`` pseudo directory is the abstract location of Nim's standard
+  library. For example, the syntax ``import std / strutils`` is used to unambiguously
+  refer to the standard library's ``strutils`` module.
+2. ``pkg``: The ``pkg`` pseudo directory is used to unambiguously refer to a Nimble
+  package. However, for technical details that lie outside of the scope of this document
+  its semantics are: *Use the search path to look for module name but ignore the standard
+  library locations*. In other words, it is the opposite of ``std``.
+
+
 From import statement
 ~~~~~~~~~~~~~~~~~~~~~
 
@@ -6301,16 +6360,6 @@ modules don't need to import a module's dependencies:
 When the exported symbol is another module, all of its definitions will
 be forwarded. You can use an ``except`` list to exclude some of the symbols.
 
-Note on paths
------------
-In module related statements, if any part of the module name /
-path begins with a number, you may have to quote it in double quotes.
-In the following example, it would be seen as a literal number '3.0' of type
-'float64' if not quoted, if uncertain - quote it:
-
-.. code-block:: nim
-  import "gfx/3d/somemodule"
-
 
 Scope rules
 -----------
@@ -7439,7 +7488,7 @@ Custom pragmas are defined using templates annotated with pragma ``pragma``:
 .. code-block:: nim
   template dbTable(name: string, table_space: string = "") {.pragma.}
   template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
-  template dbForeignKey(t: typedesc) {.pragma.}
+  template dbForeignKey(t: type) {.pragma.}
   template dbIgnore {.pragma.}
 
 
diff --git a/doc/tut1.rst b/doc/tut1.rst
index f2251c463..aa6114cf7 100644
--- a/doc/tut1.rst
+++ b/doc/tut1.rst
@@ -208,7 +208,8 @@ Note that declaring multiple variables with a single assignment which calls a
 procedure can have unexpected results: the compiler will *unroll* the
 assignments and end up calling the procedure several times. If the result of
 the procedure depends on side effects, your variables may end up having
-different values! For safety use only constant values.
+different values! For safety use side-effect free procedures if making multiple
+assignments.
 
 
 Constants
@@ -642,7 +643,7 @@ initialisation.
 
 Parameters
 ----------
-Parameters are constant in the procedure body. By default, their value cannot be
+Parameters are immutable in the procedure body. By default, their value cannot be
 changed because this allows the compiler to implement parameter passing in the
 most efficient way. If a mutable variable is needed inside the procedure, it has
 to be declared with ``var`` in the procedure body. Shadowing the parameter name
diff --git a/doc/tut2.rst b/doc/tut2.rst
index 91cb52834..39e3bd89a 100644
--- a/doc/tut2.rst
+++ b/doc/tut2.rst
@@ -618,9 +618,9 @@ Turning the ``log`` proc into a template solves this problem:
   log("x has the value: " & $x)
 
 The parameters' types can be ordinary types or the meta types ``untyped``,
-``typed``, or ``typedesc``.
-``typedesc`` stands for *type description*, and ``untyped`` means symbol lookups and
-type resolution is not performed before the expression is passed to the template.
+``typed``, or ``type``. ``type`` suggests that only a type symbol may be given
+as an argument, and ``untyped`` means symbol lookups and type resolution is not
+performed before the expression is passed to the template.
 
 If the template has no explicit return type,
 ``void`` is used for consistency with procs and methods.
diff --git a/examples/allany.nim b/examples/allany.nim
index 6ff055aa6..8a5ab81f0 100644
--- a/examples/allany.nim
+++ b/examples/allany.nim
@@ -1,22 +1,20 @@
 # All and any
 
 template all(container, cond: untyped): bool =
-  block:
-    var result = true
-    for it in items(container):
-      if not cond(it):
-        result = false
-        break
-    result
+  var result = true
+  for it in items(container):
+    if not cond(it):
+      result = false
+      break
+  result
 
 template any(container, cond: untyped): bool =
-  block:
-    var result = false
-    for it in items(container):
-      if cond(it):
-        result = true
-        break
-    result
+  var result = false
+  for it in items(container):
+    if cond(it):
+      result = true
+      break
+  result
 
 if all("mystring", {'a'..'z'}.contains) and any("myohmy", 'y'.`==`):
   echo "works"
diff --git a/examples/readme.txt b/examples/readme.txt
index 176bc8239..686271660 100644
--- a/examples/readme.txt
+++ b/examples/readme.txt
@@ -1,5 +1,2 @@
 In this directory you will find several examples for how to use the Nim
 library.
-
-Copyright (c) 2004-2012 Andreas Rumpf.
-All rights reserved.
diff --git a/koch.nim b/koch.nim
index 4f85c6583..9411a95de 100644
--- a/koch.nim
+++ b/koch.nim
@@ -254,15 +254,13 @@ proc buildTool(toolname, args: string) =
   copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe)
 
 proc buildTools(latest: bool) =
-  let nimsugExe = "bin/nimsuggest".exe
-  nimexec "c --noNimblePath -p:compiler -d:release -o:" & nimsugExe &
+  nimexec "c --noNimblePath -p:compiler -d:release -o:" & ("bin/nimsuggest".exe) &
       " nimsuggest/nimsuggest.nim"
 
-  let nimgrepExe = "bin/nimgrep".exe
-  nimexec "c -d:release -o:" & nimgrepExe & " tools/nimgrep.nim"
+  nimexec "c -d:release -o:" & ("bin/nimgrep".exe) & " tools/nimgrep.nim"
   when defined(windows): buildVccTool()
 
-  #nimexec "c -o:" & ("bin/nimresolve".exe) & " tools/nimresolve.nim"
+  nimexec "c -o:" & ("bin/nimpretty".exe) & " nimpretty/nimpretty.nim"
 
   buildNimble(latest)
 
@@ -472,7 +470,7 @@ proc temp(args: string) =
   # commit.
   let (bootArgs, programArgs) = splitArgs(args)
   let nimexec = findNim()
-  exec(nimexec & " c -d:debug " & bootArgs & " compiler" / "nim", 125)
+  exec(nimexec & " c -d:debug --debugger:native " & bootArgs & " compiler" / "nim", 125)
   copyExe(output, finalDest)
   if programArgs.len > 0: exec(finalDest & " " & programArgs)
 
diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim
new file mode 100644
index 000000000..bd48b5bd4
--- /dev/null
+++ b/lib/core/macrocache.nim
@@ -0,0 +1,47 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module provides an API for macros that need to collect compile
+## time information across module boundaries in global variables.
+## Starting with version 0.19 of Nim this is not directly supported anymore
+## as it breaks incremental compilations.
+## Instead the API here needs to be used. See XXX (wikipedia page) for a
+## theoretical foundation behind this.
+
+type
+  CacheSeq* = distinct string
+  CacheTable* = distinct string
+  CacheCounter* = distinct string
+
+proc value*(c: CacheCounter): int {.magic: "NccValue".}
+proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".}
+
+proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".}
+proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".}
+proc len*(s: CacheSeq): int {.magic: "NcsLen".}
+proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".}
+
+iterator items*(s: CacheSeq): NimNode =
+  for i in 0 ..< len(s): yield s[i]
+
+proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".}
+  ## 'key' has to be unique!
+
+proc len*(t: CacheTable): int {.magic: "NctLen".}
+proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".}
+
+proc hasNext(t: CacheTable; iter: int): bool {.magic: "NctHasNext".}
+proc next(t: CacheTable; iter: int): (string, NimNode, int) {.magic: "NctNext".}
+
+iterator pairs*(t: CacheTable): (string, NimNode) =
+  var h = 0
+  while hasNext(t, h):
+    let (a, b, h2) = next(t, h)
+    yield (a, b)
+    h = h2
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index fa5cd67df..1f251b73e 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -126,9 +126,6 @@ type
     ## represents a Nim *symbol* in the compiler; a *symbol* is a looked-up
     ## *ident*.
 
-{.deprecated: [TNimrodNodeKind: NimNodeKind, TNimNodeKinds: NimNodeKinds,
-    TNimrodTypeKind: NimTypeKind, TNimrodSymKind: NimSymKind,
-    TNimrodIdent: NimIdent, PNimrodSymbol: NimSym].}
 
 const
   nnkLiterals* = {nnkCharLit..nnkNilLit}
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index adac16777..7ee071c79 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -363,6 +363,7 @@ proc addNodes(n: PRstNode): string =
   addNodesAux(n, result)
 
 proc rstnodeToRefnameAux(n: PRstNode, r: var string, b: var bool) =
+  if n == nil: return
   if n.kind == rnLeaf:
     for i in countup(0, len(n.text) - 1):
       case n.text[i]
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index 03a27017a..ef456f093 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -188,13 +188,16 @@ proc addTexChar(dest: var string, c: char) =
   of '`': add(dest, "\\symbol{96}")
   else: add(dest, c)
 
-var splitter*: string = "<wbr />"
-
 proc escChar*(target: OutputTarget, dest: var string, c: char) {.inline.} =
   case target
   of outHtml:  addXmlChar(dest, c)
   of outLatex: addTexChar(dest, c)
 
+proc addSplitter(target: OutputTarget; dest: var string) {.inline.} =
+  case target
+  of outHtml: add(dest, "<wbr />")
+  of outLatex: add(dest, "\\-")
+
 proc nextSplitPoint*(s: string, start: int): int =
   result = start
   while result < len(s) + 0:
@@ -215,9 +218,9 @@ proc esc*(target: OutputTarget, s: string, splitAfter = -1): string =
     var j = 0
     while j < len(s):
       var k = nextSplitPoint(s, j)
-      if (splitter != " ") or (partLen + k - j + 1 > splitAfter):
-        partLen = 0
-        add(result, splitter)
+      #if (splitter != " ") or (partLen + k - j + 1 > splitAfter):
+      partLen = 0
+      addSplitter(target, result)
       for i in countup(j, k): escChar(target, result, s[i])
       inc(partLen, k - j + 1)
       j = k + 1
diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim
index 1df7c3fc0..37339d3d1 100644
--- a/lib/pure/asyncfile.nim
+++ b/lib/pure/asyncfile.nim
@@ -91,7 +91,7 @@ proc newAsyncFile*(fd: AsyncFd): AsyncFile =
 
 proc openAsync*(filename: string, mode = fmRead): AsyncFile =
   ## Opens a file specified by the path in ``filename`` using
-  ## the specified ``mode`` asynchronously.
+  ## the specified FileMode ``mode`` asynchronously.
   when defined(windows) or defined(nimdoc):
     let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL
     let desiredAccess = getDesiredAccess(mode)
diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim
index 328308a9b..409240b37 100644
--- a/lib/pure/collections/deques.nim
+++ b/lib/pure/collections/deques.nim
@@ -38,7 +38,7 @@
 ## Note: For inter thread communication use
 ## a `Channel <channels.html>`_ instead.
 
-import math
+import math, typetraits
 
 type
   Deque*[T] = object
@@ -160,16 +160,18 @@ proc peekLast*[T](deq: Deque[T]): T {.inline.} =
   emptyCheck(deq)
   result = deq.data[(deq.tail - 1) and deq.mask]
 
-template default[T](t: typedesc[T]): T =
-  var v: T
-  v
+template destroy(x: untyped) =
+  when defined(nimNewRuntime) and not supportsCopyMem(type(x)):
+    `=destroy`(x)
+  else:
+    reset(x)
 
 proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
   ## Remove and returns the first element of the `deq`.
   emptyCheck(deq)
   dec deq.count
   result = deq.data[deq.head]
-  deq.data[deq.head] = default(type(result))
+  destroy(deq.data[deq.head])
   deq.head = (deq.head + 1) and deq.mask
 
 proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
@@ -178,7 +180,34 @@ proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
   dec deq.count
   deq.tail = (deq.tail - 1) and deq.mask
   result = deq.data[deq.tail]
-  deq.data[deq.tail] = default(type(result))
+  destroy(deq.data[deq.tail])
+
+proc clear*[T](deq: var Deque[T]) {.inline.} =
+  ## Resets the deque so that it is empty.
+  for el in mitems(deq): destroy(el)
+  deq.count = 0
+  deq.tail = deq.head
+
+proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) =
+  ## Remove `fromFirst` elements from the front of the deque and
+  ## `fromLast` elements from the back. If the supplied number of
+  ## elements exceeds the total number of elements in the deque,
+  ## the deque will remain empty.
+  ##
+  ## Any user defined destructors
+  if fromFirst + fromLast > deq.count:
+    clear(deq)
+    return
+
+  for i in 0 ..< fromFirst:
+    destroy(deq.data[deq.head])
+    deq.head = (deq.head + 1) and deq.mask
+
+  for i in 0 ..< fromLast:
+    destroy(deq.data[deq.tail])
+    deq.tail = (deq.tail - 1) and deq.mask
+
+  dec deq.count, fromFirst + fromLast
 
 proc `$`*[T](deq: Deque[T]): string =
   ## Turn a deque into its string representation.
@@ -215,6 +244,22 @@ when isMainModule:
   assert deq.find(6) >= 0
   assert deq.find(789) < 0
 
+  block:
+    var d = initDeque[int](1)
+    d.addLast 7
+    d.addLast 8
+    d.addLast 10
+    d.addFirst 5
+    d.addFirst 2
+    d.addFirst 1
+    d.addLast 20
+    d.shrink(fromLast = 2)
+    doAssert($d == "[1, 2, 5, 7, 8]")
+    d.shrink(2, 1)
+    doAssert($d == "[5, 7]")
+    d.shrink(2, 2)
+    doAssert d.len == 0
+
   for i in -2 .. 10:
     if i in deq:
       assert deq.contains(i) and deq.find(i) >= 0
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 8530e4c42..8b4fb0f8c 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -1184,7 +1184,7 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string,
   ## Connects to the hostname specified by the URL and performs a request
   ## using the custom method string specified by ``httpMethod``.
   ##
-  ## Connection will kept alive. Further requests on the same ``client`` to
+  ## Connection will be kept alive. Further requests on the same ``client`` to
   ## the same hostname will not require a new connection to be made. The
   ## connection can be closed by using the ``close`` procedure.
   ##
diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim
index b88c9dd85..b90d2899c 100644
--- a/lib/pure/marshal.nim
+++ b/lib/pure/marshal.nim
@@ -270,6 +270,8 @@ proc store*[T](s: Stream, data: T) =
 
 proc `$$`*[T](x: T): string =
   ## returns a string representation of `x`.
+  ##
+  ## Note: to serialize `x` to JSON use $(%x) from the ``json`` module
   var stored = initIntSet()
   var d: T
   shallowCopy(d, x)
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index 8ea8ee203..b921b1841 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -147,6 +147,18 @@ when not defined(JS): # C
   proc ln*(x: float32): float32 {.importc: "logf", header: "<math.h>".}
   proc ln*(x: float64): float64 {.importc: "log", header: "<math.h>".}
     ## Computes the natural log of `x`
+else: # JS
+  proc sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.}
+  proc sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.}
+
+  proc ln*(x: float32): float32 {.importc: "Math.log", nodecl.}
+  proc ln*(x: float64): float64 {.importc: "Math.log", nodecl.}
+
+proc log*[T: SomeFloat](x, base: T): T =
+  ## Computes the logarithm ``base`` of ``x``
+  ln(x) / ln(base)
+
+when not defined(JS): # C
   proc log10*(x: float32): float32 {.importc: "log10f", header: "<math.h>".}
   proc log10*(x: float64): float64 {.importc: "log10", header: "<math.h>".}
     ## Computes the common logarithm (base 10) of `x`
@@ -204,11 +216,6 @@ when not defined(JS): # C
     ## Computes the inverse hyperbolic tangent of `x`
 
 else: # JS
-  proc sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.}
-  proc sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.}
-
-  proc ln*(x: float32): float32 {.importc: "Math.log", nodecl.}
-  proc ln*(x: float64): float64 {.importc: "Math.log", nodecl.}
   proc log10*(x: float32): float32 {.importc: "Math.log10", nodecl.}
   proc log10*(x: float64): float64 {.importc: "Math.log10", nodecl.}
   proc log2*(x: float32): float32 {.importc: "Math.log2", nodecl.}
@@ -682,3 +689,6 @@ when isMainModule:
 
     doAssert floorMod(8.0, -3.0) ==~ -1.0
     doAssert floorMod(-8.5, 3.0) ==~ 0.5
+
+  block: # log
+    doAssert log(4.0, 3.0) == ln(4.0) / ln(3.0)
diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim
index bf6795b0c..c7b8ebbd8 100644
--- a/lib/pure/memfiles.nim
+++ b/lib/pure/memfiles.nim
@@ -22,7 +22,11 @@ elif defined(posix):
 else:
   {.error: "the memfiles module is not supported on your operating system!".}
 
-import os
+import os, streams
+
+proc newEIO(msg: string): ref IOError =
+  new(result)
+  result.msg = msg
 
 type
   MemFile* = object  ## represents a memory mapped file
@@ -44,11 +48,14 @@ proc mapMem*(m: var MemFile, mode: FileMode = fmRead,
   ##
   ## ``mappedSize`` of ``-1`` maps to the whole file, and
   ## ``offset`` must be multiples of the PAGE SIZE of your OS
+  if mode == fmAppend:
+    raise newEIO("The append mode is not supported.")
+
   var readonly = mode == fmRead
   when defined(windows):
     result = mapViewOfFileEx(
       m.mapHandle,
-      if readonly: FILE_MAP_READ else: FILE_MAP_WRITE,
+      if readonly: FILE_MAP_READ else: FILE_MAP_READ or FILE_MAP_WRITE,
       int32(offset shr 32),
       int32(offset and 0xffffffff),
       if mappedSize == -1: 0 else: mappedSize,
@@ -113,6 +120,9 @@ proc open*(filename: string, mode: FileMode = fmRead,
   ##   mm_half = memfiles.open("/tmp/test.mmap", mode = fmReadWrite, mappedSize = 512)
 
   # The file can be resized only when write mode is used:
+  if mode == fmAppend:
+    raise newEIO("The append mode is not supported.")
+
   assert newFileSize == -1 or mode != fmRead
   var readonly = mode == fmRead
 
@@ -121,6 +131,10 @@ proc open*(filename: string, mode: FileMode = fmRead,
     result.size = 0
 
   when defined(windows):
+    let desiredAccess = GENERIC_READ
+    let shareMode = FILE_SHARE_READ
+    let flags = FILE_FLAG_RANDOM_ACCESS
+
     template fail(errCode: OSErrorCode, msg: untyped) =
       rollback()
       if result.fHandle != 0: discard closeHandle(result.fHandle)
@@ -133,11 +147,11 @@ proc open*(filename: string, mode: FileMode = fmRead,
       winApiProc(
         filename,
         # GENERIC_ALL != (GENERIC_READ or GENERIC_WRITE)
-        if readonly: GENERIC_READ else: GENERIC_READ or GENERIC_WRITE,
-        FILE_SHARE_READ,
+        if readonly: desiredAccess else: desiredAccess or GENERIC_WRITE,
+        if readonly: shareMode else: shareMode or FILE_SHARE_WRITE,
         nil,
         if newFileSize != -1: CREATE_ALWAYS else: OPEN_EXISTING,
-        if readonly: FILE_ATTRIBUTE_READONLY else: FILE_ATTRIBUTE_TEMPORARY,
+        if readonly: FILE_ATTRIBUTE_READONLY or flags else: FILE_ATTRIBUTE_NORMAL or flags,
         0)
 
     when useWinUnicode:
@@ -172,7 +186,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
 
     result.mem = mapViewOfFileEx(
       result.mapHandle,
-      if readonly: FILE_MAP_READ else: FILE_MAP_WRITE,
+      if readonly: FILE_MAP_READ else: FILE_MAP_READ or FILE_MAP_WRITE,
       int32(offset shr 32),
       int32(offset and 0xffffffff),
       if mappedSize == -1: 0 else: mappedSize,
@@ -245,6 +259,28 @@ proc open*(filename: string, mode: FileMode = fmRead,
       if close(result.handle) == 0:
         result.handle = -1
 
+proc flush*(f: var MemFile; attempts: Natural = 3) =
+  ## Flushes `f`'s buffer for the number of attempts equal to `attempts`.
+  ## If were errors an exception `OSError` will be raised.
+  var res = false
+  var lastErr: OSErrorCode
+  when defined(windows):
+    for i in 1..attempts:
+      res = flushViewOfFile(f.mem, 0) != 0
+      if res:
+        break
+      lastErr = osLastError()
+      if lastErr != ERROR_LOCK_VIOLATION.OSErrorCode:
+        raiseOSError(lastErr)
+  else:
+    for i in 1..attempts:
+      res = msync(f.mem, f.size, MS_SYNC or MS_INVALIDATE) == 0
+      if res:
+        break
+      lastErr = osLastError()
+      if lastErr != EBUSY.OSErrorCode:
+        raiseOSError(lastErr, "error flushing mapping")
+
 proc close*(f: var MemFile) =
   ## closes the memory mapped file `f`. All changes are written back to the
   ## file system, if `f` was opened with write access.
@@ -362,9 +398,8 @@ iterator lines*(mfile: MemFile, buf: var TaintedString, delim='\l', eat='\r'): T
   ##     echo line
 
   for ms in memSlices(mfile, delim, eat):
-    buf.setLen(ms.size)
-    copyMem(addr(buf[0]), ms.data, ms.size)
-    buf[ms.size] = '\0'
+    setLen(buf.string, ms.size)
+    copyMem(buf.cstring, ms.data, ms.size)
     yield buf
 
 iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.} =
@@ -382,3 +417,68 @@ iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.}
   var buf = TaintedString(newStringOfCap(80))
   for line in lines(mfile, buf, delim, eat):
     yield buf
+
+type
+  MemMapFileStream* = ref MemMapFileStreamObj ## a stream that encapsulates a `MemFile`
+  MemMapFileStreamObj* = object of Stream
+    mf: MemFile
+    mode: FileMode
+    pos: ByteAddress
+
+proc mmsClose(s: Stream) =
+  MemMapFileStream(s).pos = -1
+  close(MemMapFileStream(s).mf)
+
+proc mmsFlush(s: Stream) = flush(MemMapFileStream(s).mf)
+
+proc mmsAtEnd(s: Stream): bool = (MemMapFileStream(s).pos >= MemMapFileStream(s).mf.size) or
+                                 (MemMapFileStream(s).pos < 0)
+
+proc mmsSetPosition(s: Stream, pos: int) =
+  if pos > MemMapFileStream(s).mf.size or pos < 0:
+    raise newEIO("cannot set pos in stream")
+  MemMapFileStream(s).pos = pos
+
+proc mmsGetPosition(s: Stream): int = MemMapFileStream(s).pos
+
+proc mmsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
+  let startAddress = cast[ByteAddress](MemMapFileStream(s).mf.mem)
+  let p = cast[ByteAddress](MemMapFileStream(s).pos)
+  let l = min(bufLen, MemMapFileStream(s).mf.size - p)
+  moveMem(buffer, cast[pointer](startAddress + p), l)
+  result = l
+
+proc mmsReadData(s: Stream, buffer: pointer, bufLen: int): int =
+  result = mmsPeekData(s, buffer, bufLen)
+  inc(MemMapFileStream(s).pos, result)
+
+proc mmsWriteData(s: Stream, buffer: pointer, bufLen: int) =
+  if MemMapFileStream(s).mode == fmRead:
+    raise newEIO("cannot write to read-only stream")
+  let size = MemMapFileStream(s).mf.size
+  if MemMapFileStream(s).pos + bufLen > size:
+    raise newEIO("cannot write to stream")
+  let p = cast[ByteAddress](MemMapFileStream(s).mf.mem) +
+          cast[ByteAddress](MemMapFileStream(s).pos)
+  moveMem(cast[pointer](p), buffer, bufLen)
+  inc(MemMapFileStream(s).pos, bufLen)
+
+proc newMemMapFileStream*(filename: string, mode: FileMode = fmRead, fileSize: int = -1):
+  MemMapFileStream =
+  ## creates a new stream from the file named `filename` with the mode `mode`.
+  ## Raises ## `EOS` if the file cannot be opened. See the `system
+  ## <system.html>`_ module for a list of available FileMode enums.
+  ## ``fileSize`` can only be set if the file does not exist and is opened
+  ## with write access (e.g., with fmReadWrite).
+  var mf: MemFile = open(filename, mode, newFileSize = fileSize)
+  new(result)
+  result.mode = mode
+  result.mf = mf
+  result.closeImpl = mmsClose
+  result.atEndImpl = mmsAtEnd
+  result.setPositionImpl = mmsSetPosition
+  result.getPositionImpl = mmsGetPosition
+  result.readDataImpl = mmsReadData
+  result.peekDataImpl = mmsPeekData
+  result.writeDataImpl = mmsWriteData
+  result.flushImpl = mmsFlush
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 5008b904c..3bc87728b 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -917,7 +917,7 @@ proc rawCreateDir(dir: string): bool =
     elif errno == EEXIST:
       result = false
     else:
-      echo res
+      #echo res
       raiseOSError(osLastError())
   else:
     when useWinUnicode:
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 664446d54..e8bca4bdd 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -832,7 +832,7 @@ elif not defined(useNimRtl):
 
     # Parent process. Copy process information.
     if poEchoCmd in options:
-      echo(command, " ", join(args, " "))
+      when declared(echo): echo(command, " ", join(args, " "))
     result.id = pid
     result.exitFlag = false
 
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index d54f1454b..e633d8cf7 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -47,12 +47,14 @@ proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {.
   ##   discard parseHex("0x38", value)
   ##   assert value == -200
   ##
-  ## If 'maxLen==0' the length of the hexadecimal number has no
-  ## upper bound. Not more than ```maxLen`` characters are parsed.
+  ## If ``maxLen == 0`` the length of the hexadecimal number has no upper bound.
+  ## Else no more than ``start + maxLen`` characters are parsed, up to the
+  ## length of the string.
   var i = start
   var foundDigit = false
-  let last = if maxLen == 0: s.len else: i+maxLen
-  if i+1 < last and s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
+  # get last index based on minimum `start + maxLen` or `s.len`
+  let last = min(s.len, if maxLen == 0: s.len else: i+maxLen)
+  if i+1 < last and s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
   elif i < last and s[i] == '#': inc(i)
   while i < last:
     case s[i]
@@ -70,14 +72,20 @@ proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {.
     inc(i)
   if foundDigit: result = i-start
 
-proc parseOct*(s: string, number: var int, start = 0): int  {.
+proc parseOct*(s: string, number: var int, start = 0, maxLen = 0): int  {.
   rtl, extern: "npuParseOct", noSideEffect.} =
-  ## parses an octal number and stores its value in ``number``. Returns
+  ## Parses an octal number and stores its value in ``number``. Returns
   ## the number of the parsed characters or 0 in case of an error.
+  ##
+  ## If ``maxLen == 0`` the length of the octal number has no upper bound.
+  ## Else no more than ``start + maxLen`` characters are parsed, up to the
+  ## length of the string.
   var i = start
   var foundDigit = false
-  if i+1 < s.len and s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
-  while i < s.len:
+  # get last index based on minimum `start + maxLen` or `s.len`
+  let last = min(s.len, if maxLen == 0: s.len else: i+maxLen)
+  if i+1 < last and s[i] == '0' and (s[i+1] in {'o', 'O'}): inc(i, 2)
+  while i < last:
     case s[i]
     of '_': discard
     of '0'..'7':
@@ -87,14 +95,20 @@ proc parseOct*(s: string, number: var int, start = 0): int  {.
     inc(i)
   if foundDigit: result = i-start
 
-proc parseBin*(s: string, number: var int, start = 0): int  {.
+proc parseBin*(s: string, number: var int, start = 0, maxLen = 0): int  {.
   rtl, extern: "npuParseBin", noSideEffect.} =
-  ## parses an binary number and stores its value in ``number``. Returns
+  ## Parses an binary number and stores its value in ``number``. Returns
   ## the number of the parsed characters or 0 in case of an error.
+  ##
+  ## If ``maxLen == 0`` the length of the binary number has no upper bound.
+  ## Else no more than ``start + maxLen`` characters are parsed, up to the
+  ## length of the string.
   var i = start
   var foundDigit = false
-  if i+1 < s.len and s[i] == '0' and (s[i+1] == 'b' or s[i+1] == 'B'): inc(i, 2)
-  while i < s.len:
+  # get last index based on minimum `start + maxLen` or `s.len`
+  let last = min(s.len, if maxLen == 0: s.len else: i+maxLen)
+  if i+1 < last and s[i] == '0' and (s[i+1] in {'b', 'B'}): inc(i, 2)
+  while i < last:
     case s[i]
     of '_': discard
     of '0'..'1':
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index 39c5790ed..d16527a56 100644
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -32,7 +32,7 @@ const
                        ## can be captured. More subpatterns cannot be captured!
 
 type
-  PegKind = enum
+  PegKind* = enum
     pkEmpty,
     pkAny,              ## any character (.)
     pkAnyRune,          ## any Unicode character (_)
@@ -67,7 +67,7 @@ type
     pkRule,             ## a <- b
     pkList,             ## a, b
     pkStartAnchor       ## ^      --> Internal DSL: startAnchor()
-  NonTerminalFlag = enum
+  NonTerminalFlag* = enum
     ntDeclared, ntUsed
   NonTerminalObj = object         ## represents a non terminal symbol
     name: string                  ## the name of the symbol
@@ -86,6 +86,25 @@ type
     else: sons: seq[Peg]
   NonTerminal* = ref NonTerminalObj
 
+proc name*(nt: NonTerminal): string = nt.name
+proc line*(nt: NonTerminal): int = nt.line
+proc col*(nt: NonTerminal): int = nt.col
+proc flags*(nt: NonTerminal): set[NonTerminalFlag] = nt.flags
+proc rule*(nt: NonTerminal): Peg = nt.rule
+
+proc kind*(p: Peg): PegKind = p.kind
+proc term*(p: Peg): string = p.term
+proc ch*(p: Peg): char = p.ch
+proc charChoice*(p: Peg): ref set[char] = p.charChoice
+proc nt*(p: Peg): NonTerminal = p.nt
+proc index*(p: Peg): range[0..MaxSubpatterns] = p.index
+iterator items*(p: Peg): Peg {.inline.} =
+  for s in p.sons:
+    yield s
+iterator pairs*(p: Peg): (int, Peg) {.inline.} =
+  for i in 0 ..< p.sons.len:
+    yield (i, p.sons[i])
+
 proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} =
   ## constructs a PEG from a terminal string
   if t.len != 1:
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index a0bba05a4..1ab73faea 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -135,6 +135,11 @@ proc write*(s: Stream, x: string) =
   else:
     if x.len > 0: writeData(s, cstring(x), x.len)
 
+proc write*(s: Stream, args: varargs[string, `$`]) =
+  ## writes one or more strings to the the stream. No length fields or
+  ## terminating zeros are written.
+  for str in args: write(s, str)
+
 proc writeLine*(s: Stream, args: varargs[string, `$`]) =
   ## writes one or more strings to the the stream `s` followed
   ## by a new line. No length field or terminating zero is written.
@@ -266,8 +271,8 @@ proc peekStr*(s: Stream, length: int): TaintedString =
 proc readLine*(s: Stream, line: var TaintedString): bool =
   ## reads a line of text from the stream `s` into `line`. `line` must not be
   ## ``nil``! May throw an IO exception.
-  ## A line of text may be delimited by ``CR``, ``LF`` or
-  ## ``CRLF``. The newline character(s) are not part of the returned string.
+  ## A line of text may be delimited by ```LF`` or ``CRLF``.
+  ## The newline character(s) are not part of the returned string.
   ## Returns ``false`` if the end of the file has been reached, ``true``
   ## otherwise. If ``false`` is returned `line` contains no new data.
   line.string.setLen(0)
@@ -317,6 +322,13 @@ proc peekLine*(s: Stream): TaintedString =
   defer: setPosition(s, pos)
   result = readLine(s)
 
+iterator lines*(s: Stream): TaintedString =
+  ## Iterates over every line in the stream.
+  ## The iteration is based on ``readLine``.
+  var line: TaintedString
+  while s.readLine(line):
+    yield line
+
 when not defined(js):
 
   type
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index bea0a0243..ab34a0b2d 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -17,6 +17,10 @@ import parseutils
 from math import pow, round, floor, log10
 from algorithm import reverse
 
+when defined(nimVmExportFixed):
+  from unicode import toLower, toUpper
+  export toLower, toUpper
+
 {.deadCodeElim: on.}  # dce option deprecated
 
 {.push debugger:off .} # the user does not want to trace a part
@@ -844,7 +848,7 @@ proc parseInt*(s: string): int {.noSideEffect, procvar,
   ## Parses a decimal integer value contained in `s`.
   ##
   ## If `s` is not a valid integer, `ValueError` is raised.
-  var L = parseutils.parseInt(s, result, 0)
+  let L = parseutils.parseInt(s, result, 0)
   if L != s.len or L == 0:
     raise newException(ValueError, "invalid integer: " & s)
 
@@ -853,7 +857,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar,
   ## Parses a decimal integer value contained in `s`.
   ##
   ## If `s` is not a valid integer, `ValueError` is raised.
-  var L = parseutils.parseBiggestInt(s, result, 0)
+  let L = parseutils.parseBiggestInt(s, result, 0)
   if L != s.len or L == 0:
     raise newException(ValueError, "invalid integer: " & s)
 
@@ -862,7 +866,7 @@ proc parseUInt*(s: string): uint {.noSideEffect, procvar,
   ## Parses a decimal unsigned integer value contained in `s`.
   ##
   ## If `s` is not a valid integer, `ValueError` is raised.
-  var L = parseutils.parseUInt(s, result, 0)
+  let L = parseutils.parseUInt(s, result, 0)
   if L != s.len or L == 0:
     raise newException(ValueError, "invalid unsigned integer: " & s)
 
@@ -871,7 +875,7 @@ proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect, procvar,
   ## Parses a decimal unsigned integer value contained in `s`.
   ##
   ## If `s` is not a valid integer, `ValueError` is raised.
-  var L = parseutils.parseBiggestUInt(s, result, 0)
+  let L = parseutils.parseBiggestUInt(s, result, 0)
   if L != s.len or L == 0:
     raise newException(ValueError, "invalid unsigned integer: " & s)
 
@@ -880,33 +884,42 @@ proc parseFloat*(s: string): float {.noSideEffect, procvar,
   ## Parses a decimal floating point value contained in `s`. If `s` is not
   ## a valid floating point number, `ValueError` is raised. ``NAN``,
   ## ``INF``, ``-INF`` are also supported (case insensitive comparison).
-  var L = parseutils.parseFloat(s, result, 0)
+  let L = parseutils.parseFloat(s, result, 0)
   if L != s.len or L == 0:
     raise newException(ValueError, "invalid float: " & s)
 
+proc parseBinInt*(s: string): int {.noSideEffect, procvar,
+  rtl, extern: "nsuParseBinInt".} =
+  ## Parses a binary integer value contained in `s`.
+  ##
+  ## If `s` is not a valid binary integer, `ValueError` is raised. `s` can have
+  ## one of the following optional prefixes: ``0b``, ``0B``. Underscores within
+  ## `s` are ignored.
+  let L = parseutils.parseBin(s, result, 0)
+  if L != s.len or L == 0:
+    raise newException(ValueError, "invalid binary integer: " & s)
+
+proc parseOctInt*(s: string): int {.noSideEffect,
+  rtl, extern: "nsuParseOctInt".} =
+  ## Parses an octal integer value contained in `s`.
+  ##
+  ## If `s` is not a valid oct integer, `ValueError` is raised. `s` can have one
+  ## of the following optional prefixes: ``0o``, ``0O``.  Underscores within
+  ## `s` are ignored.
+  let L = parseutils.parseOct(s, result, 0)
+  if L != s.len or L == 0:
+    raise newException(ValueError, "invalid oct integer: " & s)  
+
 proc parseHexInt*(s: string): int {.noSideEffect, procvar,
   rtl, extern: "nsuParseHexInt".} =
   ## Parses a hexadecimal integer value contained in `s`.
   ##
-  ## If `s` is not a valid integer, `ValueError` is raised. `s` can have one
+  ## If `s` is not a valid hex integer, `ValueError` is raised. `s` can have one
   ## of the following optional prefixes: ``0x``, ``0X``, ``#``.  Underscores
   ## within `s` are ignored.
-  var i = 0
-  if i+1 < s.len and s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
-  elif i < s.len and s[i] == '#': inc(i)
-  while i < s.len:
-    case s[i]
-    of '_': inc(i)
-    of '0'..'9':
-      result = result shl 4 or (ord(s[i]) - ord('0'))
-      inc(i)
-    of 'a'..'f':
-      result = result shl 4 or (ord(s[i]) - ord('a') + 10)
-      inc(i)
-    of 'A'..'F':
-      result = result shl 4 or (ord(s[i]) - ord('A') + 10)
-      inc(i)
-    else: raise newException(ValueError, "invalid integer: " & s)
+  let L = parseutils.parseHex(s, result, 0)
+  if L != s.len or L == 0:
+    raise newException(ValueError, "invalid hex integer: " & s)
 
 proc generateHexCharToValueMap(): string =
   ## Generate a string to map a hex digit to uint value
@@ -1616,23 +1629,6 @@ proc delete*(s: var string, first, last: int) {.noSideEffect,
     inc(j)
   setLen(s, newLen)
 
-proc parseOctInt*(s: string): int {.noSideEffect,
-  rtl, extern: "nsuParseOctInt".} =
-  ## Parses an octal integer value contained in `s`.
-  ##
-  ## If `s` is not a valid integer, `ValueError` is raised. `s` can have one
-  ## of the following optional prefixes: ``0o``, ``0O``.  Underscores within
-  ## `s` are ignored.
-  var i = 0
-  if i+1 < s.len and s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
-  while i < s.len:
-    case s[i]
-    of '_': inc(i)
-    of '0'..'7':
-      result = result shl 3 or (ord(s[i]) - ord('0'))
-      inc(i)
-    else: raise newException(ValueError, "invalid integer: " & s)
-
 proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect,
   rtl, extern: "nsuToOct".} =
   ## Converts `x` into its octal representation.
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index fcca4d5d7..ac41a0aad 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -467,16 +467,18 @@ proc resetAttributes*(f: File) =
     f.write(ansiResetCode)
 
 type
-  Style* = enum         ## different styles for text output
+  Style* = enum          ## different styles for text output
     styleBright = 1,     ## bright text
     styleDim,            ## dim text
-    styleUnknown,        ## unknown
+    styleItalic,         ## italic (or reverse on terminals not supporting)
     styleUnderscore = 4, ## underscored text
     styleBlink,          ## blinking/bold text
-    styleReverse = 7,    ## unknown
+    styleReverse = 7,    ## reverse
     styleHidden          ## hidden text
+    styleStrikethrough,  ## strikethrough
 
 {.deprecated: [TStyle: Style].}
+{.deprecated: [styleUnknown: styleItalic].}
 
 when not defined(windows):
   var
@@ -540,7 +542,9 @@ type
     fgBlue,                ## blue
     fgMagenta,             ## magenta
     fgCyan,                ## cyan
-    fgWhite                ## white
+    fgWhite,               ## white
+    fg8Bit,                ## 256-color (not supported, see ``enableTrueColors`` instead.)
+    fgDefault              ## default terminal foreground color
 
   BackgroundColor* = enum  ## terminal's background colors
     bgBlack = 40,          ## black
@@ -550,28 +554,40 @@ type
     bgBlue,                ## blue
     bgMagenta,             ## magenta
     bgCyan,                ## cyan
-    bgWhite                ## white
+    bgWhite,               ## white
+    bg8Bit,                ## 256-color (not supported, see ``enableTrueColors`` instead.)
+    bgDefault              ## default terminal background color
 
 {.deprecated: [TForegroundColor: ForegroundColor,
                TBackgroundColor: BackgroundColor].}
 
+when defined(windows):
+  var defaultForegroundColor, defaultBackgroundColor: int16 = 0xFFFF'i16 # Default to an invalid value 0xFFFF
+
 proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) =
   ## Sets the terminal's foreground color.
   when defined(windows):
     let h = conHandle(f)
     var old = getAttributes(h) and not FOREGROUND_RGB
+    if defaultForegroundColor == 0xFFFF'i16:
+      defaultForegroundColor = old
     old = if bright: old or FOREGROUND_INTENSITY
           else:      old and not(FOREGROUND_INTENSITY)
     const lookup: array[ForegroundColor, int] = [
-      0,
+      0, # ForegroundColor enum with ordinal 30
       (FOREGROUND_RED),
       (FOREGROUND_GREEN),
       (FOREGROUND_RED or FOREGROUND_GREEN),
       (FOREGROUND_BLUE),
       (FOREGROUND_RED or FOREGROUND_BLUE),
       (FOREGROUND_BLUE or FOREGROUND_GREEN),
-      (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)]
-    discard setConsoleTextAttribute(h, toU16(old or lookup[fg]))
+      (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED),
+      0, # fg8Bit not supported, see ``enableTrueColors`` instead.
+      0] # unused
+    if fg == fgDefault:
+      discard setConsoleTextAttribute(h, toU16(old or defaultForegroundColor))
+    else:
+      discard setConsoleTextAttribute(h, toU16(old or lookup[fg]))
   else:
     gFG = ord(fg)
     if bright: inc(gFG, 60)
@@ -582,18 +598,25 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) =
   when defined(windows):
     let h = conHandle(f)
     var old = getAttributes(h) and not BACKGROUND_RGB
+    if defaultBackgroundColor == 0xFFFF'i16:
+      defaultBackgroundColor = old
     old = if bright: old or BACKGROUND_INTENSITY
           else:      old and not(BACKGROUND_INTENSITY)
     const lookup: array[BackgroundColor, int] = [
-      0,
+      0, # BackgroundColor enum with ordinal 40
       (BACKGROUND_RED),
       (BACKGROUND_GREEN),
       (BACKGROUND_RED or BACKGROUND_GREEN),
       (BACKGROUND_BLUE),
       (BACKGROUND_RED or BACKGROUND_BLUE),
       (BACKGROUND_BLUE or BACKGROUND_GREEN),
-      (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)]
-    discard setConsoleTextAttribute(h, toU16(old or lookup[bg]))
+      (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED),
+      0, # bg8Bit not supported, see ``enableTrueColors`` instead.
+      0] # unused
+    if bg == bgDefault:
+      discard setConsoleTextAttribute(h, toU16(old or defaultBackgroundColor))
+    else:
+      discard setConsoleTextAttribute(h, toU16(old or lookup[bg]))
   else:
     gBG = ord(bg)
     if bright: inc(gBG, 60)
@@ -690,8 +713,8 @@ template styledEchoProcessArg(f: File, cmd: TerminalCmd) =
   when cmd == bgColor:
     fgSetColor = false
 
-macro styledWriteLine*(f: File, m: varargs[typed]): untyped =
-  ## Similar to ``writeLine``, but treating terminal style arguments specially.
+macro styledWrite*(f: File, m: varargs[typed]): untyped =
+  ## Similar to ``write``, but treating terminal style arguments specially.
   ## When some argument is ``Style``, ``set[Style]``, ``ForegroundColor``,
   ## ``BackgroundColor`` or ``TerminalCmd`` then it is not sent directly to
   ## ``f``, but instead corresponding terminal style proc is called.
@@ -700,8 +723,8 @@ macro styledWriteLine*(f: File, m: varargs[typed]): untyped =
   ##
   ## .. code-block:: nim
   ##
-  ##   proc error(msg: string) =
-  ##     styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg)
+  ##   stdout.styledWrite(fgRed, "red text ")
+  ##   stdout.styledWrite(fgGreen, "green text")
   ##
   let m = callsite()
   var reset = false
@@ -712,8 +735,8 @@ macro styledWriteLine*(f: File, m: varargs[typed]): untyped =
     case item.kind
     of nnkStrLit..nnkTripleStrLit:
       if i == m.len - 1:
-        # optimize if string literal is last, just call writeLine
-        result.add(newCall(bindSym"writeLine", f, item))
+        # optimize if string literal is last, just call write
+        result.add(newCall(bindSym"write", f, item))
         if reset: result.add(newCall(bindSym"resetAttributes", f))
         return
       else:
@@ -722,16 +745,24 @@ macro styledWriteLine*(f: File, m: varargs[typed]): untyped =
     else:
       result.add(newCall(bindSym"styledEchoProcessArg", f, item))
       reset = true
-
-  result.add(newCall(bindSym"write", f, newStrLitNode("\n")))
   if reset: result.add(newCall(bindSym"resetAttributes", f))
 
-macro styledEcho*(args: varargs[untyped]): untyped =
+template styledWriteLine*(f: File, args: varargs[untyped]) =
+  ## Calls ``styledWrite`` and appends a newline at the end.
+  ##
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##
+  ##   proc error(msg: string) =
+  ##     styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg)
+  ##
+  styledWrite(f, args)
+  write(f, "\n")
+
+template styledEcho*(args: varargs[untyped]) =
   ## Echoes styles arguments to stdout using ``styledWriteLine``.
-  result = newCall(bindSym"styledWriteLine")
-  result.add(bindSym"stdout")
-  for arg in children(args):
-    result.add(arg)
+  stdout.styledWriteLine(args)
 
 proc getch*(): char =
   ## Read a single character from the terminal, blocking until it is entered.
@@ -779,7 +810,7 @@ when defined(windows):
           inc i, x
         password.string.setLen(max(password.len - x, 0))
       of chr(0x0):
-        # modifier key - ignore - for details see 
+        # modifier key - ignore - for details see
         # https://github.com/nim-lang/Nim/issues/7764
         continue
       else:
@@ -838,16 +869,6 @@ proc resetAttributes*() {.noconv.} =
   ## ``system.addQuitProc(resetAttributes)``.
   resetAttributes(stdout)
 
-when not defined(testing) and isMainModule:
-  #system.addQuitProc(resetAttributes)
-  write(stdout, "never mind")
-  stdout.eraseLine()
-  stdout.styledWriteLine("styled text ", {styleBright, styleBlink, styleUnderscore})
-  stdout.setBackGroundColor(bgCyan, true)
-  stdout.setForeGroundColor(fgBlue)
-  stdout.writeLine("ordinary text")
-  stdout.resetAttributes()
-
 proc isTrueColorSupported*(): bool =
   ## Returns true if a terminal supports true color.
   return trueColorIsSupported
@@ -898,3 +919,43 @@ proc disableTrueColors*() =
       trueColorIsEnabled = false
   else:
     trueColorIsEnabled = false
+
+when not defined(testing) and isMainModule:
+  #system.addQuitProc(resetAttributes)
+  write(stdout, "never mind")
+  stdout.eraseLine()
+  stdout.styledWriteLine({styleBright, styleBlink, styleUnderscore}, "styled text ")
+  stdout.styledWriteLine("italic text ", {styleItalic})
+  stdout.setBackGroundColor(bgCyan, true)
+  stdout.setForeGroundColor(fgBlue)
+  stdout.write("blue text in cyan background")
+  stdout.resetAttributes()
+  echo ""
+  stdout.writeLine("ordinary text")
+  echo "more ordinary text"
+  styledEcho styleBright, fgGreen, "[PASS]", resetStyle, fgGreen, " Yay!"
+  echo "ordinary text again"
+  styledEcho styleBright, fgRed, "[FAIL]", resetStyle, fgRed, " Nay :("
+  echo "ordinary text again"
+  setForeGroundColor(fgGreen)
+  echo "green text"
+  echo "more green text"
+  setForeGroundColor(fgBlue)
+  echo "blue text"
+  resetAttributes()
+  echo "ordinary text"
+
+  stdout.styledWriteLine(fgRed, "red text ")
+  stdout.styledWriteLine(fgWhite, bgRed, "white text in red background")
+  stdout.styledWriteLine(" ordinary text ")
+  stdout.styledWriteLine(fgGreen, "green text")
+
+  stdout.styledWrite(fgRed, "red text ")
+  stdout.styledWrite(fgWhite, bgRed, "white text in red background")
+  stdout.styledWrite(" ordinary text ")
+  stdout.styledWrite(fgGreen, "green text")
+  echo ""
+  echo "ordinary text"
+  stdout.styledWriteLine(fgRed, "red text ", styleBright, "bold red", fgDefault, " bold text")
+  stdout.styledWriteLine(bgYellow, "text in yellow bg", styleBright, " bold text in yellow bg", bgDefault, " bold text")
+  echo "ordinary text"
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 7cecc31ab..7fd60b818 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -238,11 +238,11 @@ proc utcZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .}
 proc utcZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .}
 proc localZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .}
 proc localZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .}
-proc initTime*(unix: int64, nanosecond: NanosecondRange): Time 
+proc initTime*(unix: int64, nanosecond: NanosecondRange): Time
   {.tags: [], raises: [], benign noSideEffect.}
 
 proc initDuration*(nanoseconds, microseconds, milliseconds,
-                   seconds, minutes, hours, days, weeks: int64 = 0): Duration 
+                   seconds, minutes, hours, days, weeks: int64 = 0): Duration
   {.tags: [], raises: [], benign noSideEffect.}
 
 proc nanosecond*(time: Time): NanosecondRange =
@@ -531,7 +531,7 @@ proc `$`*(dur: Duration): string =
     let quantity = numParts[unit]
     if quantity != 0.int64:
       parts.add(stringifyUnit(quantity, $unit))
-  
+
   result = humanizeParts(parts)
 
 proc `+`*(a, b: Duration): Duration {.operator.} =
@@ -1243,23 +1243,23 @@ proc evaluateStaticInterval(interval: TimeInterval): Duration =
     minutes = interval.minutes,
     hours = interval.hours)
 
-proc between*(startDt, endDt:DateTime): TimeInterval =
+proc between*(startDt, endDt: DateTime): TimeInterval =
   ## Evaluate difference between two dates in ``TimeInterval`` format, so, it
   ## will be relative.
   ##
-  ## **Warning:** It's not recommended to use ``between`` for ``DateTime's`` in 
-  ## different ``TimeZone's``.  
+  ## **Warning:** It's not recommended to use ``between`` for ``DateTime's`` in
+  ## different ``TimeZone's``.
   ## ``a + between(a, b) == b`` is only guaranteed when ``a`` and ``b`` are in UTC.
   runnableExamples:
-    var a = initDateTime(year = 2018, month = Month(3), monthday = 25, 
+    var a = initDateTime(year = 2018, month = Month(3), monthday = 25,
                      hour = 0, minute = 59, second = 59, nanosecond = 1,
                      zone = utc()).local
-    var b = initDateTime(year = 2018, month = Month(3), monthday = 25, 
+    var b = initDateTime(year = 2018, month = Month(3), monthday = 25,
                      hour = 1, minute =  1, second =  1, nanosecond = 0,
                      zone = utc()).local
     doAssert between(a, b) == initTimeInterval(
       nanoseconds=999, milliseconds=999, microseconds=999, seconds=1, minutes=1)
-    
+
     a = parse("2018-01-09T00:00:00+00:00", "yyyy-MM-dd'T'HH:mm:sszzz", utc())
     b = parse("2018-01-10T23:00:00-02:00", "yyyy-MM-dd'T'HH:mm:sszzz")
     doAssert between(a, b) == initTimeInterval(hours=1, days=2)
@@ -1526,7 +1526,6 @@ proc formatToken(dt: DateTime, token: string, buf: var string) =
   else:
     raise newException(ValueError, "Invalid format string: " & token)
 
-
 proc format*(dt: DateTime, f: string): string {.tags: [].}=
   ## This procedure formats `dt` as specified by `f`. The following format
   ## specifiers are available:
@@ -1601,18 +1600,14 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}=
     inc(i)
   formatToken(dt, currentF, result)
 
-proc format*(time: Time, f: string, zone_info: proc(t: Time): DateTime): string {.tags: [].} =
-  ## converts a `Time` value to a string representation. It will use format from
+proc format*(time: Time, f: string, zone: Timezone = local()): string {.tags: [].} =
+  ## Converts a `Time` value to a string representation. It will use format from
   ## ``format(dt: DateTime, f: string)``.
   runnableExamples:
-    var dt = initDateTime(01, mJan, 1970, 00, 00, 00, local())
+    var dt = initDateTime(01, mJan, 1970, 00, 00, 00, utc())
     var tm = dt.toTime()
-    doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", local) == "1970-01-01T00:00:00"
-    dt = initDateTime(01, mJan, 1970, 00, 00, 00, utc())
-    tm = dt.toTime()
-    doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", utc) == "1970-01-01T00:00:00"
-
-  zone_info(time).format(f)
+    doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", utc()) == "1970-01-01T00:00:00"
+  time.inZone(zone).format(f)
 
 proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} =
   ## Converts a `DateTime` object to a string representation.
@@ -1984,16 +1979,12 @@ proc countYearsAndDays*(daySpan: int): tuple[years: int, days: int] =
 proc toTimeInterval*(time: Time): TimeInterval =
   ## Converts a Time to a TimeInterval.
   ##
-  ## To be used when diffing times.
+  ## To be used when diffing times. Consider using `between` instead.
   runnableExamples:
     let a = fromUnix(10)
-    let dt = initDateTime(01, mJan, 1970, 00, 00, 00, local())
-    doAssert a.toTimeInterval() == initTimeInterval(
-      years=1970, days=1, seconds=10, hours=convert(
-        Seconds, Hours, -dt.utcOffset
-      )
-    )
-
+    let b = fromUnix(1_500_000_000)
+    let ti = b.toTimeInterval() - a.toTimeInterval()
+    doAssert a + ti == b
   var dt = time.local
   initTimeInterval(dt.nanosecond, 0, 0, dt.second, dt.minute, dt.hour,
     dt.monthday, 0, dt.month.ord - 1, dt.year)
@@ -2150,7 +2141,7 @@ proc timeToTimeInterval*(t: Time): TimeInterval {.deprecated.} =
   t.toTimeInterval()
 
 proc getDayOfWeek*(day, month, year: int): WeekDay  {.tags: [], raises: [], benign, deprecated.} =
-  ## **Deprecated since v0.18.0:** use 
+  ## **Deprecated since v0.18.0:** use
   ## ``getDayOfWeek(monthday: MonthdayRange; month: Month; year: int)`` instead.
   getDayOfWeek(day, month.Month, year)
 
diff --git a/lib/std/varints.nim b/lib/std/varints.nim
index bfc1945fe..483d5c96c 100644
--- a/lib/std/varints.nim
+++ b/lib/std/varints.nim
@@ -19,7 +19,7 @@ proc readVu64*(z: openArray[byte]; pResult: var uint64): int =
     return 1
   if z[0] <= 248:
     if z.len < 2: return 0
-    pResult = (uint64 z[0] - 241) * 256 + uint64 z[1] + 240
+    pResult = (uint64 z[0] - 241) * 256 + z[1].uint64 + 240
     return 2
   if z.len < int(z[0]-246): return 0
   if z[0] == 249:
@@ -135,6 +135,13 @@ when isMainModule:
     if encodeZigzag(decodeZigzag(test)) != test:
       echo "Failure for ", test, " ", encodeZigzag(decodeZigzag(test)), " ", decodeZigzag(test)
 
+  for test in 0u64..300u64:
+    let wrLen = writeVu64(dest, test)
+    let rdLen = readVu64(dest, got)
+    assert wrLen == rdLen
+    if got != test:
+      echo "BUG! expected: ", test, " got: ", got, " z0: ", dest[0]
+
   # check this also works for floats:
   for test in [0.0, 0.1, 2.0, +Inf, Nan, NegInf]:
     let t = cast[uint64](test)
diff --git a/lib/system.nim b/lib/system.nim
index fb02fde23..15f952feb 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -179,10 +179,24 @@ proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} =
   ## Cannot be overloaded.
   discard
 
-proc `type`*(x: untyped): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} =
-  ## Builtin 'type' operator for accessing the type of an expression.
-  ## Cannot be overloaded.
-  discard
+when defined(nimNewTypedesc):
+  type
+    `static`* {.magic: "Static".}[T]
+      ## meta type representing all values that can be evaluated at compile-time.
+      ##
+      ## The type coercion ``static(x)`` can be used to force the compile-time
+      ## evaluation of the given expression ``x``.
+
+    `type`* {.magic: "Type".}[T]
+      ## meta type representing the type of all type values.
+      ##
+      ## The coercion ``type(x)`` can be used to obtain the type of the given
+      ## expression ``x``.
+else:
+  proc `type`*(x: untyped): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} =
+    ## Builtin 'type' operator for accessing the type of an expression.
+    ## Cannot be overloaded.
+    discard
 
 proc `not` *(x: bool): bool {.magic: "Not", noSideEffect.}
   ## Boolean not; returns true iff ``x == false``.
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index 2c9b1e502..b33ca93f2 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -562,7 +562,8 @@ else:
 when not declared(nimNewSeqOfCap):
   proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
     when defined(gcRegions):
-      result = newStr(typ, cap, ntfNoRefs notin typ.base.flags)
+      let s = mulInt(cap, typ.base.size)  # newStr already adds GenericSeqSize
+      result = newStr(typ, s, ntfNoRefs notin typ.base.flags)
     else:
       let s = addInt(mulInt(cap, typ.base.size), GenericSeqSize)
       when declared(newObjNoInit):
diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim
index 7671e5962..5bf69dd94 100644
--- a/lib/system/nimscript.nim
+++ b/lib/system/nimscript.nim
@@ -182,7 +182,7 @@ template checkOsError =
   if err.len > 0: raise newException(OSError, err)
 
 template log(msg: string, body: untyped) =
-  if mode == ScriptMode.Verbose or mode == ScriptMode.Whatif:
+  if mode in {ScriptMode.Verbose, ScriptMode.Whatif}:
     echo "[NimScript] ", msg
   if mode != ScriptMode.WhatIf:
     body
diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim
index 97f97e8ae..b561cd3ba 100644
--- a/lib/system/platforms.nim
+++ b/lib/system/platforms.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-## Platform detection for Nim. This module is included by the system module!
+## Platform detection for NimScript. This module is included by the system module!
 ## Do not import it directly!
 
 type
@@ -62,7 +62,7 @@ const
               elif defined(haiku): OsPlatform.haiku
               elif defined(android): OsPlatform.android
               elif defined(js): OsPlatform.js
-              elif defined(nimrodVM): OsPlatform.nimVM
+              elif defined(nimVM): OsPlatform.nimVM
               elif defined(standalone): OsPlatform.standalone
               else: OsPlatform.none
     ## the OS this program will run on.
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index 7e22f98c7..3263f1d37 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -129,7 +129,6 @@ const
   PIPE_ACCESS_OUTBOUND* = 2'i32
   PIPE_NOWAIT* = 0x00000001'i32
   SYNCHRONIZE* = 0x00100000'i32
-  FILE_FLAG_WRITE_THROUGH* = 0x80000000'i32
 
   CREATE_NO_WINDOW* = 0x08000000'i32
 
@@ -281,15 +280,31 @@ else:
     importc:"CreateHardLinkA", dynlib: "kernel32", stdcall.}
 
 const
-  FILE_ATTRIBUTE_ARCHIVE* = 32'i32
-  FILE_ATTRIBUTE_COMPRESSED* = 2048'i32
-  FILE_ATTRIBUTE_NORMAL* = 128'i32
-  FILE_ATTRIBUTE_DIRECTORY* = 16'i32
-  FILE_ATTRIBUTE_HIDDEN* = 2'i32
-  FILE_ATTRIBUTE_READONLY* = 1'i32
-  FILE_ATTRIBUTE_REPARSE_POINT* = 1024'i32
-  FILE_ATTRIBUTE_SYSTEM* = 4'i32
-  FILE_ATTRIBUTE_TEMPORARY* = 256'i32
+  FILE_ATTRIBUTE_READONLY* = 0x00000001'i32
+  FILE_ATTRIBUTE_HIDDEN* = 0x00000002'i32
+  FILE_ATTRIBUTE_SYSTEM* = 0x00000004'i32
+  FILE_ATTRIBUTE_DIRECTORY* = 0x00000010'i32
+  FILE_ATTRIBUTE_ARCHIVE* = 0x00000020'i32
+  FILE_ATTRIBUTE_DEVICE* = 0x00000040'i32
+  FILE_ATTRIBUTE_NORMAL* = 0x00000080'i32
+  FILE_ATTRIBUTE_TEMPORARY* = 0x00000100'i32
+  FILE_ATTRIBUTE_SPARSE_FILE* = 0x00000200'i32
+  FILE_ATTRIBUTE_REPARSE_POINT* = 0x00000400'i32
+  FILE_ATTRIBUTE_COMPRESSED* = 0x00000800'i32
+  FILE_ATTRIBUTE_OFFLINE* = 0x00001000'i32
+  FILE_ATTRIBUTE_NOT_CONTENT_INDEXED* = 0x00002000'i32
+
+  FILE_FLAG_FIRST_PIPE_INSTANCE* = 0x00080000'i32
+  FILE_FLAG_OPEN_NO_RECALL* = 0x00100000'i32
+  FILE_FLAG_OPEN_REPARSE_POINT* = 0x00200000'i32
+  FILE_FLAG_POSIX_SEMANTICS* = 0x01000000'i32
+  FILE_FLAG_BACKUP_SEMANTICS* = 0x02000000'i32
+  FILE_FLAG_DELETE_ON_CLOSE* = 0x04000000'i32
+  FILE_FLAG_SEQUENTIAL_SCAN* = 0x08000000'i32
+  FILE_FLAG_RANDOM_ACCESS* = 0x10000000'i32
+  FILE_FLAG_NO_BUFFERING* = 0x20000000'i32
+  FILE_FLAG_OVERLAPPED* = 0x40000000'i32
+  FILE_FLAG_WRITE_THROUGH* = 0x80000000'i32
 
   MAX_PATH* = 260
 
@@ -683,8 +698,6 @@ const
   FILE_MAP_WRITE* = 2'i32
   INVALID_FILE_SIZE* = -1'i32
 
-  FILE_FLAG_BACKUP_SEMANTICS* = 33554432'i32
-  FILE_FLAG_OPEN_REPARSE_POINT* = 0x00200000'i32
   DUPLICATE_SAME_ACCESS* = 2
   FILE_READ_DATA* = 0x00000001 # file & pipe
   FILE_WRITE_DATA* = 0x00000002 # file & pipe
@@ -695,6 +708,7 @@ const
   ERROR_PATH_NOT_FOUND* = 3
   ERROR_ACCESS_DENIED* = 5
   ERROR_NO_MORE_FILES* = 18
+  ERROR_LOCK_VIOLATION* = 33
   ERROR_HANDLE_EOF* = 38
   ERROR_BAD_ARGUMENTS* = 165
 
@@ -763,6 +777,9 @@ when not useWinUnicode:
 proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall,
     dynlib: "kernel32", importc: "UnmapViewOfFile".}
 
+proc flushViewOfFile*(lpBaseAddress: pointer, dwNumberOfBytesToFlush: DWORD): WINBOOL {.
+  stdcall, dynlib: "kernel32", importc: "FlushViewOfFile".}
+
 type
   OVERLAPPED* {.pure, inheritable.} = object
     internal*: PULONG
@@ -785,7 +802,6 @@ type
 
 const
   ERROR_IO_PENDING* = 997 # a.k.a WSA_IO_PENDING
-  FILE_FLAG_OVERLAPPED* = 1073741824
   WSAECONNABORTED* = 10053
   WSAEADDRINUSE* = 10048
   WSAECONNRESET* = 10054
diff --git a/tools/nimpretty.nim b/nimpretty/nimpretty.nim
index 396f17b0b..aa9756c45 100644
--- a/tools/nimpretty.nim
+++ b/nimpretty/nimpretty.nim
@@ -12,7 +12,7 @@
 when not defined(nimpretty):
   {.error: "This needs to be compiled with --define:nimPretty".}
 
-import ../compiler / [idents, msgs, ast, syntaxes, renderer]
+import ../compiler / [idents, msgs, ast, syntaxes, renderer, options]
 
 import parseopt, strutils, os
 
@@ -25,6 +25,7 @@ Usage:
   nimpretty [options] file.nim
 Options:
   --backup:on|off     create a backup file before overwritting (default: ON)
+  --output:file       set the output file (default: overwrite the .nim file)
   --version           show the version
   --help              show this help
 """
@@ -39,14 +40,18 @@ proc writeVersion() =
   stdout.flushFile()
   quit(0)
 
-proc prettyPrint(infile: string) =
-  let fileIdx = fileInfoIdx(infile)
-  let tree = parseFile(fileIdx, newIdentCache())
-  let outfile = changeFileExt(infile, ".pretty.nim")
-  renderModule(tree, infile, outfile, {}, fileIdx)
+proc prettyPrint(infile, outfile: string) =
+  var conf = newConfigRef()
+  let fileIdx = fileInfoIdx(conf, infile)
+  conf.outFile = outfile
+  when defined(nimpretty2):
+    discard parseFile(fileIdx, newIdentCache(), conf)
+  else:
+    let tree = parseFile(fileIdx, newIdentCache(), conf)
+    renderModule(tree, infile, outfile, {}, fileIdx, conf)
 
 proc main =
-  var infile: string
+  var infile, outfile: string
   var backup = true
   for kind, key, val in getopt():
     case kind
@@ -57,12 +62,14 @@ proc main =
       of "help", "h": writeHelp()
       of "version", "v": writeVersion()
       of "backup": backup = parseBool(val)
+      of "output", "o": outfile = val
       else: writeHelp()
     of cmdEnd: assert(false) # cannot happen
   if infile.len == 0:
     quit "[Error] no input file."
   if backup:
     os.copyFile(source=infile, dest=changeFileExt(infile, ".nim.backup"))
-  prettyPrint(infile)
+  if outfile.len == 0: outfile = infile
+  prettyPrint(infile, outfile)
 
 main()
diff --git a/nimpretty/nimpretty.nim.cfg b/nimpretty/nimpretty.nim.cfg
new file mode 100644
index 000000000..5fafa6d2a
--- /dev/null
+++ b/nimpretty/nimpretty.nim.cfg
@@ -0,0 +1,2 @@
+--define: nimpretty
+--define: nimpretty2
diff --git a/nimpretty/tester.nim b/nimpretty/tester.nim
new file mode 100644
index 000000000..8798ce06a
--- /dev/null
+++ b/nimpretty/tester.nim
@@ -0,0 +1,29 @@
+# Small program that runs the test cases
+
+import strutils, os
+
+const
+  dir = "nimpretty/tests/"
+
+var
+  failures = 0
+
+proc test(infile, outfile: string) =
+  if execShellCmd("nimpretty -o:$2 --backup:off $1" % [infile, outfile]) != 0:
+    quit("FAILURE")
+  let nimFile = splitFile(infile).name
+  let expected = dir / "expected" / nimFile & ".nim"
+  let produced = dir / nimFile & ".pretty"
+  if strip(readFile(expected)) != strip(readFile(produced)):
+    echo "FAILURE: files differ: ", nimFile
+    discard execShellCmd("diff -uNdr " & expected & " " & produced)
+    failures += 1
+  else:
+    echo "SUCCESS: files identical: ", nimFile
+
+for t in walkFiles(dir / "*.nim"):
+  let res = t.changeFileExt("pretty")
+  test(t, res)
+  removeFile(res)
+
+if failures > 0: quit($failures & " failures occurred.")
diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim
new file mode 100644
index 000000000..a2501a193
--- /dev/null
+++ b/nimpretty/tests/exhaustive.nim
@@ -0,0 +1,316 @@
+discard """
+  outputsub: '''ObjectAssignmentError'''
+  exitcode: "1"
+"""
+
+import verylongnamehere,verylongnamehere,verylongnamehereverylongnamehereverylong,namehere,verylongnamehere
+
+proc `[]=`() = discard "index setter"
+proc `putter=`() = discard cast[pointer](cast[int](buffer) + size)
+
+(not false)
+
+let expr = if true: "true" else: "false"
+
+var body = newNimNode(nnkIfExpr).add(
+  newNimNode(nnkElifBranch).add(
+    infix(newDotExpr(ident("a"), ident("kind")), "==", newDotExpr(ident("b"), ident("kind"))),
+    condition
+  ),
+  newNimNode(nnkElse).add(newStmtList(newNimNode(nnkReturnStmt).add(ident("false"))))
+)
+
+# comment
+
+var x = 1
+
+type
+  GeneralTokenizer* = object of RootObj ## comment here
+    kind*: TokenClass ## and here
+    start*, length*: int ## you know how it goes...
+    buf: cstring
+    pos: int # other comment here.
+    state: TokenClass
+
+var x*: string
+var y: seq[string] #[ yay inline comments. So nice I have to care bout these. ]#
+
+echo "#", x, "##", y, "#" & "string" & $test
+
+echo (tup, here)
+echo(argA, argB)
+
+import macros
+
+## A documentation comment here.
+## That spans multiple lines.
+## And is not to be touched.
+
+const numbers = [4u8, 5'u16, 89898_00]
+
+macro m(n): untyped =
+  result = foo"string literal"
+
+{.push m.}
+proc p() = echo "p", 1+4 * 5, if true: 5 else: 6
+proc q(param: var ref ptr string) =
+  p()
+  if true:
+    echo a and b or not c and not -d
+{.pop.}
+
+q()
+
+when false:
+  # bug #4766
+  type
+    Plain = ref object
+      discard
+
+    Wrapped[T] = object
+      value: T
+
+  converter toWrapped[T](value: T): Wrapped[T] =
+    Wrapped[T](value: value)
+
+  let result = Plain()
+  discard $result
+
+when false:
+  # bug #3670
+  template someTempl(someConst: bool) =
+    when someConst:
+      var a: int
+    if true:
+      when not someConst:
+        var a: int
+      a = 5
+
+  someTempl(true)
+
+
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Layouter for nimpretty. Still primitive but useful.
+
+import idents, lexer, lineinfos, llstream, options, msgs, strutils
+from os import changeFileExt
+
+const
+  MaxLineLen = 80
+  LineCommentColumn = 30
+
+type
+  SplitKind = enum
+    splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary
+
+  Emitter* = object
+    f: PLLStream
+    config: ConfigRef
+    fid: FileIndex
+    lastTok: TTokType
+    inquote {.pragmaHereWrongCurlyEnd}: bool
+    col, lastLineNumber, lineSpan, indentLevel: int
+    content: string
+    fixedUntil: int # marks where we must not go in the content
+    altSplitPos: array[SplitKind, int] # alternative split positions
+
+proc openEmitter*[T, S](em: var Emitter; config: ConfigRef, fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd} =
+  let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim")
+  em.f = llStreamOpen(outfile, fmWrite)
+  em.config = config
+  em.fid = fileIdx
+  em.lastTok = tkInvalid
+  em.inquote = false
+  em.col = 0
+  em.content = newStringOfCap(16_000)
+  if em.f == nil:
+    rawMessage(config, errGenerated, "cannot open file: " & outfile)
+
+proc closeEmitter*(em: var Emitter) {.inline.} =
+  em.f.llStreamWrite em.content
+  llStreamClose(em.f)
+
+proc countNewlines(s: string): int =
+  result = 0
+  for i in 0..<s.len:
+    if s[i+1] == '\L': inc result
+
+proc calcCol(em: var Emitter; s: string) =
+  var i = s.len-1
+  em.col = 0
+  while i >= 0 and s[i] != '\L':
+    dec i
+    inc em.col
+
+template wr(x) =
+  em.content.add x
+  inc em.col, x.len
+
+template goodCol(col): bool = col in 40..MaxLineLen
+
+const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe,
+                   tkBracketLe, tkBracketLeColon, tkCurlyDotLe,
+                   tkCurlyLe}
+
+template rememberSplit(kind) =
+  if goodCol(em.col):
+    em.altSplitPos[kind] = em.content.len
+
+proc softLinebreak(em: var Emitter, lit: string) =
+  # XXX Use an algorithm that is outlined here:
+  # https://llvm.org/devmtg/2013-04/jasper-slides.pdf
+  # +2 because we blindly assume a comma or ' &' might follow
+  if not em.inquote and em.col+lit.len+2 >= MaxLineLen:
+    if em.lastTok in splitters:
+      wr("\L")
+      em.col = 0
+      for i in 1..em.indentLevel+2: wr(" ")
+    else:
+      # search backwards for a good split position:
+      for a in em.altSplitPos:
+        if a > em.fixedUntil:
+          let ws = "\L" & repeat(' ',em.indentLevel+2)
+          em.col = em.content.len - a
+          em.content.insert(ws, a)
+          break
+
+proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
+
+  template endsInWhite(em): bool =
+    em.content.len > 0 and em.content[em.content.high] in {' ', '\L'}
+  template endsInAlpha(em): bool =
+    em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'}
+
+  proc emitComment(em: var Emitter; tok: TToken) =
+    let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, tok.commentOffsetB)
+    em.lineSpan = countNewlines(lit)
+    if em.lineSpan > 0: calcCol(em, lit)
+    if not endsInWhite(em):
+      wr(" ")
+      if em.lineSpan == 0 and max(em.col, LineCommentColumn) + lit.len <= MaxLineLen:
+        for i in 1 .. LineCommentColumn - em.col: wr(" ")
+    wr lit
+
+  var preventComment = case tok.tokType
+                       of tokKeywordLow..tokKeywordHigh:
+                          if endsInAlpha(em): wr(" ")
+                          wr(TokTypeToStr[tok.tokType])
+
+                          case tok.tokType
+                          of tkAnd: rememberSplit(splitAnd)
+                          of tkOr: rememberSplit(splitOr)
+                          of tkIn: rememberSplit(splitIn)
+                          else: 90
+                       else:
+                         "case returns value"
+
+
+  if tok.tokType == tkComment and tok.line == em.lastLineNumber and tok.indent >= 0:
+    # we have an inline comment so handle it before the indentation token:
+    emitComment(em, tok)
+    preventComment = true
+    em.fixedUntil = em.content.high
+
+  elif tok.indent >= 0:
+        em.indentLevel = tok.indent
+        # remove trailing whitespace:
+        while em.content.len > 0 and em.content[em.content.high] == ' ':
+          setLen(em.content, em.content.len-1)
+        wr("\L")
+        for i in 2..tok.line - em.lastLineNumber: wr("\L")
+        em.col = 0
+        for i in 1..tok.indent:
+          wr(" ")
+        em.fixedUntil = em.content.high
+
+  case tok.tokType
+  of tokKeywordLow..tokKeywordHigh:
+    if endsInAlpha(em): wr(" ")
+    wr(TokTypeToStr[tok.tokType])
+
+    case tok.tokType
+    of tkAnd: rememberSplit(splitAnd)
+    of tkOr: rememberSplit(splitOr)
+    of tkIn: rememberSplit(splitIn)
+    else: discard
+
+  of tkColon:
+    wr(TokTypeToStr[tok.tokType])
+    wr(" ")
+  of tkSemicolon,
+     tkComma:
+    wr(TokTypeToStr[tok.tokType])
+    wr(" ")
+    rememberSplit(splitComma)
+  of tkParLe, tkParRi, tkBracketLe,
+     tkBracketRi, tkCurlyLe, tkCurlyRi,
+     tkBracketDotLe, tkBracketDotRi,
+     tkCurlyDotLe, tkCurlyDotRi,
+     tkParDotLe, tkParDotRi,
+     tkColonColon, tkDot, tkBracketLeColon:
+    wr(TokTypeToStr[tok.tokType])
+    if tok.tokType in splitters:
+      rememberSplit(splitParLe)
+  of tkEquals:
+    if not em.endsInWhite: wr(" ")
+    wr(TokTypeToStr[tok.tokType])
+    wr(" ")
+  of tkOpr, tkDotDot:
+    if not em.endsInWhite: wr(" ")
+    wr(tok.ident.s)
+    template isUnary(tok): bool =
+      tok.strongSpaceB == 0 and tok.strongSpaceA > 0
+
+    if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}:
+      wr(" ")
+      rememberSplit(splitBinary)
+  of tkAccent:
+    wr(TokTypeToStr[tok.tokType])
+    em.inquote = not em.inquote
+  of tkComment:
+    if not preventComment:
+      emitComment(em, tok)
+  of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit:
+    let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
+    softLinebreak(em, lit)
+    if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wr(" ")
+    em.lineSpan = countNewlines(lit)
+    if em.lineSpan > 0: calcCol(em, lit)
+    wr lit
+  of tkEof: discard
+  else:
+    let lit = if tok.ident != nil: tok.ident.s else: tok.literal
+    softLinebreak(em, lit)
+    if endsInAlpha(em): wr(" ")
+    wr lit
+
+  em.lastTok = tok.tokType
+  em.lastLineNumber = tok.line + em.lineSpan
+  em.lineSpan = 0
+
+proc starWasExportMarker*(em: var Emitter) =
+  if em.content.endsWith(" * "):
+    setLen(em.content, em.content.len-3)
+    em.content.add("*")
+    dec em.col, 2
+
+type
+  Thing = ref object
+    grade: string
+    # this name is great
+    name: string
+
+proc f() =
+  var c: char
+  var str: string
+  if c == '\\':
+    # escape char
+    str &= c
diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim
new file mode 100644
index 000000000..95071fce3
--- /dev/null
+++ b/nimpretty/tests/expected/exhaustive.nim
@@ -0,0 +1,325 @@
+discard """
+  outputsub: '''ObjectAssignmentError'''
+  exitcode: "1"
+"""
+
+import verylongnamehere, verylongnamehere,
+  verylongnamehereverylongnamehereverylong, namehere, verylongnamehere
+
+proc `[]=`() = discard "index setter"
+proc `putter=`() = discard cast[pointer](cast[int](buffer) + size)
+
+(not false)
+
+let expr = if true: "true" else: "false"
+
+var body = newNimNode(nnkIfExpr).add(
+  newNimNode(nnkElifBranch).add(
+    infix(newDotExpr(ident("a"), ident("kind")), "==", newDotExpr(ident("b"),
+        ident("kind"))),
+    condition
+  ),
+  newNimNode(nnkElse).add(newStmtList(newNimNode(nnkReturnStmt).add(ident(
+      "false"))))
+)
+
+# comment
+
+var x = 1
+
+type
+  GeneralTokenizer* = object of RootObj ## comment here
+    kind*: TokenClass         ## and here
+    start*, length*: int      ## you know how it goes...
+    buf: cstring
+    pos: int                  # other comment here.
+    state: TokenClass
+
+var x*: string
+var y: seq[string] #[ yay inline comments. So nice I have to care bout these. ]#
+
+echo "#", x, "##", y, "#" & "string" & $test
+
+echo (tup, here)
+echo(argA, argB)
+
+import macros
+
+## A documentation comment here.
+## That spans multiple lines.
+## And is not to be touched.
+
+const numbers = [4u8, 5'u16, 89898_00]
+
+macro m(n): untyped =
+  result = foo"string literal"
+
+{.push m.}
+proc p() = echo "p", 1+4 * 5, if true: 5 else: 6
+proc q(param: var ref ptr string) =
+  p()
+  if true:
+    echo a and b or not c and not -d
+{.pop.}
+
+q()
+
+when false:
+  # bug #4766
+  type
+    Plain = ref object
+      discard
+
+    Wrapped[T] = object
+      value: T
+
+  converter toWrapped[T](value: T): Wrapped[T] =
+    Wrapped[T](value: value)
+
+  let result = Plain()
+  discard $result
+
+when false:
+  # bug #3670
+  template someTempl(someConst: bool) =
+    when someConst:
+      var a: int
+    if true:
+      when not someConst:
+        var a: int
+      a = 5
+
+  someTempl(true)
+
+
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2018 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Layouter for nimpretty. Still primitive but useful.
+
+import idents, lexer, lineinfos, llstream, options, msgs, strutils
+from os import changeFileExt
+
+const
+  MaxLineLen = 80
+  LineCommentColumn = 30
+
+type
+  SplitKind = enum
+    splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary
+
+  Emitter* = object
+    f: PLLStream
+    config: ConfigRef
+    fid: FileIndex
+    lastTok: TTokType
+    inquote {.pragmaHereWrongCurlyEnd.}: bool
+    col, lastLineNumber, lineSpan, indentLevel: int
+    content: string
+    fixedUntil: int           # marks where we must not go in the content
+    altSplitPos: array[SplitKind, int] # alternative split positions
+
+proc openEmitter*[T, S](em: var Emitter; config: ConfigRef;
+    fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd.} =
+  let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim")
+  em.f = llStreamOpen(outfile, fmWrite)
+  em.config = config
+  em.fid = fileIdx
+  em.lastTok = tkInvalid
+  em.inquote = false
+  em.col = 0
+  em.content = newStringOfCap(16_000)
+  if em.f == nil:
+    rawMessage(config, errGenerated, "cannot open file: " & outfile)
+
+proc closeEmitter*(em: var Emitter) {.inline.} =
+  em.f.llStreamWrite em.content
+  llStreamClose(em.f)
+
+proc countNewlines(s: string): int =
+  result = 0
+  for i in 0..<s.len:
+    if s[i+1] == '\L': inc result
+
+proc calcCol(em: var Emitter; s: string) =
+  var i = s.len-1
+  em.col = 0
+  while i >= 0 and s[i] != '\L':
+    dec i
+    inc em.col
+
+template wr(x) =
+  em.content.add x
+  inc em.col, x.len
+
+template goodCol(col): bool = col in 40..MaxLineLen
+
+const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe,
+                   tkBracketLe, tkBracketLeColon, tkCurlyDotLe,
+                   tkCurlyLe}
+
+template rememberSplit(kind) =
+  if goodCol(em.col):
+    em.altSplitPos[kind] = em.content.len
+
+proc softLinebreak(em: var Emitter; lit: string) =
+  # XXX Use an algorithm that is outlined here:
+  # https://llvm.org/devmtg/2013-04/jasper-slides.pdf
+  # +2 because we blindly assume a comma or ' &' might follow
+  if not em.inquote and em.col+lit.len+2 >= MaxLineLen:
+    if em.lastTok in splitters:
+      wr("\L")
+      em.col = 0
+      for i in 1..em.indentLevel+2: wr(" ")
+    else:
+      # search backwards for a good split position:
+      for a in em.altSplitPos:
+        if a > em.fixedUntil:
+          let ws = "\L" & repeat(' ', em.indentLevel+2)
+          em.col = em.content.len - a
+          em.content.insert(ws, a)
+          break
+
+proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
+
+  template endsInWhite(em): bool =
+    em.content.len > 0 and em.content[em.content.high] in {' ', '\L'}
+  template endsInAlpha(em): bool =
+    em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'}
+
+  proc emitComment(em: var Emitter; tok: TToken) =
+    let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA,
+        tok.commentOffsetB)
+    em.lineSpan = countNewlines(lit)
+    if em.lineSpan > 0: calcCol(em, lit)
+    if not endsInWhite(em):
+      wr(" ")
+      if em.lineSpan == 0 and max(em.col,
+          LineCommentColumn) + lit.len <= MaxLineLen:
+        for i in 1 .. LineCommentColumn - em.col: wr(" ")
+    wr lit
+
+  var preventComment = case tok.tokType
+    of tokKeywordLow..tokKeywordHigh:
+      if endsInAlpha(em): wr(" ")
+      wr(TokTypeToStr[tok.tokType])
+
+      case tok.tokType
+      of tkAnd: rememberSplit(splitAnd)
+      of tkOr: rememberSplit(splitOr)
+      of tkIn: rememberSplit(splitIn)
+      else: 90
+    else:
+      "case returns value"
+
+
+  if tok.tokType == tkComment and tok.line == em.lastLineNumber and
+      tok.indent >= 0:
+    # we have an inline comment so handle it before the indentation token:
+    emitComment(em, tok)
+    preventComment = true
+    em.fixedUntil = em.content.high
+
+  elif tok.indent >= 0:
+    em.indentLevel = tok.indent
+    # remove trailing whitespace:
+    while em.content.len > 0 and em.content[em.content.high] == ' ':
+      setLen(em.content, em.content.len-1)
+    wr("\L")
+    for i in 2..tok.line - em.lastLineNumber: wr("\L")
+    em.col = 0
+    for i in 1..tok.indent:
+      wr(" ")
+    em.fixedUntil = em.content.high
+
+  case tok.tokType
+  of tokKeywordLow..tokKeywordHigh:
+    if endsInAlpha(em): wr(" ")
+    wr(TokTypeToStr[tok.tokType])
+
+    case tok.tokType
+    of tkAnd: rememberSplit(splitAnd)
+    of tkOr: rememberSplit(splitOr)
+    of tkIn: rememberSplit(splitIn)
+    else: discard
+
+  of tkColon:
+    wr(TokTypeToStr[tok.tokType])
+    wr(" ")
+  of tkSemicolon,
+     tkComma:
+    wr(TokTypeToStr[tok.tokType])
+    wr(" ")
+    rememberSplit(splitComma)
+  of tkParLe, tkParRi, tkBracketLe,
+     tkBracketRi, tkCurlyLe, tkCurlyRi,
+     tkBracketDotLe, tkBracketDotRi,
+     tkCurlyDotLe, tkCurlyDotRi,
+     tkParDotLe, tkParDotRi,
+     tkColonColon, tkDot, tkBracketLeColon:
+    wr(TokTypeToStr[tok.tokType])
+    if tok.tokType in splitters:
+      rememberSplit(splitParLe)
+  of tkEquals:
+    if not em.endsInWhite: wr(" ")
+    wr(TokTypeToStr[tok.tokType])
+    wr(" ")
+  of tkOpr, tkDotDot:
+    if not em.endsInWhite: wr(" ")
+    wr(tok.ident.s)
+    template isUnary(tok): bool =
+      tok.strongSpaceB == 0 and tok.strongSpaceA > 0
+
+    if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}:
+      wr(" ")
+      rememberSplit(splitBinary)
+  of tkAccent:
+    wr(TokTypeToStr[tok.tokType])
+    em.inquote = not em.inquote
+  of tkComment:
+    if not preventComment:
+      emitComment(em, tok)
+  of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit,
+      tkGTripleStrLit, tkCharLit:
+    let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
+    softLinebreak(em, lit)
+    if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wr(
+        " ")
+    em.lineSpan = countNewlines(lit)
+    if em.lineSpan > 0: calcCol(em, lit)
+    wr lit
+  of tkEof: discard
+  else:
+    let lit = if tok.ident != nil: tok.ident.s else: tok.literal
+    softLinebreak(em, lit)
+    if endsInAlpha(em): wr(" ")
+    wr lit
+
+  em.lastTok = tok.tokType
+  em.lastLineNumber = tok.line + em.lineSpan
+  em.lineSpan = 0
+
+proc starWasExportMarker*(em: var Emitter) =
+  if em.content.endsWith(" * "):
+    setLen(em.content, em.content.len-3)
+    em.content.add("*")
+    dec em.col, 2
+
+type
+  Thing = ref object
+    grade: string
+    # this name is great
+    name: string
+
+proc f() =
+  var c: char
+  var str: string
+  if c == '\\':
+    # escape char
+    str &= c
diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim
index 5c505ddf7..3ba1120b2 100644
--- a/nimsuggest/nimsuggest.nim
+++ b/nimsuggest/nimsuggest.nim
@@ -9,6 +9,9 @@
 
 ## Nimsuggest is a tool that helps to give editors IDE like capabilities.
 
+when not defined(nimcore):
+  {.error: "nimcore MUST be defined for Nim's core tooling".}
+
 import strutils, os, parseopt, parseutils, sequtils, net, rdstdin, sexp
 # Do NOT import suggest. It will lead to wierd bugs with
 # suggestionResultHook, because suggest.nim is included by sigmatch.
@@ -17,7 +20,7 @@ import compiler / [options, commands, modules, sem,
   passes, passaux, msgs, nimconf,
   extccomp, condsyms,
   sigmatch, ast, scriptconfig,
-  idents, modulegraphs, vm, prefixmatches]
+  idents, modulegraphs, vm, prefixmatches, lineinfos]
 
 when defined(windows):
   import winlean
@@ -74,8 +77,8 @@ proc writelnToChannel(line: string) =
 proc sugResultHook(s: Suggest) =
   results.send(s)
 
-proc errorHook(info: TLineInfo; msg: string; sev: Severity) =
-  results.send(Suggest(section: ideChk, filePath: toFullPath(info),
+proc errorHook(conf: ConfigRef; info: TLineInfo; msg: string; sev: Severity) =
+  results.send(Suggest(section: ideChk, filePath: toFullPath(conf, info),
     line: toLinenumber(info), column: toColumn(info), doc: msg,
     forth: $sev))
 
@@ -109,7 +112,7 @@ proc sexp(s: Suggest): SexpNode =
   let qp = if s.qualifiedPath.isNil: @[] else: s.qualifiedPath
   result = convertSexp([
     s.section,
-    s.symkind,
+    TSymKind s.symkind,
     qp.map(newSString),
     s.filePath,
     s.forth,
@@ -141,49 +144,48 @@ proc listEpc(): SexpNode =
     methodDesc.add(docstring)
     result.add(methodDesc)
 
-proc findNode(n: PNode): PSym =
+proc findNode(n: PNode; trackPos: TLineInfo): PSym =
   #echo "checking node ", n.info
   if n.kind == nkSym:
-    if isTracked(n.info, n.sym.name.s.len): return n.sym
+    if isTracked(n.info, trackPos, n.sym.name.s.len): return n.sym
   else:
     for i in 0 ..< safeLen(n):
-      let res = n.sons[i].findNode
+      let res = findNode(n[i], trackPos)
       if res != nil: return res
 
-proc symFromInfo(graph: ModuleGraph; gTrackPos: TLineInfo): PSym =
-  let m = graph.getModule(gTrackPos.fileIndex)
-  #echo m.isNil, " I knew it ", gTrackPos.fileIndex
+proc symFromInfo(graph: ModuleGraph; trackPos: TLineInfo): PSym =
+  let m = graph.getModule(trackPos.fileIndex)
   if m != nil and m.ast != nil:
-    result = m.ast.findNode
+    result = findNode(m.ast, trackPos)
 
 proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
-             graph: ModuleGraph; cache: IdentCache) =
+             graph: ModuleGraph) =
   let conf = graph.config
   myLog("cmd: " & $cmd & ", file: " & file & ", dirtyFile: " & dirtyfile &
         "[" & $line & ":" & $col & "]")
   conf.ideCmd = cmd
   if cmd == ideChk:
-    msgs.structuredErrorHook = errorHook
-    msgs.writelnHook = myLog
+    conf.structuredErrorHook = errorHook
+    conf.writelnHook = myLog
   else:
-    msgs.structuredErrorHook = nil
-    msgs.writelnHook = myLog
-  if cmd == ideUse and suggestVersion != 0:
+    conf.structuredErrorHook = nil
+    conf.writelnHook = myLog
+  if cmd == ideUse and conf.suggestVersion != 0:
     graph.resetAllModules()
   var isKnownFile = true
   let dirtyIdx = fileInfoIdx(conf, file, isKnownFile)
 
-  if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile)
-  else: msgs.setDirtyFile(dirtyIdx, nil)
+  if dirtyfile.len != 0: msgs.setDirtyFile(conf, dirtyIdx, dirtyfile)
+  else: msgs.setDirtyFile(conf, dirtyIdx, nil)
 
-  gTrackPos = newLineInfo(dirtyIdx, line, col)
-  gTrackPosAttached = false
+  conf.m.trackPos = newLineInfo(dirtyIdx, line, col)
+  conf.m.trackPosAttached = false
   conf.errorCounter = 0
-  if suggestVersion == 1:
+  if conf.suggestVersion == 1:
     graph.usageSym = nil
   if not isKnownFile:
-    graph.compileProject(cache)
-  if suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and
+    graph.compileProject()
+  if conf.suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and
       dirtyfile.len == 0:
     discard "no need to recompile anything"
   else:
@@ -191,16 +193,16 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int;
     graph.markDirty dirtyIdx
     graph.markClientsDirty dirtyIdx
     if conf.ideCmd != ideMod:
-      graph.compileProject(cache, modIdx)
+      graph.compileProject(modIdx)
   if conf.ideCmd in {ideUse, ideDus}:
-    let u = if suggestVersion != 1: graph.symFromInfo(gTrackPos) else: graph.usageSym
+    let u = if conf.suggestVersion != 1: graph.symFromInfo(conf.m.trackPos) else: graph.usageSym
     if u != nil:
       listUsages(conf, u)
     else:
-      localError(conf, gTrackPos, "found no symbol at this position " & $gTrackPos)
+      localError(conf, conf.m.trackPos, "found no symbol at this position " & (conf $ conf.m.trackPos))
 
 proc executeEpc(cmd: IdeCmd, args: SexpNode;
-                graph: ModuleGraph; cache: IdentCache) =
+                graph: ModuleGraph) =
   let
     file = args[0].getStr
     line = args[1].getNum
@@ -208,7 +210,7 @@ proc executeEpc(cmd: IdeCmd, args: SexpNode;
   var dirtyfile = ""
   if len(args) > 3:
     dirtyfile = args[3].getStr(nil)
-  execute(cmd, file, dirtyfile, int(line), int(column), graph, cache)
+  execute(cmd, file, dirtyfile, int(line), int(column), graph)
 
 proc returnEpc(socket: Socket, uid: BiggestInt, s: SexpNode|string,
                return_symbol = "return") =
@@ -377,7 +379,7 @@ proc replEpc(x: ThreadParams) {.thread.} =
                          "unexpected call: " & epcAPI
       quit errMessage
 
-proc execCmd(cmd: string; graph: ModuleGraph; cache: IdentCache; cachedMsgs: CachedMsgs) =
+proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
   let conf = graph.config
 
   template sentinel() =
@@ -432,20 +434,20 @@ proc execCmd(cmd: string; graph: ModuleGraph; cache: IdentCache; cachedMsgs: Cac
     results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, orig))))
   else:
     if conf.ideCmd == ideChk:
-      for cm in cachedMsgs: errorHook(cm.info, cm.msg, cm.sev)
-    execute(conf.ideCmd, orig, dirtyfile, line, col, graph, cache)
+      for cm in cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev)
+    execute(conf.ideCmd, orig, dirtyfile, line, col, graph)
   sentinel()
 
-proc recompileFullProject(graph: ModuleGraph; cache: IdentCache) =
+proc recompileFullProject(graph: ModuleGraph) =
   #echo "recompiling full project"
   resetSystemArtifacts(graph)
-  vm.globalCtx = nil
+  graph.vm = nil
   graph.resetAllModules()
   GC_fullcollect()
-  compileProject(graph, cache)
+  compileProject(graph)
   #echo GC_getStatistics()
 
-proc mainThread(graph: ModuleGraph; cache: IdentCache) =
+proc mainThread(graph: ModuleGraph) =
   let conf = graph.config
   if gLogging:
     for it in conf.searchPaths:
@@ -457,17 +459,17 @@ proc mainThread(graph: ModuleGraph; cache: IdentCache) =
     else:
       writelnToChannel(line)
 
-  msgs.writelnHook = wrHook
-  suggestionResultHook = sugResultHook
+  conf.writelnHook = wrHook
+  conf.suggestionResultHook = sugResultHook
   graph.doStopCompile = proc (): bool = requests.peek() > 0
   var idle = 0
   var cachedMsgs: CachedMsgs = @[]
   while true:
     let (hasData, req) = requests.tryRecv()
     if hasData:
-      msgs.writelnHook = wrHook
-      suggestionResultHook = sugResultHook
-      execCmd(req, graph, cache, cachedMsgs)
+      conf.writelnHook = wrHook
+      conf.suggestionResultHook = sugResultHook
+      execCmd(req, graph, cachedMsgs)
       idle = 0
     else:
       os.sleep 250
@@ -475,23 +477,22 @@ proc mainThread(graph: ModuleGraph; cache: IdentCache) =
     if idle == 20 and gRefresh:
       # we use some nimsuggest activity to enable a lazy recompile:
       conf.ideCmd = ideChk
-      msgs.writelnHook = proc (s: string) = discard
+      conf.writelnHook = proc (s: string) = discard
       cachedMsgs.setLen 0
-      msgs.structuredErrorHook = proc (info: TLineInfo; msg: string; sev: Severity) =
+      conf.structuredErrorHook = proc (conf: ConfigRef; info: TLineInfo; msg: string; sev: Severity) =
         cachedMsgs.add(CachedMsg(info: info, msg: msg, sev: sev))
-      suggestionResultHook = proc (s: Suggest) = discard
-      recompileFullProject(graph, cache)
+      conf.suggestionResultHook = proc (s: Suggest) = discard
+      recompileFullProject(graph)
 
 var
   inputThread: Thread[ThreadParams]
 
-proc mainCommand(graph: ModuleGraph; cache: IdentCache) =
+proc mainCommand(graph: ModuleGraph) =
   let conf = graph.config
-  clearPasses()
-  registerPass verbosePass
-  registerPass semPass
+  clearPasses(graph)
+  registerPass graph, verbosePass
+  registerPass graph, semPass
   conf.cmd = cmdIdeTools
-  incl conf.globalOptions, optCaasEnabled
   wantMainModule(conf)
 
   if not fileExists(conf.projectFull):
@@ -502,12 +503,12 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) =
   # do not stop after the first error:
   conf.errorMax = high(int)
   # do not print errors, but log them
-  msgs.writelnHook = proc (s: string) = log(s)
-  msgs.structuredErrorHook = nil
+  conf.writelnHook = proc (s: string) = log(s)
+  conf.structuredErrorHook = nil
 
   # compile the project before showing any input so that we already
   # can answer questions right away:
-  compileProject(graph, cache)
+  compileProject(graph)
 
   open(requests)
   open(results)
@@ -520,7 +521,7 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) =
                             (gPort, "sug \"" & conf.projectFull & "\":" & gAddress))
   of mcmdcon: createThread(inputThread, replCmdline,
                             (gPort, "con \"" & conf.projectFull & "\":" & gAddress))
-  mainThread(graph, cache)
+  mainThread(graph)
   joinThread(inputThread)
   close(requests)
   close(results)
@@ -555,8 +556,8 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
         gMode = mepc
         conf.verbosity = 0          # Port number gotta be first.
       of "debug": incl(conf.globalOptions, optIdeDebug)
-      of "v2": suggestVersion = 0
-      of "v1": suggestVersion = 1
+      of "v2": conf.suggestVersion = 0
+      of "v1": conf.suggestVersion = 1
       of "tester":
         gMode = mstdin
         gEmitEof = true
@@ -568,7 +569,7 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
         else:
           gRefresh = true
       of "maxresults":
-        suggestMaxResults = parseInt(p.val)
+        conf.suggestMaxResults = parseInt(p.val)
       else: processSwitch(pass, p, conf)
     of cmdArgument:
       let a = unixToNativePath(p.key)
@@ -589,7 +590,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
   else:
     processCmdLine(passCmd1, "", conf)
     if gMode != mstdin:
-      msgs.writelnHook = proc (msg: string) = discard
+      conf.writelnHook = proc (msg: string) = discard
     if conf.projectName != "":
       try:
         conf.projectFull = canonicalizePath(conf, conf.projectName)
@@ -628,8 +629,8 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
     extccomp.initVars(conf)
     processCmdLine(passCmd2, "", conf)
 
-    let graph = newModuleGraph(conf)
+    let graph = newModuleGraph(cache, conf)
     graph.suggestMode = true
-    mainCommand(graph, cache)
+    mainCommand(graph)
 
 handleCmdline(newIdentCache(), newConfigRef())
diff --git a/nimsuggest/nimsuggest.nim.cfg b/nimsuggest/nimsuggest.nim.cfg
index 38e74b3c7..820db0dba 100644
--- a/nimsuggest/nimsuggest.nim.cfg
+++ b/nimsuggest/nimsuggest.nim.cfg
@@ -8,6 +8,8 @@ path:"$lib/packages/docutils"
 
 define:useStdoutAsStdmsg
 define:nimsuggest
+define:nimcore
+
 # die when nimsuggest uses more than 4GB:
 @if cpu32:
   define:"nimMaxHeap=2000"
@@ -15,7 +17,6 @@ define:nimsuggest
   define:"nimMaxHeap=4000"
 @end
 
-#cs:partial
 #define:useNodeIds
 #define:booting
 #define:noDocgen
diff --git a/tests/arithm/tnot.nim b/tests/arithm/tnot.nim
new file mode 100644
index 000000000..6a4877b2c
--- /dev/null
+++ b/tests/arithm/tnot.nim
@@ -0,0 +1,58 @@
+discard """
+  output: '''
+-5
+-5
+-5
+-5
+4
+4
+4
+4
+251
+65531
+4294967291
+18446744073709551611
+4
+4
+4
+4
+'''
+"""
+
+# Signed types
+block:
+  const t0: int8  = not 4
+  const t1: int16 = not 4
+  const t2: int32 = not 4
+  const t3: int64 = not 4
+  const t4: int8  = not -5
+  const t5: int16 = not -5
+  const t6: int32 = not -5
+  const t7: int64 = not -5
+  echo t0
+  echo t1
+  echo t2
+  echo t3
+  echo t4
+  echo t5
+  echo t6
+  echo t7
+
+# Unsigned types
+block:
+  const t0: uint8  = not 4'u8
+  const t1: uint16 = not 4'u16
+  const t2: uint32 = not 4'u32
+  const t3: uint64 = not 4'u64
+  const t4: uint8  = not 251'u8
+  const t5: uint16 = not 65531'u16
+  const t6: uint32 = not 4294967291'u32
+  const t7: uint64 = not 18446744073709551611'u64
+  echo t0
+  echo t1
+  echo t2
+  echo t3
+  echo t4
+  echo t5
+  echo t6
+  echo t7
diff --git a/tests/arithm/tshl.nim b/tests/arithm/tshl.nim
new file mode 100644
index 000000000..0aa46d021
--- /dev/null
+++ b/tests/arithm/tshl.nim
@@ -0,0 +1,34 @@
+discard """
+  output: '''
+0
+0
+1
+1
+0
+0
+0
+1
+'''
+"""
+
+# Signed types
+block:
+  const t0: int8  = 1'i8 shl 8
+  const t1: int16 = 1'i16 shl 16
+  const t2: int32 = 1'i32 shl 32
+  const t3: int64 = 1'i64 shl 64
+  echo t0
+  echo t1
+  echo t2
+  echo t3
+
+# Unsigned types
+block:
+  const t0: uint8  = 1'u8 shl 8
+  const t1: uint16 = 1'u16 shl 16
+  const t2: uint32 = 1'u32 shl 32
+  const t3: uint64 = 1'u64 shl 64
+  echo t0
+  echo t1
+  echo t2
+  echo t3
diff --git a/tests/async/tasynctry2.nim b/tests/async/tasynctry2.nim
deleted file mode 100644
index 4b3f17cc5..000000000
--- a/tests/async/tasynctry2.nim
+++ /dev/null
@@ -1,18 +0,0 @@
-discard """
-  file: "tasynctry2.nim"
-  errormsg: "\'yield\' cannot be used within \'try\' in a non-inlined iterator"
-  line: 14
-"""
-import asyncdispatch
-
-{.experimental: "oldIterTransf".}
-
-proc foo(): Future[bool] {.async.} = discard
-
-proc test5(): Future[int] {.async.} =
-  try:
-    discard await foo()
-    raise newException(ValueError, "Test5")
-  except:
-    discard await foo()
-    result = 0
diff --git a/tests/compilerapi/exposed.nim b/tests/compilerapi/exposed.nim
new file mode 100644
index 000000000..73becd93d
--- /dev/null
+++ b/tests/compilerapi/exposed.nim
@@ -0,0 +1,3 @@
+
+proc addFloats*(x, y, z: float): float =
+  discard "implementation overriden by tcompilerapi.nim"
diff --git a/tests/compilerapi/myscript.nim b/tests/compilerapi/myscript.nim
new file mode 100644
index 000000000..539b07de1
--- /dev/null
+++ b/tests/compilerapi/myscript.nim
@@ -0,0 +1,9 @@
+
+import exposed
+
+echo "top level statements are executed!"
+
+proc hostProgramRunsThis*(a, b: float): float =
+  result = addFloats(a, b, 1.0)
+
+let hostProgramWantsThis* = "my secret"
diff --git a/tests/compilerapi/tcompilerapi.nim b/tests/compilerapi/tcompilerapi.nim
new file mode 100644
index 000000000..90d343264
--- /dev/null
+++ b/tests/compilerapi/tcompilerapi.nim
@@ -0,0 +1,47 @@
+discard """
+  output: '''top level statements are executed!
+2.0
+my secret
+'''
+"""
+
+## Example program that demonstrates how to use the
+## compiler as an API to embed into your own projects.
+
+import "../../compiler" / [ast, vmdef, vm, nimeval]
+import std / [os]
+
+proc main() =
+  let std = findNimStdLib()
+  if std.len == 0:
+    quit "cannot find Nim's standard library"
+
+  var intr = createInterpreter("myscript.nim", [std, getAppDir()])
+  intr.implementRoutine("*", "exposed", "addFloats", proc (a: VmArgs) =
+    setResult(a, getFloat(a, 0) + getFloat(a, 1) + getFloat(a, 2))
+  )
+
+  intr.evalScript()
+
+  let foreignProc = selectRoutine(intr, "hostProgramRunsThis")
+  if foreignProc == nil:
+    quit "script does not export a proc of the name: 'hostProgramRunsThis'"
+  let res = intr.callRoutine(foreignProc, [newFloatNode(nkFloatLit, 0.9),
+                                           newFloatNode(nkFloatLit, 0.1)])
+  if res.kind == nkFloatLit:
+    echo res.floatVal
+  else:
+    echo "bug!"
+
+  let foreignValue = selectUniqueSymbol(intr, "hostProgramWantsThis")
+  if foreignValue == nil:
+    quit "script does not export a global of the name: hostProgramWantsThis"
+  let val = intr.getGlobalValue(foreignValue)
+  if val.kind in {nkStrLit..nkTripleStrLit}:
+    echo val.strVal
+  else:
+    echo "bug!"
+
+  destroyInterpreter(intr)
+
+main()
\ No newline at end of file
diff --git a/tests/concepts/libs/trie.nim b/tests/concepts/libs/trie.nim
new file mode 100644
index 000000000..bdd318492
--- /dev/null
+++ b/tests/concepts/libs/trie.nim
@@ -0,0 +1,26 @@
+import
+  hashes, tables, trie_database
+
+type
+  MemDBTable = Table[KeccakHash, string]
+
+  MemDB* = object
+    tbl: MemDBTable
+
+proc hash*(key: KeccakHash): int =
+  hashes.hash(key.data)
+
+proc get*(db: MemDB, key: KeccakHash): string =
+  db.tbl[key]
+
+proc del*(db: var MemDB, key: KeccakHash): bool =
+  if db.tbl.hasKey(key):
+    db.tbl.del(key)
+    return true
+  else:
+    return false
+
+proc put*(db: var MemDB, key: KeccakHash, value: string): bool =
+  db.tbl[key] = value
+  return true
+
diff --git a/tests/concepts/libs/trie_database.nim b/tests/concepts/libs/trie_database.nim
new file mode 100644
index 000000000..a45c64842
--- /dev/null
+++ b/tests/concepts/libs/trie_database.nim
@@ -0,0 +1,12 @@
+type
+  KeccakHash* = object
+    data*: string
+
+  BytesRange* = object
+    bytes*: string
+
+  TrieDatabase* = concept db
+    put(var db, KeccakHash, string) is bool
+    del(var db, KeccakHash) is bool
+    get(db, KeccakHash) is string
+
diff --git a/tests/concepts/t6770.nim b/tests/concepts/t6770.nim
new file mode 100644
index 000000000..1787ee670
--- /dev/null
+++ b/tests/concepts/t6770.nim
@@ -0,0 +1,27 @@
+discard """
+output: '''
+10
+10
+'''
+"""
+
+type GA = concept c
+  c.a is int
+
+type A = object
+  a: int
+
+type AA = object
+  case exists: bool
+  of true:
+    a: int
+  else:
+    discard
+
+proc print(inp: GA) =
+  echo inp.a
+
+let failing = AA(exists: true, a: 10)
+let working = A(a:10)
+print(working)
+print(failing)
diff --git a/tests/concepts/treversable.nim b/tests/concepts/treversable.nim
new file mode 100644
index 000000000..6ebc077d9
--- /dev/null
+++ b/tests/concepts/treversable.nim
@@ -0,0 +1,31 @@
+# issue 7705, 7703, 7702
+discard """
+  output: '''
+z
+e
+  '''
+"""
+
+type
+  Reversable*[T] = concept a
+    a[int] is T
+    a.high is int
+    a.len is int
+    a.low is int
+
+proc get[T](s: Reversable[T], n: int): T =
+  s[n]
+
+proc hi[T](s: Reversable[T]): int =
+  s.high
+
+proc lo[T](s: Reversable[T]): int =
+  s.low
+
+iterator reverse*[T](s: Reversable[T]): T =
+  assert hi(s) - lo(s) == len(s) - 1
+  for z in hi(s).countdown(lo(s)):
+    yield s.get(z)
+
+for s in @["e", "z"].reverse:
+  echo s
diff --git a/tests/concepts/ttrieconcept.nim b/tests/concepts/ttrieconcept.nim
new file mode 100644
index 000000000..a26e6b146
--- /dev/null
+++ b/tests/concepts/ttrieconcept.nim
@@ -0,0 +1,7 @@
+import libs/[trie_database, trie]
+
+proc takeDb(d: TrieDatabase) = discard
+var mdb: MemDB
+
+takeDb(mdb)
+
diff --git a/tests/cpp/tempty_generic_obj.nim b/tests/cpp/tempty_generic_obj.nim
index b4c746a30..b61c699f6 100644
--- a/tests/cpp/tempty_generic_obj.nim
+++ b/tests/cpp/tempty_generic_obj.nim
@@ -20,3 +20,19 @@ v.doSomething()
 
 var vf = initVector[float]()
 vf.doSomething() # Nim uses doSomething[int] here in C++
+
+# Alternative definition:
+# https://github.com/nim-lang/Nim/issues/7653
+
+type VectorAlt* {.importcpp: "std::vector", header: "<vector>", nodecl.} [T] = object
+proc mkVector*[T]: VectorAlt[T] {.importcpp: "std::vector<'*0>()", header: "<vector>", constructor, nodecl.}
+
+proc foo(): VectorAlt[cint] =
+  mkVector[cint]()
+
+proc bar(): VectorAlt[cstring] =
+  mkVector[cstring]()
+
+var x = foo()
+var y = bar()
+
diff --git a/tests/errmsgs/tproper_stacktrace.nim b/tests/errmsgs/tproper_stacktrace.nim
index 57e65fa6f..134946651 100644
--- a/tests/errmsgs/tproper_stacktrace.nim
+++ b/tests/errmsgs/tproper_stacktrace.nim
@@ -1,11 +1,141 @@
 discard """
-  outputsub: '''tproper_stacktrace.nim(7) tproper_stacktrace'''
-  exitcode: 1
+  output: '''ok'''
 """
+import strscans, strutils
 
-template fuzzy(x) =
-  echo x[] != 9
+proc raiseTestException*() =
+  raise newException(Exception, "test")
 
-var p: ptr int
-fuzzy p
+proc matchStackTrace(actualEntries: openarray[StackTraceEntry], expected: string) =
+  var expectedEntries = newSeq[StackTraceEntry]()
+  var i = 0
 
+  template checkEqual(actual, expected: typed, subject: string) =
+    if actual != expected:
+      echo "Unexpected ", subject, " on line ", i
+      echo "Actual: ", actual
+      echo "Expected: ", expected
+      doAssert(false)
+
+  for l in splitLines(expected.strip):
+    var procname, filename: string
+    var line: int
+    if not scanf(l, "$s$w.nim($i) $w", filename, line, procname):
+      doAssert(false, "Wrong expected stack trace")
+    checkEqual($actualEntries[i].filename, filename & ".nim", "file name")
+    if line != 0:
+      checkEqual(actualEntries[i].line, line, "line number")
+    checkEqual($actualEntries[i].procname, procname, "proc name")
+    inc i
+
+  doAssert(i == actualEntries.len, "Unexpected number of lines in stack trace")
+
+template verifyStackTrace*(expectedStackTrace: string, body: untyped) =
+  var verified = false
+  try:
+    body
+  except Exception as e:
+    verified = true
+    # echo "Stack trace:"
+    # echo e.getStackTrace
+    matchStackTrace(e.getStackTraceEntries(), expectedStackTrace)
+
+  doAssert(verified, "No exception was raised")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+when isMainModule:
+# <-- Align with line 70 in the text editor
+  block:
+    proc bar() =
+      raiseTestException()
+
+    proc foo() =
+      bar()
+
+    const expectedStackTrace = """
+      tproper_stacktrace.nim(86) tproper_stacktrace
+      tproper_stacktrace.nim(76) foo
+      tproper_stacktrace.nim(73) bar
+      tproper_stacktrace.nim(7) raiseTestException
+    """
+
+    verifyStackTrace expectedStackTrace:
+      foo()
+
+  block:
+    proc bar(x: int) =
+      raiseTestException()
+
+    template foo(x: int) =
+      bar(x)
+
+    const expectedStackTrace = """
+      tproper_stacktrace.nim(103) tproper_stacktrace
+      tproper_stacktrace.nim(90) bar
+      tproper_stacktrace.nim(7) raiseTestException
+    """
+
+    verifyStackTrace expectedStackTrace:
+      var x: int
+      foo(x)
+
+  block: #6803
+    proc bar(x = 500) =
+      raiseTestException()
+
+    proc foo() =
+      bar()
+
+    const expectedStackTrace = """
+      tproper_stacktrace.nim(120) tproper_stacktrace
+      tproper_stacktrace.nim(110) foo
+      tproper_stacktrace.nim(107) bar
+      tproper_stacktrace.nim(7) raiseTestException
+    """
+
+    verifyStackTrace expectedStackTrace:
+      foo()
+
+  block:
+    proc bar() {.stackTrace: off.} =
+      proc baz() = # Stack trace should be enabled
+        raiseTestException()
+      baz()
+
+    proc foo() =
+      bar()
+
+    const expectedStackTrace = """
+      tproper_stacktrace.nim(139) tproper_stacktrace
+      tproper_stacktrace.nim(129) foo
+      tproper_stacktrace.nim(125) baz
+      tproper_stacktrace.nim(7) raiseTestException
+    """
+
+    verifyStackTrace expectedStackTrace:
+      foo()
+
+  echo "ok"
diff --git a/tests/float/tfloatrange.nim b/tests/float/tfloatrange.nim
new file mode 100644
index 000000000..e8ea1912e
--- /dev/null
+++ b/tests/float/tfloatrange.nim
@@ -0,0 +1,49 @@
+discard """
+  cmd: "nim c -d:release --rangeChecks:on $file"
+  output: '''StrictPositiveRange
+float
+range fail expected
+range fail expected
+'''
+"""
+import math, fenv
+
+type
+  Positive = range[0.0..Inf]
+  StrictPositive = range[minimumPositiveValue(float)..Inf]
+  Negative32 = range[-maximumPositiveValue(float32) .. -1.0'f32]
+
+proc myoverload(x: float) =
+  echo "float"
+
+proc myoverload(x: Positive) =
+  echo "PositiveRange"
+
+proc myoverload(x: StrictPositive) =
+  echo "StrictPositiveRange"
+
+let x = 9.0.StrictPositive
+myoverload(x)
+myoverload(9.0)
+
+doAssert(sqrt(x) == 3.0)
+
+var z = -10.0
+try:
+  myoverload(StrictPositive(z))
+except:
+  echo "range fail expected"
+  
+  
+proc strictOnlyProc(x: StrictPositive): bool =
+  if x > 1.0: true else: false
+  
+let x2 = 5.0.Positive
+doAssert(strictOnlyProc(x2))
+
+try:
+  let x4 = 0.0.Positive
+  discard strictOnlyProc(x4)
+except:
+  echo "range fail expected"
+  
\ No newline at end of file
diff --git a/tests/generics/t3977.nim b/tests/generics/t3977.nim
index 314017744..eed1a7d63 100644
--- a/tests/generics/t3977.nim
+++ b/tests/generics/t3977.nim
@@ -1,12 +1,14 @@
 discard """
-  output: '''42'''
+  output: "42\n42"
 """
 
 type
   Foo[N: static[int]] = object
 
 proc foo[N](x: Foo[N]) =
+  let n = N
   echo N
+  echo n
 
 var f1: Foo[42]
 f1.foo
diff --git a/tests/generics/tlateboundgenericparams.nim b/tests/generics/tlateboundgenericparams.nim
new file mode 100644
index 000000000..9f0580fd2
--- /dev/null
+++ b/tests/generics/tlateboundgenericparams.nim
@@ -0,0 +1,145 @@
+discard """
+  output: "1\n10\n1\n10"
+  nimout: '''
+bar instantiated with 1
+bar instantiated with 10
+'''
+"""
+
+import typetraits
+
+type
+  Foo = object
+
+proc defaultFoo: Foo = discard
+proc defaultInt: int = 1
+proc defaultTInt(T: type): int = 2
+proc defaultTFoo[T](x: typedesc[T]): Foo = discard
+proc defaultTOldSchool[T](x: typedesc[T]): T = discard
+proc defaultTModern(T: type): T = discard
+
+proc specializedDefault(T: type int): int = 10
+proc specializedDefault(T: type string): string = "default"
+
+converter intFromFoo(x: Foo): int = 3
+
+proc consumeInt(x: int) =
+  discard
+
+const activeTests = {1..100}
+
+when true:
+  template test(n, body) =
+    when n in activeTests:
+      block:
+        body
+
+  template reject(x) =
+    static: assert(not compiles(x))
+
+  test 1:
+    proc t[T](val: T = defaultInt()) =
+      consumeInt val
+
+    t[int]()
+    reject t[string]()
+
+  test 2:
+    proc t1[T](val: T = defaultFoo()) =
+      static:
+        assert type(val).name == "int"
+        assert T.name == "int"
+
+      consumeInt val
+
+    # here, the converter should kick in, but notice
+    # how `val` is still typed `int` inside the proc.
+    t1[int]()
+
+    proc t2[T](val: T = defaultFoo()) =
+      discard
+
+    reject t2[string]()
+
+  test 3:
+    proc tInt[T](val = defaultInt()): string =
+      return type(val).name
+
+    doAssert tInt[int]() == "int"
+    doAssert tInt[string]() == "int"
+
+    proc tInt2[T](val = defaultTInt(T)): string =
+      return type(val).name
+
+    doAssert tInt2[int]() == "int"
+    doAssert tInt2[string]() == "int"
+
+    proc tDefTModern[T](val = defaultTModern(T)): string =
+      return type(val).name
+
+    doAssert tDefTModern[int]() == "int"
+    doAssert tDefTModern[string]() == "string"
+    doAssert tDefTModern[Foo]() == "Foo"
+
+    proc tDefTOld[T](val = defaultTOldSchool(T)): string =
+      return type(val).name
+
+    doAssert tDefTOld[int]() == "int"
+    doAssert tDefTOld[string]() == "string"
+    doAssert tDefTOld[Foo]() == "Foo"
+
+  test 4:
+    proc t[T](val: T = defaultTFoo(T)): string =
+      return type(val).name
+
+    doAssert t[int]() == "int"
+    doAssert t[Foo]() == "Foo"
+    reject t[string]()
+
+  test 5:
+    proc t1[T](a: T = specializedDefault(T)): T =
+      return a
+
+    doAssert t1[int]() == 10
+    doAssert t1[string]() == "default"
+
+    proc t2[T](a: T, b = specializedDefault(T)): auto =
+      return $a & $b
+
+    doAssert t2(5) == "510"
+    doAssert t2("string ") == "string default"
+
+    proc t3[T](a: T, b = specializedDefault(type(a))): auto =
+      return $a & $b
+
+    doAssert t3(100) == "10010"
+    doAssert t3("another ") == "another default"
+
+  test 6:
+    # https://github.com/nim-lang/Nim/issues/5595
+    type
+      Point[T] = object
+        x, y: T
+
+    proc getOrigin[T](): Point[T] = Point[T](x: 0, y: 0)
+
+    proc rotate[T](point: Point[T], radians: float,
+                   origin = getOrigin[T]()): Point[T] =
+      discard
+
+    var p = getOrigin[float]()
+    var rotated = p.rotate(2.1)
+
+  test 7:
+    proc bar(x: static[int]) =
+      static: echo "bar instantiated with ", x
+      echo x
+
+    proc foo(x: static[int] = 1) =
+      bar(x)
+
+    foo()
+    foo(10)
+    foo(1)
+    foo(10)
+
diff --git a/tests/global/t5958.nim b/tests/global/t5958.nim
new file mode 100644
index 000000000..5abcad4a9
--- /dev/null
+++ b/tests/global/t5958.nim
@@ -0,0 +1,9 @@
+discard """
+  line: 9
+  errormsg: "undeclared identifier: 'a'"
+"""
+
+static:
+  var a = 1
+
+echo a
diff --git a/tests/lexer/tmissingnl.nim b/tests/lexer/tmissingnl.nim
index 33b7debf1..095d9570e 100644
--- a/tests/lexer/tmissingnl.nim
+++ b/tests/lexer/tmissingnl.nim
@@ -4,7 +4,7 @@ discard """
   errormsg: "invalid indentation"
 """
 
-import strutils var s: seq[int] = @[0, 1, 2, 3, 4, 5, 6]
+import strutils let s: seq[int] = @[0, 1, 2, 3, 4, 5, 6]
 
 #s[1..3] = @[]
 
diff --git a/tests/macros/tmacros1.nim b/tests/macros/tmacros1.nim
index 9e3ab028b..80afb6641 100644
--- a/tests/macros/tmacros1.nim
+++ b/tests/macros/tmacros1.nim
@@ -12,12 +12,12 @@ macro outterMacro*(n, blck: untyped): untyped =
     echo "Using arg ! " & n.repr
     result = "Got: '" & $n.kind & "' " & $j
   var callNode = n[0]
-  expectKind(n, TNimrodNodeKind.nnkCall)
-  if n.len != 3 or n[1].kind != TNimrodNodeKind.nnkIdent:
+  expectKind(n, NimNodeKind.nnkCall)
+  if n.len != 3 or n[1].kind != NimNodeKind.nnkIdent:
     error("Macro " & callNode.repr &
       " requires the ident passed as parameter (eg: " & callNode.repr &
       "(the_name_you_want)): statements.")
-  result = newNimNode(TNimrodNodeKind.nnkStmtList)
+  result = newNimNode(NimNodeKind.nnkStmtList)
   var ass : NimNode = newNimNode(nnkAsgn)
   ass.add(newIdentNode(n[1].ident))
   ass.add(newStrLitNode(innerProc(4)))
diff --git a/tests/macros/tmacrostmt.nim b/tests/macros/tmacrostmt.nim
index 6f648958f..849a32ea3 100644
--- a/tests/macros/tmacrostmt.nim
+++ b/tests/macros/tmacrostmt.nim
@@ -1,5 +1,5 @@
 import macros
-macro case_token(n: untyped): untyped {.immediate.} =
+macro case_token(n: varargs[untyped]): untyped =
   # creates a lexical analyzer from regular expressions
   # ... (implementation is an exercise for the reader :-)
   nil
@@ -21,6 +21,6 @@ case_token: inc i
 macro foo: typed =
   var exp = newCall("whatwhat", newIntLitNode(1))
   if compiles(getAst(exp)): return exp
-  else: echo "Does not compute!"
+  else: echo "Does not compute! (test OK)"
 
 foo()
diff --git a/tests/metatype/ttypedesc1.nim b/tests/metatype/ttypedesc1.nim
index e9eee581f..837c8eccc 100644
--- a/tests/metatype/ttypedesc1.nim
+++ b/tests/metatype/ttypedesc1.nim
@@ -5,15 +5,16 @@ type
     x: T
     y: U
 
-proc getTypeName(t: typedesc): string = t.name
+proc getTypeName1(t: typedesc): string = t.name
+proc getTypeName2(t: type): string = t.name
 
-proc foo(T: typedesc[float], a: auto): string =
+proc foo(T: type float, a: auto): string =
   result = "float " & $(a.len > 5)
 
 proc foo(T: typedesc[TFoo], a: int): string =
   result = "TFoo "  & $(a)
 
-proc foo(T: typedesc[int or bool]): string =
+proc foo(T: type[int or bool]): string =
   var a: T
   a = 10
   result = "int or bool " & ($a)
@@ -23,8 +24,8 @@ template foo(T: typedesc[seq]): string = "seq"
 test "types can be used as proc params":
   # XXX: `check` needs to know that TFoo[int, float] is a type and
   # cannot be assigned for a local variable for later inspection
-  check ((string.getTypeName == "string"))
-  check ((getTypeName(int) == "int"))
+  check ((string.getTypeName1 == "string"))
+  check ((getTypeName2(int) == "int"))
 
   check ((foo(TFoo[int, float], 1000) == "TFoo 1000"))
 
@@ -37,6 +38,25 @@ test "types can be used as proc params":
   check ((foo(seq[int]) == "seq"))
   check ((foo(seq[TFoo[bool, string]]) == "seq"))
 
-when false:
-  proc foo(T: typedesc[seq], s: T) = nil
+template accept(x) =
+  static: assert(compiles(x))
+
+template reject(x) =
+  static: assert(not compiles(x))
+
+var
+  si: seq[int]
+  ss: seq[string]
+
+proc foo(T: typedesc[seq], s: T) =
+  discard
+
+accept:
+  foo seq[int], si
+
+reject:
+  foo seq[string], si
+
+reject:
+  foo seq[int], ss
 
diff --git a/tests/metatype/ttypedesc2.nim b/tests/metatype/ttypedesc2.nim
index 4b6cfe6bc..94a7367e7 100644
--- a/tests/metatype/ttypedesc2.nim
+++ b/tests/metatype/ttypedesc2.nim
@@ -34,3 +34,17 @@ when true:
 type Point[T] = tuple[x, y: T]
 proc origin(T: typedesc): Point[T] = discard
 discard origin(int)
+
+# https://github.com/nim-lang/Nim/issues/7516
+import typetraits
+
+proc hasDefault1(T: type = int): auto = return T.name
+doAssert hasDefault1(int) == "int"
+doAssert hasDefault1(string) == "string"
+doAssert hasDefault1() == "int"
+
+proc hasDefault2(T = string): auto = return T.name
+doAssert hasDefault2(int) == "int"
+doAssert hasDefault2(string) == "string"
+doAssert hasDefault2() == "string"
+
diff --git a/tests/metatype/ttypedesc3.nim b/tests/metatype/ttypedesc3.nim
index 9f19bd6e3..3d1cf2ec9 100644
--- a/tests/metatype/ttypedesc3.nim
+++ b/tests/metatype/ttypedesc3.nim
@@ -4,9 +4,9 @@ type
   Base = object of RootObj
   Child = object of Base
 
-proc pr(T: typedesc[Base]) = echo "proc " & T.name
-method me(T: typedesc[Base]) = echo "method " & T.name
-iterator it(T: typedesc[Base]): auto = yield "yield " & T.name
+proc pr(T: type[Base]) = echo "proc " & T.name
+method me(T: type[Base]) = echo "method " & T.name
+iterator it(T: type[Base]): auto = yield "yield " & T.name
 
 Base.pr
 Child.pr
diff --git a/tests/metatype/ttypeselectors.nim b/tests/metatype/ttypeselectors.nim
index 1209fe78f..2a2455adb 100644
--- a/tests/metatype/ttypeselectors.nim
+++ b/tests/metatype/ttypeselectors.nim
@@ -5,16 +5,16 @@ output: "8\n8\n4"
 import
   macros, typetraits
 
-template selectType(x: int): typeDesc =
+template selectType(x: int): type =
   when x < 10:
     int
   else:
     string
 
-template simpleTypeTempl: typeDesc =
+template simpleTypeTempl: type =
   string
 
-macro typeFromMacro: typedesc = string
+macro typeFromMacro: type = string
 
 # The tests below check that the result variable of the
 # selected type matches the literal types in the code:
diff --git a/tests/misc/tparamsindefault.nim b/tests/misc/tparamsindefault.nim
new file mode 100644
index 000000000..c678dcc60
--- /dev/null
+++ b/tests/misc/tparamsindefault.nim
@@ -0,0 +1,114 @@
+discard """
+output: '''
+@[1, 2, 3]@[1, 2, 3]
+a
+a
+1
+3 is an int
+2 is an int
+miau is a string
+f1 1 1 1
+f1 2 3 3
+f1 10 20 30
+f2 100 100 100
+f2 200 300 300
+f2 300 400 400
+f3 10 10 20
+f3 10 15 25
+true true
+false true
+world
+'''
+"""
+
+template reject(x) =
+  assert(not compiles(x))
+
+block:
+  # https://github.com/nim-lang/Nim/issues/7756
+  proc foo[T](x: seq[T], y: seq[T] = x) =
+    echo x, y
+
+  let a = @[1, 2, 3]
+  foo(a)
+
+block:
+  # https://github.com/nim-lang/Nim/issues/1201
+  proc issue1201(x: char|int = 'a') = echo x
+
+  issue1201()
+  issue1201('a')
+  issue1201(1)
+
+  # https://github.com/nim-lang/Nim/issues/7000
+  proc test(a: int|string = 2) =
+    when a is int:
+        echo a, " is an int"
+    elif a is string:
+        echo a, " is a string"
+
+  test(3) # works
+  test() # works
+  test("miau")
+
+block:
+  # https://github.com/nim-lang/Nim/issues/3002 and similar
+  proc f1(a: int, b = a, c = b) =
+    echo "f1 ", a, " ", b, " ", c
+
+  proc f2(a: int, b = a, c: int = b) =
+    echo "f2 ", a, " ", b, " ", c
+
+  proc f3(a: int, b = a, c = a + b) =
+    echo "f3 ", a, " ", b, " ", c
+
+  f1 1
+  f1(2, 3)
+  f1 10, 20, 30
+  100.f2
+  200.f2 300
+  300.f2(400)
+
+  10.f3()
+  10.f3(15)
+
+  reject:
+    # This is a type mismatch error:
+    proc f4(a: int, b = a, c: float = b) = discard
+
+  reject:
+    # undeclared identifier
+    proc f5(a: int, b = c, c = 10) = discard
+
+  reject:
+    # undeclared identifier
+    proc f6(a: int, b = b) = discard
+
+  reject:
+    # undeclared identifier
+    proc f7(a = a) = discard
+
+block:
+  proc f(a: var int, b: ptr int, c = addr(a)) =
+    echo addr(a) == b, " ",  b == c
+
+  var x = 10
+  f(x, addr(x))
+  f(x, nil, nil)
+
+block:
+  # https://github.com/nim-lang/Nim/issues/1046
+  proc pySubstr(s: string, start: int, endd = s.len()): string =
+    var
+      revStart = start
+      revEnd = endd
+
+    if start < 0:
+      revStart = s.len() + start
+    if endd < 0:
+      revEnd = s.len() + endd
+
+    return s[revStart ..  revEnd-1]
+
+  echo pySubstr("Hello world", -5)
+
diff --git a/tests/niminaction/Chapter1/various1.nim b/tests/niminaction/Chapter1/various1.nim
new file mode 100644
index 000000000..688180fd2
--- /dev/null
+++ b/tests/niminaction/Chapter1/various1.nim
@@ -0,0 +1,45 @@
+discard """
+  exitCode: 0
+  outputsub: "Woof!"
+"""
+
+import strutils
+echo("hello".to_upper())
+echo("world".toUpper())
+
+type
+  Dog = object #<1>
+    age: int #<2>
+
+let dog = Dog(age: 3) #<3>
+
+proc showNumber(num: int | float) =
+  echo(num)
+
+showNumber(3.14)
+showNumber(42)
+
+for i in 0 .. <10:
+  echo(i)
+
+block: # Block added due to clash.
+  type
+    Dog = object
+
+  proc bark(self: Dog) = #<1>
+    echo("Woof!")
+
+  let dog = Dog()
+  dog.bark() #<2>
+
+import sequtils, future, strutils
+let list = @["Dominik Picheta", "Andreas Rumpf", "Desmond Hume"]
+list.map(
+  (x: string) -> (string, string) => (x.split[0], x.split[1])
+).echo
+
+import strutils
+let list1 = @["Dominik Picheta", "Andreas Rumpf", "Desmond Hume"]
+for name in list1:
+  echo((name.split[0], name.split[1]))
+
diff --git a/tests/niminaction/Chapter2/explicit_discard.nim b/tests/niminaction/Chapter2/explicit_discard.nim
new file mode 100644
index 000000000..3e94c335b
--- /dev/null
+++ b/tests/niminaction/Chapter2/explicit_discard.nim
@@ -0,0 +1,7 @@
+discard """
+  line: 7
+  errormsg: "has to be discarded"
+"""
+
+proc myProc(name: string): string = "Hello " & name
+myProc("Dominik")
\ No newline at end of file
diff --git a/tests/niminaction/Chapter2/no_def_eq.nim b/tests/niminaction/Chapter2/no_def_eq.nim
new file mode 100644
index 000000000..77f0a7dd8
--- /dev/null
+++ b/tests/niminaction/Chapter2/no_def_eq.nim
@@ -0,0 +1,16 @@
+discard """
+  line: 16
+  errormsg: "type mismatch"
+"""
+
+type
+    Dog = object
+      name: string
+
+    Cat = object
+      name: string
+
+let dog: Dog = Dog(name: "Fluffy")
+let cat: Cat = Cat(name: "Fluffy")
+
+echo(dog == cat)
\ No newline at end of file
diff --git a/tests/niminaction/Chapter2/no_iterator.nim b/tests/niminaction/Chapter2/no_iterator.nim
new file mode 100644
index 000000000..331d69480
--- /dev/null
+++ b/tests/niminaction/Chapter2/no_iterator.nim
@@ -0,0 +1,7 @@
+discard """
+  line: 6
+  errormsg: "type mismatch"
+"""
+
+for i in 5:
+  echo i
\ No newline at end of file
diff --git a/tests/niminaction/Chapter2/no_seq_type.nim b/tests/niminaction/Chapter2/no_seq_type.nim
new file mode 100644
index 000000000..493be270a
--- /dev/null
+++ b/tests/niminaction/Chapter2/no_seq_type.nim
@@ -0,0 +1,6 @@
+discard """
+  line: 6
+  errormsg: "cannot infer the type of the sequence"
+"""
+
+var list = @[]
\ No newline at end of file
diff --git a/tests/niminaction/Chapter2/resultaccept.nim b/tests/niminaction/Chapter2/resultaccept.nim
new file mode 100644
index 000000000..7dd976b40
--- /dev/null
+++ b/tests/niminaction/Chapter2/resultaccept.nim
@@ -0,0 +1,28 @@
+discard """
+  output: ""
+"""
+
+# Page 35.
+
+proc implicit: string =
+  "I will be returned"
+  
+proc discarded: string =
+  discard "I will not be returned"
+  
+proc explicit: string =
+  return "I will be returned"
+
+proc resultVar: string =
+  result = "I will be returned"
+  
+proc resultVar2: string =
+  result = ""
+  result.add("I will be ")
+  result.add("returned")
+
+doAssert implicit() == "I will be returned"
+doAssert discarded() == nil
+doAssert explicit() == "I will be returned"
+doAssert resultVar() == "I will be returned"
+doAssert resultVar2() == "I will be returned"
\ No newline at end of file
diff --git a/tests/niminaction/Chapter2/resultreject.nim b/tests/niminaction/Chapter2/resultreject.nim
new file mode 100644
index 000000000..de59af7d9
--- /dev/null
+++ b/tests/niminaction/Chapter2/resultreject.nim
@@ -0,0 +1,33 @@
+discard """
+  line: 27
+  errormsg: "has to be discarded"
+"""
+
+# Page 35.
+
+proc implicit: string =
+  "I will be returned"
+  
+proc discarded: string =
+  discard "I will not be returned"
+
+proc explicit: string =
+  return "I will be returned"
+
+proc resultVar: string =
+  result = "I will be returned"
+  
+proc resultVar2: string =
+  result = ""
+  result.add("I will be ")
+  result.add("returned")
+
+proc resultVar3: string =
+  result = "I am the result"
+  "I will cause an error"
+
+doAssert implicit() == "I will be returned"
+doAssert discarded() == nil
+doAssert explicit() == "I will be returned"
+doAssert resultVar() == "I will be returned"
+doAssert resultVar2() == "I will be returned"
\ No newline at end of file
diff --git a/tests/niminaction/Chapter2/various2.nim b/tests/niminaction/Chapter2/various2.nim
new file mode 100644
index 000000000..3f6a3f453
--- /dev/null
+++ b/tests/niminaction/Chapter2/various2.nim
@@ -0,0 +1,369 @@
+discard """
+  exitCode: 0
+  outputsub: '''42 is greater than 0'''
+"""
+
+if 42 >= 0:
+  echo "42 is greater than 0"
+
+
+echo("Output: ",
+  5)
+echo(5 +
+  5)
+# --- Removed code that is supposed to fail here. Not going to test those. ---
+
+# Single-line comment
+#[
+Multiline comment
+]#
+when false:
+  echo("Commented-out code")
+
+let decimal = 42
+let hex = 0x42
+let octal = 0o42
+let binary = 0b101010
+
+let a: int16 = 42
+let b = 42'i8
+
+let c = 1'f32 # --- Changed names here to avoid clashes ---
+let d = 1.0e19
+
+let e = false
+let f = true
+
+let g = 'A'
+let h = '\109'
+let i = '\x79'
+
+let text = "The book title is \"Nim in Action\""
+
+let filepath = "C:\\Program Files\\Nim"
+
+# --- Changed name here to avoid clashes ---
+let filepath1 = r"C:\Program Files\Nim"
+
+let multiLine = """foo
+  bar
+  baz
+"""
+echo multiLine
+
+import strutils
+# --- Changed name here to avoid clashes ---
+let multiLine1 = """foo
+  bar
+  baz
+"""
+echo multiLine1.unindent
+doAssert multiLine1.unindent == "foo\nbar\nbaz\n"
+
+proc fillString(): string =
+  result = ""
+  echo("Generating string")
+  for i in 0 .. 4:
+    result.add($i) #<1>
+
+const count = fillString()
+
+var
+  text1 = "hello"
+  number: int = 10
+  isTrue = false
+
+var 火 = "Fire"
+let ogień = true
+
+var `var` = "Hello"
+echo(`var`)
+
+proc myProc(name: string): string = "Hello " & name
+discard myProc("Dominik")
+
+proc bar(): int #<1>
+
+proc foo(): float = bar().float
+proc bar(): int = foo().int
+
+proc noReturn() = echo("Hello")
+proc noReturn2(): void = echo("Hello")
+
+proc noReturn3 = echo("Hello")
+
+proc message(recipient: string): auto =
+  "Hello " & recipient
+
+doAssert message("Dom") == "Hello Dom"
+
+proc max(a: int, b: int): int =
+  if a > b: a else: b
+
+doAssert max(5, 10) == 10
+
+proc max2(a, b: int): int =
+  if a > b: a else: b
+
+proc genHello(name: string, surname = "Doe"): string =
+  "Hello " & name & " " & surname
+
+# -- Leaving these as asserts as that is in the original code, just in case
+# -- somehow in the future `assert` is removed :)
+assert genHello("Peter") == "Hello Peter Doe"
+assert genHello("Peter", "Smith") == "Hello Peter Smith"
+
+proc genHello2(names: varargs[string]): string =
+  result = ""
+  for name in names:
+    result.add("Hello " & name & "\n")
+
+doAssert genHello2("John", "Bob") == "Hello John\nHello Bob\n"
+
+proc getUserCity(firstName, lastName: string): string =
+  case firstName
+  of "Damien": return "Tokyo"
+  of "Alex": return "New York"
+  else: return "Unknown"
+
+proc getUserCity(userID: int): string =
+  case userID
+  of 1: return "Tokyo"
+  of 2: return "New York"
+  else: return "Unknown"
+
+doAssert getUserCity("Damien", "Lundi") == "Tokyo"
+doAssert getUserCity(2) == "New York" # -- Errata here: missing closing "
+
+import sequtils
+let numbers = @[1, 2, 3, 4, 5, 6]
+let odd = filter(numbers, proc (x: int): bool = x mod 2 != 0)
+doAssert odd == @[1, 3, 5]
+
+import sequtils, future
+let numbers1 = @[1, 2, 3, 4, 5, 6]
+let odd1 = filter(numbers1, (x: int) -> bool => x mod 2 != 0)
+assert odd1 == @[1, 3, 5]
+
+proc isValid(x: int, validator: proc (x: int): bool) =
+  if validator(x): echo(x, " is valid")
+  else: echo(x, " is NOT valid")
+
+import future
+proc isValid2(x: int, validator: (x: int) -> bool) =
+  if validator(x): echo(x, " is valid")
+  else: echo(x, " is NOT valid")
+
+var list: array[3, int]
+list[0] = 1
+list[1] = 42
+assert list[0] == 1
+assert list[1] == 42
+assert list[2] == 0 #<1>
+
+echo list.repr #<2>
+
+# echo list[500]
+
+var list2: array[-10 .. -9, int]
+list2[-10] = 1
+list2[-9] = 2
+
+var list3 = ["Hi", "There"]
+
+var list4 = ["My", "name", "is", "Dominik"]
+for item in list4:
+  echo(item)
+
+for i in list4.low .. list4.high:
+  echo(list4[i])
+
+var list5: seq[int] = @[]
+doAssertRaises(IndexError):
+  list5[0] = 1
+
+list5.add(1)
+
+assert list5[0] == 1
+doAssertRaises(IndexError):
+  echo list5[42]
+
+# -- Errata: var list: seq[int]; echo(list[0]). This now creates an exception,
+# --         not a SIGSEGV.
+
+block:
+  var list = newSeq[string](3)
+  assert list[0] == nil
+  list[0] = "Foo"
+  list[1] = "Bar"
+  list[2] = "Baz"
+
+  list.add("Lorem")
+
+block:
+  let list = @[4, 8, 15, 16, 23, 42]
+  for i in 0 .. <list.len:
+    stdout.write($list[i] & " ")
+
+var collection: set[int16]
+doAssert collection == {}
+
+block:
+  let collection = {'a', 'x', 'r'}
+  doAssert 'a' in collection
+
+block:
+  let collection = {'a', 'T', 'z'}
+  let isAllLowerCase = {'A' .. 'Z'} * collection == {}
+  doAssert(not isAllLowerCase)
+
+let age = 10
+if age > 0 and age <= 10:
+  echo("You're still a child")
+elif age > 10 and age < 18:
+  echo("You're a teenager")
+else:
+  echo("You're an adult")
+
+let variable = "Arthur"
+case variable
+of "Arthur", "Zaphod", "Ford":
+  echo("Male")
+of "Marvin":
+  echo("Robot")
+of "Trillian":
+  echo("Female")
+else:
+  echo("Unknown")
+
+let ageDesc = if age < 18: "Non-Adult" else: "Adult"
+
+block:
+  var i = 0
+  while i < 3:
+    echo(i)
+    i.inc
+
+block label:
+  var i = 0
+  while true:
+    while i < 5:
+      if i > 3: break label
+      i.inc
+
+iterator values(): int =
+  var i = 0
+  while i < 5:
+    yield i
+    i.inc
+
+for value in values():
+  echo(value)
+
+import os
+for filename in walkFiles("*.nim"):
+  echo(filename)
+
+for item in @[1, 2, 3]:
+  echo(item)
+
+for i, value in @[1, 2, 3]: echo("Value at ", i, ": ", value)
+
+doAssertRaises(IOError):
+  proc second() =
+    raise newException(IOError, "Somebody set us up the bomb")
+
+  proc first() =
+    second()
+
+  first()
+
+block:
+  proc second() =
+    raise newException(IOError, "Somebody set us up the bomb")
+
+  proc first() =
+    try:
+      second()
+    except:
+      echo("Cannot perform second action because: " &
+        getCurrentExceptionMsg())
+
+  first()
+
+block:
+  type
+    Person = object
+      name: string
+      age: int
+
+  var person: Person
+  var person1 = Person(name: "Neo", age: 28)
+
+block:
+  type
+    PersonObj = object
+      name: string
+      age: int
+    PersonRef = ref PersonObj
+
+  # proc setName(person: PersonObj) =
+  #   person.name = "George"
+
+  proc setName(person: PersonRef) =
+    person.name = "George"
+
+block:
+  type
+    Dog = object
+      name: string
+
+    Cat = object
+      name: string
+
+  let dog: Dog = Dog(name: "Fluffy")
+  let cat: Cat = Cat(name: "Fluffy")
+
+block:
+  type
+    Dog = tuple
+      name: string
+
+    Cat = tuple
+      name: string
+
+  let dog: Dog = (name: "Fluffy")
+  let cat: Cat = (name: "Fluffy")
+
+  echo(dog == cat)
+
+block:
+  type
+    Point = tuple[x, y: int]
+    Point2 = (int, int)
+
+  let pos: Point = (x: 100, y: 50)
+  doAssert pos == (100, 50)
+
+  let pos1: Point = (x: 100, y: 50)
+  let (x, y) = pos1 #<1>
+  let (left, _) = pos1
+  doAssert x == pos1[0]
+  doAssert y == pos1[1]
+  doAssert left == x
+
+block:
+  type
+    Color = enum
+      colRed,
+      colGreen,
+      colBlue
+
+  let color: Color = colRed
+
+block:
+  type
+    Color {.pure.} = enum
+      red, green, blue
+
+  let color = Color.red
\ No newline at end of file
diff --git a/tests/niminaction/Chapter3/various3.nim b/tests/niminaction/Chapter3/various3.nim
new file mode 100644
index 000000000..478229b00
--- /dev/null
+++ b/tests/niminaction/Chapter3/various3.nim
@@ -0,0 +1,93 @@
+import threadpool
+proc foo: string = "Dog"
+var x: FlowVar[string] = spawn foo()
+assert(^x == "Dog")
+
+block:
+  type
+    Box = object
+      case empty: bool
+      of false:
+        contents: string
+      else:
+        discard
+
+  var obj = Box(empty: false, contents: "Hello")
+  assert obj.contents == "Hello"
+
+  var obj2 = Box(empty: true)
+  doAssertRaises(FieldError):
+    echo(obj2.contents)
+
+import json
+assert parseJson("null").kind == JNull
+assert parseJson("true").kind == JBool
+assert parseJson("42").kind == JInt
+assert parseJson("3.14").kind == JFloat
+assert parseJson("\"Hi\"").kind == JString
+assert parseJson("""{ "key": "value" }""").kind == JObject
+assert parseJson("[1, 2, 3, 4]").kind == JArray
+
+import json
+let data = """
+  {"username": "Dominik"}
+"""
+
+let obj = parseJson(data) 
+assert obj.kind == JObject 
+assert obj["username"].kind == JString 
+assert obj["username"].str == "Dominik"
+
+block:
+  proc count10(): int =
+    for i in 0 .. <10:
+      result.inc
+  assert count10() == 10
+
+type
+  Point = tuple[x, y: int]
+
+var point = (5, 10)
+var point2 = (x: 5, y: 10)
+
+type
+  Human = object
+    name: string
+    age: int
+
+var jeff = Human(name: "Jeff", age: 23)
+var amy = Human(name: "Amy", age: 20)
+
+import asyncdispatch
+
+var future = newFuture[int]() 
+doAssert(not future.finished) 
+
+future.callback = 
+  proc (future: Future[int]) = 
+    echo("Future is no longer empty, ", future.read) 
+
+future.complete(42)
+
+import asyncdispatch, asyncfile
+
+when false:
+  var file = openAsync("")
+  let dataFut = file.readAll()
+  dataFut.callback =
+    proc (future: Future[string]) =
+      echo(future.read())
+
+  asyncdispatch.runForever()
+
+import asyncdispatch, asyncfile, os
+
+proc readFiles() {.async.} =
+  # --- Changed to getTempDir here.
+  var file = openAsync(getTempDir() / "test.txt", fmReadWrite)
+  let data = await file.readAll() 
+  echo(data) 
+  await file.write("Hello!\n") 
+
+waitFor readFiles()
+
diff --git a/tests/niminaction/Chapter3/various3.nim.cfg b/tests/niminaction/Chapter3/various3.nim.cfg
new file mode 100644
index 000000000..6c1ded992
--- /dev/null
+++ b/tests/niminaction/Chapter3/various3.nim.cfg
@@ -0,0 +1 @@
+threads:on
\ No newline at end of file
diff --git a/tests/overflw/toverflw.nim b/tests/overflw/toverflw.nim
index 771a43303..20bc56a53 100644
--- a/tests/overflw/toverflw.nim
+++ b/tests/overflw/toverflw.nim
@@ -1,21 +1,84 @@
 discard """
   file: "toverflw.nim"
-  output: "the computation overflowed"
+  output: "ok"
+  cmd: "nim $target -d:release $options $file"
+
 """
 # Tests nim's ability to detect overflows
 
 {.push overflowChecks: on.}
 
 var
-  a, b: int
-a = high(int)
-b = -2
+  a = high(int)
+  b = -2
+  overflowDetected = false
+
 try:
   writeLine(stdout, b - a)
 except OverflowError:
-  writeLine(stdout, "the computation overflowed")
+  overflowDetected = true
 
 {.pop.} # overflow check
-#OUT the computation overflowed
+
+doAssert(overflowDetected)
+
+block: # Overflow checks in a proc
+  var
+    a = high(int)
+    b = -2
+    overflowDetected = false
+
+  {.push overflowChecks: on.}
+  proc foo() =
+    let c = b - a
+  {.pop.}
+
+  try:
+    foo()
+  except OverflowError:
+    overflowDetected = true
+
+  doAssert(overflowDetected)
+
+block: # Overflow checks in a forward declared proc
+  var
+    a = high(int)
+    b = -2
+    overflowDetected = false
+
+  proc foo()
+
+  {.push overflowChecks: on.}
+  proc foo() =
+    let c = b - a
+  {.pop.}
+
+  try:
+    foo()
+  except OverflowError:
+    overflowDetected = true
+
+  doAssert(overflowDetected)
+
+block: # Overflow checks doesn't affect fwd declaration
+  var
+    a = high(int)
+    b = -2
+    overflowDetected = false
+
+  {.push overflowChecks: on.}
+  proc foo()
+  {.pop.}
+
+  proc foo() =
+    let c = b - a
+
+  try:
+    foo()
+  except OverflowError:
+    overflowDetected = true
+
+  doAssert(not overflowDetected)
 
 
+echo "ok"
diff --git a/tests/parser/ttypeclasses.nim b/tests/parser/ttypeclasses.nim
index 9f487c7a8..46fd20686 100644
--- a/tests/parser/ttypeclasses.nim
+++ b/tests/parser/ttypeclasses.nim
@@ -1,17 +1,42 @@
-discard """
-    action: run
-"""
-
 type
     R = ref
     V = var
     D = distinct
     P = ptr
+    T = type
+    S = static
+    OBJ = object
+    TPL = tuple
+    SEQ = seq
 
+var i: int
 var x: ref int
 var y: distinct int
 var z: ptr int
+const C = @[1, 2, 3]
+
+static:
+  assert x is ref
+  assert y is distinct
+  assert z is ptr
+  assert C is static
+  assert C[1] is static[int]
+  assert C[0] is static[SomeInteger]
+  assert C isnot static[string]
+  assert C is SEQ|OBJ
+  assert C isnot OBJ|TPL
+  assert int is int
+  assert int is T
+  assert int is SomeInteger
+  assert seq[int] is type
+  assert seq[int] is type[seq]
+  assert seq[int] isnot type[seq[float]]
+  assert i isnot type[int]
+  assert type(i) is type[int]
+  assert x isnot T
+  assert y isnot S
+  assert z isnot enum
+  assert x isnot object
+  assert y isnot tuple
+  assert z isnot seq
 
-doAssert x is ref
-doAssert y is distinct
-doAssert z is ptr
\ No newline at end of file
diff --git a/tests/parser/ttypemodifiers.nim b/tests/parser/ttypemodifiers.nim
new file mode 100644
index 000000000..2c322b44b
--- /dev/null
+++ b/tests/parser/ttypemodifiers.nim
@@ -0,0 +1,527 @@
+discard """
+nimout: '''
+StmtList
+  TypeSection
+    TypeDef
+      Ident "BarePtr"
+      Empty
+      PtrTy
+    TypeDef
+      Ident "GenericPtr"
+      Empty
+      PtrTy
+        Bracket
+          Ident "int"
+    TypeDef
+      Ident "PrefixPtr"
+      Empty
+      PtrTy
+        Ident "int"
+    TypeDef
+      Ident "PtrTuple"
+      Empty
+      PtrTy
+        Par
+          Ident "int"
+          Ident "string"
+    TypeDef
+      Ident "BareRef"
+      Empty
+      RefTy
+    TypeDef
+      Ident "GenericRef"
+      Empty
+      RefTy
+        Bracket
+          Ident "int"
+    TypeDef
+      Ident "RefTupleCl"
+      Empty
+      RefTy
+        TupleTy
+    TypeDef
+      Ident "RefTupleType"
+      Empty
+      RefTy
+        Par
+          Ident "int"
+          Ident "string"
+    TypeDef
+      Ident "RefTupleVars"
+      Empty
+      RefTy
+        Par
+          Ident "a"
+          Ident "b"
+    TypeDef
+      Ident "BareStatic"
+      Empty
+      Ident "static"
+    TypeDef
+      Ident "GenericStatic"
+      Empty
+      BracketExpr
+        Ident "static"
+        Ident "int"
+    TypeDef
+      Ident "PrefixStatic"
+      Empty
+      Command
+        Ident "static"
+        Ident "int"
+    TypeDef
+      Ident "StaticTupleCl"
+      Empty
+      Command
+        Ident "static"
+        TupleClassTy
+    TypeDef
+      Ident "StaticTuple"
+      Empty
+      Command
+        Ident "static"
+        Par
+          Ident "int"
+          Ident "string"
+    TypeDef
+      Ident "BareType"
+      Empty
+      Ident "type"
+    TypeDef
+      Ident "GenericType"
+      Empty
+      BracketExpr
+        Ident "type"
+        Ident "float"
+    TypeDef
+      Ident "TypeTupleGen"
+      Empty
+      BracketExpr
+        Ident "type"
+        TupleClassTy
+    TypeDef
+      Ident "TypeTupleCl"
+      Empty
+      Command
+        Ident "type"
+        TupleClassTy
+    TypeDef
+      Ident "TypeInstance"
+      Empty
+      Command
+        Ident "type"
+        BracketExpr
+          Ident "Foo"
+          RefTy
+    TypeDef
+      Ident "bareTypeDesc"
+      Empty
+      Ident "typedesc"
+    TypeDef
+      Ident "TypeOfVar"
+      Empty
+      Call
+        Ident "type"
+        Ident "a"
+    TypeDef
+      Ident "TypeOfVarAlt"
+      Empty
+      Command
+        Ident "type"
+        Par
+          Ident "a"
+    TypeDef
+      Ident "TypeOfTuple1"
+      Empty
+      Call
+        Ident "type"
+        Ident "a"
+    TypeDef
+      Ident "TypeOfTuple2"
+      Empty
+      Call
+        Ident "type"
+        Ident "a"
+        Ident "b"
+    TypeDef
+      Ident "TypeOfTuple1A"
+      Empty
+      Command
+        Ident "type"
+        TupleConstr
+          Ident "a"
+    TypeDef
+      Ident "TypeOfTuple2A"
+      Empty
+      Command
+        Ident "type"
+        Par
+          Ident "a"
+          Ident "b"
+    TypeDef
+      Ident "TypeTuple"
+      Empty
+      Command
+        Ident "type"
+        Par
+          Ident "int"
+          Ident "string"
+    TypeDef
+      Ident "GenericTypedesc"
+      Empty
+      BracketExpr
+        Ident "typedesc"
+        Ident "int"
+    TypeDef
+      Ident "T"
+      Empty
+      Ident "type"
+  ProcDef
+    Ident "foo"
+    Empty
+    Empty
+    FormalParams
+      Ident "type"
+      IdentDefs
+        Ident "bareType"
+        Ident "type"
+        Empty
+      IdentDefs
+        Ident "genType"
+        BracketExpr
+          Ident "type"
+          Ident "int"
+        Empty
+      IdentDefs
+        Ident "typeInt"
+        Command
+          Ident "type"
+          Ident "int"
+        Empty
+      IdentDefs
+        Ident "typeIntAlt"
+        Call
+          Ident "type"
+          Ident "int"
+        Empty
+      IdentDefs
+        Ident "typeOfVar"
+        Call
+          Ident "type"
+          Ident "a"
+        Empty
+      IdentDefs
+        Ident "typeDotType"
+        DotExpr
+          Ident "foo"
+          Ident "type"
+        Empty
+      IdentDefs
+        Ident "typeTupleCl"
+        Command
+          Ident "type"
+          TupleClassTy
+        Empty
+      IdentDefs
+        Ident "bareStatic"
+        Ident "static"
+        Empty
+      IdentDefs
+        Ident "genStatic"
+        BracketExpr
+          Ident "static"
+          Ident "int"
+        Empty
+      IdentDefs
+        Ident "staticInt"
+        Command
+          Ident "static"
+          Ident "int"
+        Empty
+      IdentDefs
+        Ident "staticVal1"
+        Command
+          Ident "static"
+          IntLit 10
+        Empty
+      IdentDefs
+        Ident "staticVal2"
+        Call
+          Ident "static"
+          StrLit "str"
+        Empty
+      IdentDefs
+        Ident "staticVal3"
+        Command
+          Ident "static"
+          StrLit "str"
+        Empty
+      IdentDefs
+        Ident "staticVal4"
+        CallStrLit
+          Ident "static"
+          RStrLit "str"
+        Empty
+      IdentDefs
+        Ident "staticDotVal"
+        DotExpr
+          IntLit 10
+          Ident "static"
+        Empty
+      IdentDefs
+        Ident "bareRef"
+        RefTy
+        Empty
+      IdentDefs
+        Ident "refTuple1"
+        RefTy
+          Par
+            Ident "int"
+        Empty
+      IdentDefs
+        Ident "refTuple1A"
+        RefTy
+          TupleConstr
+            Ident "int"
+        Empty
+      IdentDefs
+        Ident "refTuple2"
+        RefTy
+          Par
+            Ident "int"
+            Ident "string"
+        Empty
+      IdentDefs
+        Ident "genRef"
+        RefTy
+          Bracket
+            Ident "int"
+        Empty
+      IdentDefs
+        Ident "refInt"
+        RefTy
+          Ident "int"
+        Empty
+      IdentDefs
+        Ident "refCall"
+        RefTy
+          Par
+            Ident "a"
+        Empty
+      IdentDefs
+        Ident "macroCall1"
+        Command
+          Ident "foo"
+          Ident "bar"
+        Empty
+      IdentDefs
+        Ident "macroCall2"
+        Call
+          Ident "foo"
+          Ident "bar"
+        Empty
+      IdentDefs
+        Ident "macroCall3"
+        Call
+          DotExpr
+            Ident "foo"
+            Ident "bar"
+          Ident "baz"
+        Empty
+      IdentDefs
+        Ident "macroCall4"
+        Call
+          BracketExpr
+            Ident "foo"
+            Ident "bar"
+          Ident "baz"
+        Empty
+      IdentDefs
+        Ident "macroCall5"
+        Command
+          Ident "foo"
+          Command
+            Ident "bar"
+            Ident "baz"
+        IntLit 10
+    Empty
+    Empty
+    StmtList
+      Asgn
+        Ident "staticTen"
+        Command
+          Ident "static"
+          IntLit 10
+      Asgn
+        Ident "staticA"
+        Call
+          Ident "static"
+          Ident "a"
+      Asgn
+        Ident "staticCall"
+        Command
+          Ident "static"
+          Call
+            Ident "foo"
+            IntLit 1
+      Asgn
+        Ident "staticStrCall"
+        Command
+          Ident "static"
+          CallStrLit
+            Ident "foo"
+            RStrLit "x"
+      Asgn
+        Ident "staticChainCall"
+        Command
+          Ident "static"
+          Command
+            Ident "foo"
+            Ident "bar"
+      Asgn
+        Ident "typeTen"
+        Command
+          Ident "type"
+          IntLit 10
+      Asgn
+        Ident "typeA"
+        Call
+          Ident "type"
+          Ident "a"
+      Asgn
+        Ident "typeCall"
+        Command
+          Ident "type"
+          Call
+            Ident "foo"
+            IntLit 1
+      Asgn
+        Ident "typeStrCall"
+        Command
+          Ident "type"
+          CallStrLit
+            Ident "foo"
+            RStrLit "x"
+      Asgn
+        Ident "typeChainCall"
+        Command
+          Ident "type"
+          Command
+            Ident "foo"
+            Ident "bar"
+      Asgn
+        Ident "normalChainCall"
+        Command
+          Ident "foo"
+          Command
+            Ident "bar"
+            Ident "baz"
+      Asgn
+        Ident "normalTupleCall2"
+        Call
+          Ident "foo"
+          Ident "a"
+          Ident "b"
+      StaticStmt
+        StmtList
+          Ident "singleStaticStmt"
+      StaticStmt
+        StmtList
+          Ident "staticStmtList1"
+          Ident "staticStmtList2"
+'''
+"""
+
+import macros
+
+dumpTree:
+  type
+    BarePtr       = ptr
+    GenericPtr    = ptr[int]
+    PrefixPtr     = ptr int
+    PtrTuple      = ptr (int, string)
+    BareRef       = ref
+    GenericRef    = ref[int]
+    RefTupleCl    = ref tuple
+    RefTupleType  = ref (int, string)
+    RefTupleVars  = ref (a, b)
+    BareStatic    = static                # Used to be Error: invalid indentation
+    GenericStatic = static[int]
+    PrefixStatic  = static int
+    StaticTupleCl = static tuple
+    StaticTuple   = static (int, string)
+    BareType      = type
+    GenericType   = type[float]
+    TypeTupleGen  = type[tuple]
+    TypeTupleCl   = type tuple            # Used to be Error: invalid indentation
+    TypeInstance  = type Foo[ref]
+    bareTypeDesc  = typedesc
+    TypeOfVar     = type(a)
+    TypeOfVarAlt  = type (a)              # Used to be Error: invalid indentation
+    TypeOfTuple1  = type(a,)
+    TypeOfTuple2  = type(a,b)
+    TypeOfTuple1A = type (a,)             # Used to be Error: invalid indentation
+    TypeOfTuple2A = type (a,b)            # Used to be Error: invalid indentation
+    TypeTuple     = type (int, string)    # Used to be Error: invalid indentation
+    GenericTypedesc = typedesc[int]
+    T = type
+
+  proc foo(
+    bareType        : type,
+    genType         : type[int],
+    typeInt         : type int,
+    typeIntAlt      : type(int),
+    typeOfVar       : type(a),
+    typeDotType     : foo.type,
+    typeTupleCl     : type tuple,         # Used to be Error: ')' expected
+    bareStatic      : static,             # Used to be Error: expression expected, but found ','
+    genStatic       : static[int],
+    staticInt       : static int,
+    staticVal1      : static 10,
+    staticVal2      : static("str"),
+    staticVal3      : static "str",
+    staticVal4      : static"str",        # Used to be Error: expression expected, but found 'str'
+    staticDotVal    : 10.static,
+    bareRef         : ref,
+    refTuple1       : ref (int),
+    refTuple1A      : ref (int,),
+    refTuple2       : ref (int,string),
+    genRef          : ref[int],
+    refInt          : ref int,
+    refCall         : ref(a),
+    macroCall1      : foo bar,
+    macroCall2      : foo(bar),
+    macroCall3      : foo.bar(baz),
+    macroCall4      : foo[bar](baz),
+    macroCall5      : foo bar baz = 10
+  ): type =
+    staticTen       = static 10
+    staticA         = static(a)
+    # staticAspace    = static (a)          # With newTypedesc: Error: invalid indentation
+    # staticAtuple    = static (a,)         # With newTypedesc: Error: invalid indentation
+    # staticTuple     = static (a,b)        # With newTypedesc: Error: invalid indentation
+    # staticTypeTuple = static (int,string) # With newTypedesc: Error: invalid indentation
+    staticCall      = static foo(1)
+    staticStrCall   = static foo"x"
+    staticChainCall = static foo bar
+
+    typeTen         = type 10
+    typeA           = type(a)
+    # typeAspace    = type (a)            # Error: invalid indentation
+    # typeAtuple    = type (a,)           # Error: invalid indentation
+    # typeTuple     = type (a,b)          # Error: invalid indentation
+    # typeTypeTuple = type (int,string)   # Error: invalid indentation
+    typeCall        = type foo(1)
+    typeStrCall     = type foo"x"
+    typeChainCall   = type foo bar
+
+    normalChainCall = foo bar baz
+    # normalTupleCall1 = foo(a,)          # Error: invalid indentation
+    normalTupleCall2 = foo(a,b)
+    # normalTupleCall3 = foo (a,b)        # Error: invalid indentation
+
+    static: singleStaticStmt
+    static:
+      staticStmtList1
+      staticStmtList2
+
diff --git a/tests/pragmas/treorder.nim b/tests/pragmas/treorder.nim
index 6a6bbff4d..1006af527 100644
--- a/tests/pragmas/treorder.nim
+++ b/tests/pragmas/treorder.nim
@@ -71,5 +71,4 @@ macro make(arg: untyped): untyped =
 proc first(i: int): void =
   make(second)
 
-static:
-  var ss: string = ""
\ No newline at end of file
+var ss {.compileTime.}: string = ""
\ No newline at end of file
diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim
new file mode 100644
index 000000000..789bd7588
--- /dev/null
+++ b/tests/statictypes/tstatictypes.nim
@@ -0,0 +1,109 @@
+discard """
+nimout: '''
+staticAlialProc instantiated with 358
+staticAlialProc instantiated with 368
+'''
+output: '''
+16
+16
+b is 2 times a
+17
+'''
+"""
+
+import macros
+
+template ok(x) = assert(x)
+template no(x) = assert(not x)
+
+template accept(x) =
+  static: assert(compiles(x))
+
+template reject(x) =
+  static: assert(not compiles(x))
+
+proc plus(a, b: int): int = a + b
+
+template isStatic(x: static): bool = true
+template isStatic(x: auto): bool = false
+
+var v = 1
+
+when true:
+  # test that `isStatic` works as expected
+  const C = 2
+
+  static:
+    ok C.isStatic
+    ok isStatic(plus(1, 2))
+    ok plus(C, 2).isStatic
+
+    no isStatic(v)
+    no plus(1, v).isStatic
+
+when true:
+  # test that proc instantiation works as expected
+  type
+    StaticTypeAlias = static[int]
+
+  proc staticAliasProc(a: StaticTypeAlias,
+                       b: static[int],
+                       c: static int) =
+    static:
+      assert a.isStatic and b.isStatic and c.isStatic
+      assert isStatic(a + plus(b, c))
+      echo "staticAlialProc instantiated with ", a, b, c
+
+    when b mod a == 0:
+      echo "b is ", b div a, " times a"
+
+    echo a + b + c
+
+  staticAliasProc 1+2, 5, 8
+  staticAliasProc 3, 2+3, 9-1
+  staticAliasProc 3, 3+3, 4+4
+
+when true:
+  # test static coercions. normal cases that should work:
+  accept:
+    var s1 = static[int] plus(1, 2)
+    var s2 = static(plus(1,2))
+    var s3 = static plus(1,2)
+    var s4 = static[SomeInteger](1 + 2)
+
+  # the sub-script operator can be used only with types:
+  reject:
+    var just_static3 = static[plus(1,2)]
+
+  # static coercion takes into account the type:
+  reject:
+    var x = static[string](plus(1, 2))
+  reject:
+    var x = static[string] plus(1, 2)
+  reject:
+    var x = static[SomeFloat] plus(3, 4)
+
+  # you cannot coerce a run-time variable
+  reject:
+    var x = static(v)
+
+when true:
+  type
+    ArrayWrapper1[S: static int] = object
+      data: array[S + 1, int]
+
+    ArrayWrapper2[S: static[int]] = object
+      data: array[S.plus(2), int]
+
+    ArrayWrapper3[S: static[(int, string)]] = object
+      data: array[S[0], int]
+
+  var aw1: ArrayWrapper1[5]
+  var aw2: ArrayWrapper2[5]
+  var aw3: ArrayWrapper3[(10, "str")]
+
+  static:
+    assert aw1.data.high == 5
+    assert aw2.data.high == 6
+    assert aw3.data.high == 9
+
diff --git a/tests/stdlib/tmemfiles2.nim b/tests/stdlib/tmemfiles2.nim
index 7ea94cffc..d6cfa533a 100644
--- a/tests/stdlib/tmemfiles2.nim
+++ b/tests/stdlib/tmemfiles2.nim
@@ -4,9 +4,10 @@ discard """
 Half read size: 10 Data: Hello'''
 """
 import memfiles, os
+const
+  fn = "test.mmap"
 var
   mm, mm_full, mm_half: MemFile
-  fn = "test.mmap"
   p: pointer
 
 if fileExists(fn): removeFile(fn)
diff --git a/tests/stdlib/tmemmapstreams.nim b/tests/stdlib/tmemmapstreams.nim
new file mode 100644
index 000000000..243574f1a
--- /dev/null
+++ b/tests/stdlib/tmemmapstreams.nim
@@ -0,0 +1,53 @@
+discard """
+  file: "tmemmapstreams.nim"
+  output: '''Created size: 10
+Position after writing: 5
+Position after writing one char: 6
+Peeked data: Hello
+Position after peeking: 0
+Readed data: Hello!
+Position after reading line: 7
+Position after setting position: 6
+Readed line: Hello!
+Position after reading line: 7'''
+"""
+import os, streams, memfiles
+const
+  fn = "test.mmapstream"
+var
+  mms: MemMapFileStream
+
+if fileExists(fn): removeFile(fn)
+
+# Create a new memory mapped file, data all zeros
+mms = newMemMapFileStream(fn, mode = fmReadWrite, fileSize = 10)
+mms.close()
+if fileExists(fn): echo "Created size: ", getFileSize(fn)
+
+# write, flush, peek, read
+mms = newMemMapFileStream(fn, mode = fmReadWrite)
+let s = "Hello"
+
+mms.write(s)
+mms.flush
+echo "Position after writing: ", mms.getPosition()
+mms.write('!')
+mms.flush
+echo "Position after writing one char: ", mms.getPosition()
+mms.close()
+
+mms = newMemMapFileStream(fn, mode = fmRead)
+echo "Peeked data: ", mms.peekStr(s.len)
+echo "Position after peeking: ", mms.getPosition()
+echo "Readed data: ", mms.readLine
+echo "Position after reading line: ", mms.getPosition()
+mms.setPosition(mms.getPosition() - 1)
+echo "Position after setting position: ", mms.getPosition()
+
+mms.setPosition(0)
+echo "Readed line: ", mms.readLine
+echo "Position after reading line: ", mms.getPosition()
+
+mms.close()
+
+if fileExists(fn): removeFile(fn)
diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim
new file mode 100644
index 000000000..e5b709a66
--- /dev/null
+++ b/tests/stdlib/tpegs.nim
@@ -0,0 +1,78 @@
+discard """
+  output: '''
+pkNonTerminal: Sum @(2, 3)
+  pkSequence: (Product (('+' / '-') Product)*)
+    pkNonTerminal: Product @(3, 7)
+      pkSequence: (Value (('*' / '/') Value)*)
+        pkNonTerminal: Value @(4, 5)
+          pkOrderedChoice: (([0-9] [0-9]*) / ('(' Expr ')'))
+            pkSequence: ([0-9] [0-9]*)
+              pkCharChoice: [0-9]
+              pkGreedyRepSet: [0-9]*
+            pkSequence: ('(' Expr ')')
+              pkChar: '('
+              pkNonTerminal: Expr @(1, 4)
+                pkNonTerminal: Sum @(2, 3)
+              pkChar: ')'
+        pkGreedyRep: (('*' / '/') Value)*
+          pkSequence: (('*' / '/') Value)
+            pkOrderedChoice: ('*' / '/')
+              pkChar: '*'
+              pkChar: '/'
+            pkNonTerminal: Value @(4, 5)
+    pkGreedyRep: (('+' / '-') Product)*
+      pkSequence: (('+' / '-') Product)
+        pkOrderedChoice: ('+' / '-')
+          pkChar: '+'
+          pkChar: '-'
+        pkNonTerminal: Product @(3, 7)
+'''
+"""
+
+import strutils, streams
+import pegs
+
+const
+  indent = "  "
+
+let
+  pegSrc = """
+Expr <- Sum
+Sum <- Product (('+' / '-') Product)*
+Product <- Value (('*' / '/') Value)*
+Value <- [0-9]+ / '(' Expr ')'
+  """
+  pegAst: Peg = pegSrc.peg
+
+var
+  outp = newStringStream()
+  processed: seq[string] = @[]
+
+proc prt(outp: Stream, kind: PegKind, s: string; level: int = 0) =
+  outp.writeLine indent.repeat(level) & "$1: $2" % [$kind, s]
+
+proc recLoop(p: Peg, level: int = 0) =
+  case p.kind
+  of pkEmpty..pkWhitespace:
+    discard
+  of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle:
+    outp.prt(p.kind, $p, level)
+  of pkChar, pkGreedyRepChar:
+    outp.prt(p.kind, $p, level)
+  of pkCharChoice, pkGreedyRepSet:
+    outp.prt(p.kind, $p, level)
+  of pkNonTerminal:
+    outp.prt(p.kind,
+      "$1 @($3, $4)" % [p.nt.name, $p.nt.rule.kind, $p.nt.line, $p.nt.col], level)
+    if not(p.nt.name in processed):
+      processed.add p.nt.name
+      p.nt.rule.recLoop level+1
+  of pkBackRef..pkBackRefIgnoreStyle:
+    outp.prt(p.kind, $p, level)
+  else:
+    outp.prt(p.kind, $p, level)
+    for s in items(p):
+      s.recLoop level+1
+
+pegAst.recLoop
+echo outp.data
\ No newline at end of file
diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim
index 6f78a91ac..4d4081d39 100644
--- a/tests/stdlib/tstrutil.nim
+++ b/tests/stdlib/tstrutil.nim
@@ -7,6 +7,14 @@ discard """
 import
   strutils
 
+import macros
+
+template rejectParse(e) =
+  try:
+    discard e
+    raise newException(AssertionError, "This was supposed to fail: $#!" % astToStr(e))
+  except ValueError: discard
+
 proc testStrip() =
   write(stdout, strip("  ha  "))
 
@@ -148,7 +156,6 @@ proc testDelete =
   delete(s, 0, 0)
   assert s == "1236789ABCDEFG"
 
-
 proc testIsAlphaNumeric =
   assert isAlphaNumeric("abcdABC1234") == true
   assert isAlphaNumeric("a") == true
@@ -203,10 +210,50 @@ proc testCountLines =
   assertCountLines("\nabc\n123")
   assertCountLines("\nabc\n123\n")
 
+proc testParseInts =
+  # binary
+  assert "0b1111".parseBinInt == 15
+  assert "0B1111".parseBinInt == 15
+  assert "1111".parseBinInt == 15
+  assert "1110".parseBinInt == 14
+  assert "1_1_1_1".parseBinInt == 15
+  assert "0b1_1_1_1".parseBinInt == 15
+  rejectParse "".parseBinInt
+  rejectParse "_".parseBinInt
+  rejectParse "0b".parseBinInt
+  rejectParse "0b1234".parseBinInt
+  # hex
+  assert "0x72".parseHexInt == 114
+  assert "0X72".parseHexInt == 114
+  assert "#72".parseHexInt == 114
+  assert "72".parseHexInt == 114
+  assert "FF".parseHexInt == 255
+  assert "ff".parseHexInt == 255
+  assert "fF".parseHexInt == 255  
+  assert "0x7_2".parseHexInt == 114
+  rejectParse "".parseHexInt
+  rejectParse "_".parseHexInt
+  rejectParse "0x".parseHexInt
+  rejectParse "0xFFG".parseHexInt
+  rejectParse "reject".parseHexInt
+  # octal
+  assert "0o17".parseOctInt == 15
+  assert "0O17".parseOctInt == 15
+  assert "17".parseOctInt == 15
+  assert "10".parseOctInt == 8
+  assert "0o1_0_0".parseOctInt == 64
+  rejectParse "".parseOctInt
+  rejectParse "_".parseOctInt
+  rejectParse "0o".parseOctInt
+  rejectParse "9".parseOctInt
+  rejectParse "0o9".parseOctInt
+  rejectParse "reject".parseOctInt
+
 testDelete()
 testFind()
 testRFind()
 testCountLines()
+testParseInts()
 
 assert(insertSep($1000_000) == "1_000_000")
 assert(insertSep($232) == "232")
diff --git a/tests/template/tdefined_overload.nim b/tests/template/tdefined_overload.nim
new file mode 100644
index 000000000..bcc83e414
--- /dev/null
+++ b/tests/template/tdefined_overload.nim
@@ -0,0 +1,46 @@
+discard """
+  output: "Valid and not defined"
+"""
+# test for issue #7997
+# checking for `when not defined` in a template for some compile time symbol
+# results in a compilation error of:
+# Error: obsolete usage of 'defined', use 'declared' instead
+# if the symbol is 'overloaded' by some variable or procedure, because in
+# that case the argument of `defined` is of kind `nkSym` instead of `nkIdent`
+# (for which was checked in `semexprs.semDefined`).
+
+block:
+  # check whether a proc with the same name as the argument to `defined`
+  # compiles
+  proc overloaded() =
+    discard
+
+  template definedCheck(): untyped =
+    when not defined(overloaded): true
+    else: false
+  doAssert definedCheck == true
+
+block:
+  # check whether a variable with the same name as the argument to `defined`
+  # compiles
+  var overloaded: int
+
+  template definedCheck(): untyped =
+    when not defined(overloaded): true
+    else: false
+  doAssert definedCheck == true
+
+block:
+  # check whether a non overloaded when check still works properly
+  when not defined(validIdentifier):
+    echo "Valid and not defined"
+
+block:
+  # now check that invalid identifiers cause a compilation error
+  # by using reject template.
+  template reject(b) =
+    static: doAssert(not compiles(b))
+
+  reject:
+    when defined(123):
+      echo "Invalid identifier! Will not be echoed"
diff --git a/tests/template/tpattern_with_converter.nim b/tests/template/tpattern_with_converter.nim
new file mode 100644
index 000000000..e0632552b
--- /dev/null
+++ b/tests/template/tpattern_with_converter.nim
@@ -0,0 +1,27 @@
+discard """
+  output: 10.0
+"""
+
+type
+  MyFloat = object
+    val: float
+
+converter to_myfloat*(x: float): MyFloat {.inline.} =
+  MyFloat(val: x)
+
+proc `+`(x1, x2: MyFloat): MyFloat =
+  MyFloat(val: x1.val + x2.val)
+
+proc `*`(x1, x2: MyFloat): MyFloat =
+    MyFloat(val: x1.val * x2.val)
+
+template optMul{`*`(a, 2.0)}(a: MyFloat): MyFloat =
+  a + a
+
+func floatMyFloat(x: MyFloat): MyFloat =
+  result = x * 2.0
+
+func floatDouble(x: float): float =
+  result = x * 2.0
+
+echo floatDouble(5)
\ No newline at end of file
diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim
index 84e536636..9affbc159 100644
--- a/tests/testament/categories.nim
+++ b/tests/testament/categories.nim
@@ -266,8 +266,17 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) =
     testSpec r, makeTest(filename, options, cat, actionCompile), targetCPP
 
   let tests = [
+    "niminaction/Chapter1/various1",
+    "niminaction/Chapter2/various2",
+    "niminaction/Chapter2/resultaccept",
+    "niminaction/Chapter2/resultreject",
+    "niminaction/Chapter2/explicit_discard",
+    "niminaction/Chapter2/no_def_eq",
+    "niminaction/Chapter2/no_iterator",
+    "niminaction/Chapter2/no_seq_type",
     "niminaction/Chapter3/ChatApp/src/server",
     "niminaction/Chapter3/ChatApp/src/client",
+    "niminaction/Chapter3/various3",
     "niminaction/Chapter6/WikipediaStats/concurrency_regex",
     "niminaction/Chapter6/WikipediaStats/concurrency",
     "niminaction/Chapter6/WikipediaStats/naive",
@@ -278,8 +287,34 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) =
     "niminaction/Chapter7/Tweeter/src/tweeter",
     "niminaction/Chapter7/Tweeter/src/createDatabase",
     "niminaction/Chapter7/Tweeter/tests/database_test",
-    "niminaction/Chapter8/sdl/sdl_test",
+    "niminaction/Chapter8/sdl/sdl_test"
     ]
+
+  # Verify that the files have not been modified. Death shall fall upon
+  # whoever edits these hashes without dom96's permission, j/k. But please only
+  # edit when making a conscious breaking change, also please try to make your
+  # commit message clear and notify me so I can easily compile an errata later.
+  var testHashes: seq[string] = @[]
+
+  for test in tests:
+    testHashes.add(getMD5(readFile("tests" / test.addFileExt("nim")).string))
+
+  const refHashes = @[
+    "51afdfa84b3ca3d810809d6c4e5037ba", "30f07e4cd5eaec981f67868d4e91cfcf",
+    "d14e7c032de36d219c9548066a97e846", "2e40bfd5daadb268268727da91bb4e81",
+    "c5d3853ed0aba04bf6d35ba28a98dca0", "058603145ff92d46c009006b06e5b228",
+    "7b94a029b94ddb7efafddd546c965ff6", "586d74514394e49f2370dfc01dd9e830",
+    "e1901837b757c9357dc8d259fd0ef0f6", "097670c7ae12e825debaf8ec3995227b",
+    "a8cb7b78cc78d28535ab467361db5d6e", "bfaec2816a1848991b530c1ad17a0184",
+    "47cb71bb4c1198d6d29cdbee05aa10b9", "87e4436809f9d73324cfc4f57f116770",
+    "7b7db5cddc8cf8fa9b6776eef1d0a31d", "e6e40219f0f2b877869b738737b7685e",
+    "6532ee87d819f2605a443d5e94f9422a", "9a8fe78c588d08018843b64b57409a02",
+    "03a801275b8b76b4170c870cd0da079d", "20bb7d3e2d38d43b0cb5fcff4909a4a8",
+    "af6844598f534fab6942abfa4dfe9ab2", "2a7a17f84f6503d9bc89a5ab8feea127"
+  ]
+  doAssert testHashes == refHashes, "Nim in Action tests were changed."
+
+  # Run the tests.
   for testfile in tests:
     test "tests/" & testfile & ".nim", actionCompile
 
@@ -291,6 +326,7 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) =
 
 
 
+
 # ------------------------- manyloc -------------------------------------------
 #proc runSpecialTests(r: var TResults, options: string) =
 #  for t in ["lib/packages/docutils/highlite"]:
diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim
index 0185156ec..0764b6363 100644
--- a/tests/testament/tester.nim
+++ b/tests/testament/tester.nim
@@ -129,7 +129,7 @@ proc callCCompiler(cmdTemplate, filename, options: string,
   let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
                        "options", options, "file", filename.quoteShell,
                        "filedir", filename.getFileDir()])
-  var p = startProcess(command="gcc", args=c[5.. ^1],
+  var p = startProcess(command="gcc", args=c[5 .. ^1],
                        options={poStdErrToStdOut, poUsePath})
   let outp = p.outputStream
   var x = newStringOfCap(120)
diff --git a/tests/types/t7905.nim b/tests/types/t7905.nim
new file mode 100644
index 000000000..ef75bb86c
--- /dev/null
+++ b/tests/types/t7905.nim
@@ -0,0 +1,33 @@
+discard """
+  output: '''
+(member: "hello world")
+(member: 123.456)
+(member: "hello world", x: ...)
+(member: 123.456, x: ...)
+'''
+"""
+
+template foobar(arg: typed): untyped =
+  type
+    MyType = object
+      member: type(arg)
+
+  var myVar: MyType
+  myVar.member = arg
+  echo myVar
+
+foobar("hello world")
+foobar(123.456'f64)
+
+template foobarRec(arg: typed): untyped =
+  type
+    MyType = object
+      member: type(arg)
+      x: ref MyType
+
+  var myVar: MyType
+  myVar.member = arg
+  echo myVar
+
+foobarRec("hello world")
+foobarRec(123.456'f64)
diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim
index 2f9dbf245..f05f443de 100644
--- a/tests/types/tisopr.nim
+++ b/tests/types/tisopr.nim
@@ -5,7 +5,7 @@ false
 false
 true
 true
-no'''
+yes'''
 """
 
 proc IsVoid[T](): string =
diff --git a/tests/vm/tnilref.nim b/tests/vm/tnilref.nim
new file mode 100644
index 000000000..5e27cf0cb
--- /dev/null
+++ b/tests/vm/tnilref.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "attempt to access a nil address"
+"""
+
+static:
+    var s: ref int
+    s[] = 1
\ No newline at end of file
diff --git a/tests/vm/tnimnode.nim b/tests/vm/tnimnode.nim
index 188bac9bf..4210ab41d 100644
--- a/tests/vm/tnimnode.nim
+++ b/tests/vm/tnimnode.nim
@@ -4,14 +4,12 @@ proc assertEq(arg0,arg1: string): void =
   if arg0 != arg1:
     raiseAssert("strings not equal:\n" & arg0 & "\n" & arg1)
 
-static:
-  # a simple assignment of stmtList to another variable
-  var node: NimNode
-  # an assignment of stmtList into an array
-  var nodeArray: array[1, NimNode]
-  # an assignment of stmtList into a seq
-  var nodeSeq = newSeq[NimNode](2)
-
+# a simple assignment of stmtList to another variable
+var node {.compileTime.}: NimNode
+# an assignment of stmtList into an array
+var nodeArray {.compileTime.}: array[1, NimNode]
+# an assignment of stmtList into a seq
+var nodeSeq {.compileTime.} = newSeq[NimNode](2)
 
 proc checkNode(arg: NimNode; name: string): void {. compileTime .} =
   echo "checking ", name
@@ -35,10 +33,10 @@ proc checkNode(arg: NimNode; name: string): void {. compileTime .} =
 
   echo "OK"
 
-static:
-  # the root node that is used to generate the Ast
-  var stmtList: NimNode
+# the root node that is used to generate the Ast
+var stmtList {.compileTime.}: NimNode
 
+static:
   stmtList = newStmtList(nnkDiscardStmt.newTree(newEmptyNode()))
 
   checkNode(stmtList, "direct construction")
diff --git a/tests/vm/tref.nim b/tests/vm/tref.nim
index 517a67fb0..27b7bf313 100644
--- a/tests/vm/tref.nim
+++ b/tests/vm/tref.nim
@@ -9,4 +9,49 @@ static:
 
   b[5] = 'c'
   doAssert a[] == "Hellocworld"
-  doAssert b[] == "Hellocworld"
\ No newline at end of file
+  doAssert b[] == "Hellocworld"
+
+  proc notGlobal() =
+    var
+      a: ref string
+      b: ref string
+    new a
+
+    a[] = "Hello world"
+    b = a
+
+    b[5] = 'c'
+    doAssert a[] == "Hellocworld"
+    doAssert b[] == "Hellocworld"
+  notGlobal()
+
+static: # bug 6081
+  block:
+    type Obj = object
+      field: ref int
+    var i: ref int
+    new(i)
+    var r = Obj(field: i)
+    var rr = r
+    r.field = nil
+    doAssert rr.field != nil
+
+  proc foo() = # Proc to avoid special global logic
+    var s: seq[ref int]
+    var i: ref int
+    new(i)
+    s.add(i)
+    var head = s[0]
+    s[0] = nil
+    doAssert head != nil
+
+  foo()
+
+static:
+
+  block: # global alias
+    var s: ref int
+    new(s)
+    var ss = s
+    s[] = 1
+    doAssert ss[] == 1
\ No newline at end of file
diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim
index 4af824cf4..80f5aeee0 100644
--- a/tests/vm/tvmmisc.nim
+++ b/tests/vm/tvmmisc.nim
@@ -19,9 +19,9 @@ block:
     var x = default(type(0))
 
 # #6379
-static:
-  import algorithm
+import algorithm
 
+static:
   var numArray = [1, 2, 3, 4, -1]
   numArray.sort(cmp)
   assert numArray == [-1, 1, 2, 3, 4]